diff --git a/cmd/katapult/console/chunk_reader.go b/cmd/katapult/console/chunk_reader.go new file mode 100644 index 0000000..faea470 --- /dev/null +++ b/cmd/katapult/console/chunk_reader.go @@ -0,0 +1,77 @@ +package console + +import ( + "bytes" + "io" + "sync" + "sync/atomic" +) + +type chunk struct { + a []byte + n int +} + +type chunkReader struct { + chunks []chunk + pendingErr error + pipe io.Reader + stopLoop uintptr + m sync.Mutex +} + +func (c *chunkReader) loop() { + // Used to stop racing before the start of the loop. + c.m.Unlock() + + for atomic.LoadUintptr(&c.stopLoop) == 0 { + // Read from the pipe. + a := make([]byte, 3) + n, err := c.pipe.Read(a) + if atomic.LoadUintptr(&c.stopLoop) == 1 { + // The loop was stopped in the time since last run. + return + } + + // Handle setting the values in the struct. + c.m.Lock() + if err == nil { + // Append to the chunks. + doAppend := true + for _, v := range c.chunks { + if bytes.Equal(v.a, a) && v.n == n { + doAppend = false + break + } + } + if doAppend { + c.chunks = append(c.chunks, chunk{a: a, n: n}) + } + c.m.Unlock() + } else { + // In this case, put the error and stop the loop. + c.pendingErr = err + c.m.Unlock() + return + } + } +} + +func (c *chunkReader) close() { + atomic.StoreUintptr(&c.stopLoop, 1) +} + +func (c *chunkReader) flush() []chunk { + c.m.Lock() + defer c.m.Unlock() + a := c.chunks + c.chunks = []chunk{} + return a +} + +func newChunkReader(r io.Reader) *chunkReader { + c := chunkReader{chunks: []chunk{}, pipe: r} + c.m.Lock() + go c.loop() + return &c +} diff --git a/cmd/katapult/console/default_terminal.go b/cmd/katapult/console/default_terminal.go new file mode 100644 index 0000000..3f19197 --- /dev/null +++ b/cmd/katapult/console/default_terminal.go @@ -0,0 +1,131 @@ +package console + +import ( + "os" + "sync" + + "github.com/buger/goterm" + "golang.org/x/term" +) + +type gotermTerminal struct { + raw *term.State + m sync.Mutex +} + +func (*gotermTerminal) Height() int { + return goterm.Height() +} + +func (*gotermTerminal) Width() int { + return goterm.Width() +} + +func (g *gotermTerminal) Print(items ...interface{}) (int, error) { + g.m.Lock() + defer g.m.Unlock() + if g.raw != nil { + _ = term.Restore(0, g.raw) + } + n, err := goterm.Print(items...) + if err == nil && g.raw != nil { + g.raw, err = term.MakeRaw(0) + if err != nil { + return 0, err + } + } + return n, err +} + +func (g *gotermTerminal) Sync(s string) error { + // TODO: There are better ways of doing this. I took a few hours to attempt to resync the screen line by line. + // TODO-1: However, by piping the whole chunk out, I find this stops the tearing a lot. + g.m.Lock() + defer g.m.Unlock() + if g.raw != nil { + _ = term.Restore(0, g.raw) + } + _, err := os.Stdout.Write([]byte(s)) + if err == nil && g.raw != nil { + g.raw, err = term.MakeRaw(0) + if err != nil { + return err + } + } + return err +} + +func (g *gotermTerminal) Clear() { + g.m.Lock() + defer g.m.Unlock() + if g.raw != nil { + _ = term.Restore(0, g.raw) + } + goterm.Clear() + if g.raw != nil { + g.raw, _ = term.MakeRaw(0) + } +} + +func (g *gotermTerminal) Println(items ...interface{}) (int, error) { + g.m.Lock() + defer g.m.Unlock() + if g.raw != nil { + _ = term.Restore(0, g.raw) + } + n, err := goterm.Println(items...) + if err == nil && g.raw != nil { + g.raw, err = term.MakeRaw(0) + if err != nil { + return 0, err + } + } + return n, err +} + +func (g *gotermTerminal) Flush() { + g.m.Lock() + defer g.m.Unlock() + if g.raw != nil { + _ = term.Restore(0, g.raw) + } + goterm.Flush() + if g.raw != nil { + g.raw, _ = term.MakeRaw(0) + } +} + +func (g *gotermTerminal) SignalInterrupt() { + g.m.Lock() + if g.raw != nil { + _ = term.Restore(0, g.raw) + g.raw = nil + } + g.m.Unlock() + os.Exit(1) +} + +func (g *gotermTerminal) MakeRaw() error { + g.m.Lock() + raw, err := term.MakeRaw(0) + if raw != nil { + g.raw = raw + } + g.m.Unlock() + return err +} + +func (g *gotermTerminal) Unraw() error { + var err error + g.m.Lock() + if g.raw != nil { + err = term.Restore(0, g.raw) + g.raw = nil + } + g.m.Unlock() + return err +} + +func (*gotermTerminal) BufferInputs() bool { + return true +} diff --git a/cmd/katapult/console/multi_input.go b/cmd/katapult/console/multi_input.go new file mode 100644 index 0000000..f48455e --- /dev/null +++ b/cmd/katapult/console/multi_input.go @@ -0,0 +1,459 @@ +package console + +import ( + "io" + "math" + "strings" + "time" + + "github.com/buger/goterm" + "github.com/krystal/katapult-cli/internal/keystrokes" +) + +// InputField is used to define a field which users can input text into. +type InputField struct { + // Optional defines if the field can be blank. + Optional bool `json:"optional"` + + // Name defines the input boxes name. + Name string `json:"name"` + + // Description defines the input boxes description. + Description string `json:"description"` +} + +func prepStringForTableView(s string, sLen int, l int, pad bool) []string { + if l >= sLen { + // The length is more or equal to the string length. + // Return a 1 length slice that contains spacing(total + // length minus string length) plus the string. + padLen := l - sLen + return []string{ + addSideBorder(s+strings.Repeat(" ", padLen), pad), + } + } + + // Defines the chunk count. + chunkCount := int(math.Ceil(float64(sLen) / float64(l))) + + // Defines the string slice. + a := make([]string, chunkCount) + for i := 0; i < chunkCount; i++ { + // Defines the previous count of items. + prevItems := i * l + + // If prevItems + sLen is greater than the string + // length, set it to the length. + endIndex := prevItems + l + if endIndex > sLen { + endIndex = sLen + } + + // Add the string. + a[i] = s[prevItems:endIndex] + } + + // If the final item isn't padded, we should pad it accordingly. + final := a[chunkCount-1] + if len(final) != l { + a[chunkCount-1] = final + strings.Repeat(" ", l-len(final)) + } + + // Add side borders. + for i, v := range a { + a[i] = addSideBorder(v, pad) + } + + // Return the slice. + return a +} + +func renderCursor(content string, width, highlighted int, active bool) string { + contentLen := len(content) + if highlighted > contentLen { + // Set the highlight to the end of the string. + highlighted = contentLen + } + cursor := "▓" + if !active { + // We should switch to the inactive cursor. + cursor = "░" + } + if width > contentLen { + // Greater than means there's enough room for the cursor too. + padAmount := width - contentLen - 1 + if 0 > padAmount { + // Do not panic here. + padAmount = 0 + } + pad := strings.Repeat(" ", padAmount) + if contentLen == highlighted { + // Return the content with the cursor at the end. + return content + cursor + pad + } + return content[:highlighted] + cursor + content[highlighted:] + pad + } + + // Create a width - 1 chunk and recall it. + wm1 := width - 1 + if 0 > wm1 { + // only room for 1 icon. + return cursor + } + + if highlighted == contentLen { + // If the content length is equal to the highlighted index, cut out the width - 1 and cursor. + return content[contentLen-wm1:] + cursor + } + + if wm1 > highlighted { + // In this case, we should recall it with the start. + return renderCursor(content[:wm1], width, highlighted, active) + } + + // Slice the text for part before the highlighted bit. + beforeHighlighted := highlighted - wm1 + textBefore := content[beforeHighlighted:highlighted] + return renderCursor(textBefore, width, len(textBefore), active) +} + +func addSideBorder(s string, pad bool) string { + if pad { + return "│ " + s + " │" + } + return "│" + s + "│" +} + +func createInputChunk(content string, width, highlighted int, active bool) [3]string { + // Handle if there isn't enough length. + if 3 > width { + return [3]string{} + } + + // Defines the top and bottom of the chunk. + topBottomShared := strings.Repeat("─", width-2) + top := "┌" + topBottomShared + "┐" + bottom := "└" + topBottomShared + "┘" + + // Return the array. + return [3]string{ + // Defines the top border. + top, + + // Defines the input box. + addSideBorder(renderCursor(content, width-2, highlighted, active), false), + + // Defines the bottom border. + bottom, + } +} + +func renderInputField(field InputField, content string, highlighted int, width int, active bool) []string { + // If the width is less than 10, return blank. + // It's technically more space than we need, but the UX will be so unusable at this point. + if 10 > width { + return []string{} + } + + // Defines the top and bottom of the field. + topBottomShared := strings.Repeat("─", width-2) + top := "┌" + topBottomShared + "┐" + bottom := "└" + topBottomShared + "┘" + + // Add a red asterisk to the title if required. + name := field.Name + nameLen := len(name) + if !field.Optional { + name = goterm.Color("* ", goterm.RED) + name + nameLen += 2 + } + + // Render the title into chunks. + titleChunks := prepStringForTableView(name, nameLen, width-4, true) + + // Render the description into chunks. + descriptionChunks := prepStringForTableView(field.Description, len(field.Description), width-4, true) + + // Create the input box. + inputChunk := createInputChunk(content, width-4, highlighted, active) + + // We should render the following: + // Top line, title, blank line, description, blank line, input field (with one space each side), + // bottom line + totalLen := 5 + len(titleChunks) + len(descriptionChunks) + toRender := make([]string, totalLen) + toRender[0] = top + copy(toRender[1:], titleChunks) + index := len(titleChunks) + 1 + copy(toRender[index:], descriptionChunks) + index += len(descriptionChunks) + for _, v := range inputChunk { + toRender[index] = addSideBorder(v, true) + index++ + } + toRender[index] = bottom + + // Return the freshly created slice. + return toRender +} + +type consoleChunk struct { + content string + fullyContains []int + partiallyContains []int + lines int +} + +func handleKeypress(buf []byte, n, activeIndex int, fields []InputField, + highlightedIndexes []int, fieldsContent []string, terminal TerminalInterface) (int, bool) { + // Handle single byte. + if n == 1 { + switch buf[0] { + case 3: + // CTRL+C + terminal.SignalInterrupt() + return activeIndex, true + case 9: + // Tab + activeIndex++ + if activeIndex == len(fields) { + // Loop to the start. + activeIndex = 0 + } + case 13: + // Enter + for i, v := range fields { + content := fieldsContent[i] + if content == "" && !v.Optional { + // Non-optional field missing. + return activeIndex, false + } + } + return activeIndex, true + case 127: + // Backspace + stringIndex := highlightedIndexes[activeIndex] + if stringIndex == 0 { + // Impossible to backspace at zero index. + return activeIndex, false + } + highlightedIndexes[activeIndex]-- + s := fieldsContent[activeIndex] + fieldsContent[activeIndex] = s[:stringIndex-1] + s[stringIndex:] + default: + // Character + stringIndex := highlightedIndexes[activeIndex] + highlightedIndexes[activeIndex]++ + s := fieldsContent[activeIndex] + if stringIndex == len(s) { + // Add to the string. + fieldsContent[activeIndex] = s + string(buf[0]) + } else { + // Add into the middle of the string. + fieldsContent[activeIndex] = s[:stringIndex] + string(buf[0]) + s[stringIndex:] + } + } + return activeIndex, false + } + + // AT&T style key input. + switch string(buf) { + case string(keystrokes.UpArrow): + activeIndex-- + if activeIndex == -1 { + // Don't let people try and access index -1. + activeIndex = 0 + } + case string(keystrokes.DownArrow): + activeIndex++ + if activeIndex == len(fields) { + // Loop to the start. + activeIndex = 0 + } + case string(keystrokes.LeftArrow): + stringIndex := highlightedIndexes[activeIndex] + if stringIndex != 0 { + highlightedIndexes[activeIndex]-- + } + case string(keystrokes.RightArrow): + stringIndex := highlightedIndexes[activeIndex] + content := fieldsContent[activeIndex] + stringIndex++ + if stringIndex > len(content) { + stringIndex = len(content) + } + highlightedIndexes[activeIndex] = stringIndex + default: + // Something else. Ignore this. + } + return activeIndex, false +} + +func chunkForConsole(renderedFields [][]string, usableItemRows int) []consoleChunk { + consoleSizedChunks := make([]consoleChunk, 0, 1) + startFrom := 0 + for len(renderedFields) != startFrom { + lines := 0 + chunk := "" + fullyContains := []int{} + partial := false + for ; startFrom < len(renderedFields); startFrom++ { + v := renderedFields[startFrom] + for _, line := range v { + if lines+1 > usableItemRows { + partial = true + break + } + lines++ + chunk += line + "\n" + } + if partial { + // Partial means we should add it to the array of partial chunks and stop. + consoleSizedChunks = append(consoleSizedChunks, consoleChunk{ + content: chunk, + fullyContains: fullyContains, + partiallyContains: []int{startFrom}, + lines: lines, + }) + break + } else { + // Add to fully contains. + fullyContains = append(fullyContains, startFrom) + } + } + if !partial { + // No partials. We should append. + consoleSizedChunks = append(consoleSizedChunks, consoleChunk{ + content: chunk, + fullyContains: fullyContains, + partiallyContains: []int{}, + lines: lines, + }) + } + } + return consoleSizedChunks +} + +// MultiInput is used to format multiple input slots to the display. +//nolint:funlen +func MultiInput(fields []InputField, stdin io.Reader, terminal TerminalInterface) []string { + // Ensure the terminal isn't nil. + if terminal == nil { + terminal = &gotermTerminal{} + } + + // Make sure the terminal is in raw mode. + err := terminal.MakeRaw() + if err != nil { + panic(err) + } + + // Defines the active index. + activeIndex := 0 + + // Defines the highlighted index in all fields. + highlightedIndexes := make([]int, len(fields)) + + // Defines the content for all fields. + fieldsContent := make([]string, len(fields)) + + // Create a chunk reader. + var r *chunkReader + if terminal.BufferInputs() { + r = newChunkReader(stdin) + defer r.close() + } + + // Loop until we are done. + for { + // Get the usable item rows. + usableItemRows := terminal.Height() - 1 + if 0 >= usableItemRows { + // Weird. Return status code 1. + terminal.SignalInterrupt() + return nil + } + + // Get the width. + width := terminal.Width() + + // Clear the terminal. + terminal.Clear() + + // Render all fields so we can get the height of them all. + renderedFields := make([][]string, len(fields)) + for i, v := range fields { + renderedFields[i] = renderInputField(v, fieldsContent[i], highlightedIndexes[i], width, i == activeIndex) + } + + // Create console sized chunks for all of the fields. + consoleSizedChunks := chunkForConsole(renderedFields, usableItemRows) + + // Find the best chunk to display. + containsInt := func(a []int, i int) bool { + for _, v := range a { + if v == i { + return true + } + } + return false + } + block := "" + for _, chunk := range consoleSizedChunks { + // First pass will work for most use cases where console is reasonably sized. + if containsInt(chunk.fullyContains, activeIndex) { + block += chunk.content + block += strings.Repeat("\n", usableItemRows-chunk.lines) + goto postChunkPrint + } + } + for _, chunk := range consoleSizedChunks { + // Second pass will work in poorly sized consoles. + if containsInt(chunk.partiallyContains, activeIndex) { + block += chunk.content + block += strings.Repeat("\n", usableItemRows-chunk.lines) + break + } + } + + postChunkPrint: + // Flush out the output. + _ = terminal.Sync(block) + + // Handle keypresses. + if terminal.BufferInputs() { + for { + // The 15ms cooldown is here to give the terminal time to catch up. + // For some reason, whilst Ubuntu's terminal seems fine without this, some (e.g.: iterm, goland) fail. + time.Sleep(time.Millisecond * 15) + + // Flush the buffer and check what we have. + a := r.flush() + if len(a) != 0 { + for _, v := range a { + var ret bool + activeIndex, ret = handleKeypress( + v.a, v.n, activeIndex, fields, highlightedIndexes, fieldsContent, terminal) + if ret { + _ = terminal.Unraw() + return fieldsContent + } + } + break + } + } + } else { + buf := make([]byte, 3) + n, err := stdin.Read(buf) + if err != nil { + return nil + } + var ret bool + activeIndex, ret = handleKeypress(buf, n, activeIndex, fields, highlightedIndexes, fieldsContent, terminal) + if ret { + _ = terminal.Unraw() + return fieldsContent + } + } + } +} diff --git a/cmd/katapult/console/multi_input_test.go b/cmd/katapult/console/multi_input_test.go new file mode 100644 index 0000000..92f8d60 --- /dev/null +++ b/cmd/katapult/console/multi_input_test.go @@ -0,0 +1,524 @@ +package console + +import ( + "strings" + "testing" + + "github.com/krystal/katapult-cli/internal/keystrokes" + + "github.com/krystal/katapult-cli/internal/golden" + "github.com/stretchr/testify/assert" +) + +func Test_prepStringForTableView(t *testing.T) { + tests := []struct { + name string + + content string + chunks []string + }{ + { + name: "blank", + content: "", + chunks: []string{"│ │"}, + }, + { + name: "under length", + content: "hey", + chunks: []string{"│hey │"}, + }, + { + name: "equal to length", + content: "hello", + chunks: []string{"│hello│"}, + }, + { + name: "split equal length", + content: "helloworld", + chunks: []string{"│hello│", "│world│"}, + }, + { + name: "split pad", + content: "helloxd", + chunks: []string{"│hello│", "│xd │"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.chunks, prepStringForTableView(tt.content, len(tt.content), 5, false)) + }) + } +} + +func Test_renderCursor(t *testing.T) { + tests := []struct { + name string + + content string + width int + highlighted int + expected string + active bool + }{ + { + name: "under width inactive", + content: "hello", + width: 10, + highlighted: 5, + expected: "hello░ ", + }, + { + name: "under width midpoint inactive", + content: "hello", + width: 10, + highlighted: 4, + expected: "hell░o ", + }, + { + name: "full minus 1 inactive", + content: "aaa", + width: 4, + highlighted: 3, + expected: "aaa░", + }, + { + name: "full minus 1 midpoint inactive", + content: "aaa", + width: 4, + highlighted: 2, + expected: "aa░a", + }, + { + name: "content overflow first chunk midpoint inactive", + content: "aaax", + width: 4, + highlighted: 2, + expected: "aa░a", + }, + { + name: "content overflow first chunk end inactive", + content: "aaax", + width: 4, + highlighted: 3, + expected: "aaa░", + }, + { + name: "content overflow one over inactive", + content: "abcd", + width: 3, + highlighted: 3, + expected: "bc░", + }, + { + name: "content overflow not first chunk inactive", + content: "abcd", + width: 3, + highlighted: 4, + expected: "cd░", + }, + + { + name: "under width active", + content: "hello", + width: 10, + highlighted: 5, + expected: "hello▓ ", + active: true, + }, + { + name: "under width midpoint active", + content: "hello", + width: 10, + highlighted: 4, + expected: "hell▓o ", + active: true, + }, + { + name: "full minus 1 active", + content: "aaa", + width: 4, + highlighted: 3, + expected: "aaa▓", + active: true, + }, + { + name: "full minus 1 midpoint active", + content: "aaa", + width: 4, + highlighted: 2, + expected: "aa▓a", + active: true, + }, + { + name: "content overflow first chunk midpoint active", + content: "aaax", + width: 4, + highlighted: 2, + expected: "aa▓a", + active: true, + }, + { + name: "content overflow first chunk end active", + content: "aaax", + width: 4, + highlighted: 3, + expected: "aaa▓", + active: true, + }, + { + name: "content overflow one over active", + content: "abcd", + width: 3, + highlighted: 3, + expected: "bc▓", + active: true, + }, + { + name: "content overflow not first chunk active", + content: "abcd", + width: 3, + highlighted: 4, + expected: "cd▓", + active: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.expected, renderCursor(tt.content, tt.width, tt.highlighted, tt.active)) + }) + } +} + +func createFormattedInputTestResult(expectInput string) [3]string { + topBottomShared := strings.Repeat("─", len(expectInput)-2) + top := "┌" + topBottomShared + "┐" + bottom := "└" + topBottomShared + "┘" + return [3]string{ + top, "│" + expectInput + "│", bottom, + } +} + +func Test_createInputChunk(t *testing.T) { + tests := []struct { + name string + + content string + width int + highlighted int + expected [3]string + active bool + }{ + { + name: "under width", + content: "hello", + width: 12, + highlighted: 5, + expected: createFormattedInputTestResult("hello▓ "), + active: true, + }, + { + name: "under width midpoint", + content: "hello", + width: 12, + highlighted: 4, + expected: createFormattedInputTestResult("hell▓o "), + active: true, + }, + { + name: "full minus 1", + content: "aaa", + width: 6, + highlighted: 3, + expected: createFormattedInputTestResult("aaa▓"), + active: true, + }, + { + name: "full minus 1 midpoint", + content: "aaa", + width: 6, + highlighted: 2, + expected: createFormattedInputTestResult("aa▓a"), + active: true, + }, + { + name: "content overflow first chunk midpoint", + content: "aaax", + width: 6, + highlighted: 2, + expected: createFormattedInputTestResult("aa▓a"), + active: true, + }, + { + name: "content overflow first chunk end", + content: "aaax", + width: 6, + highlighted: 3, + expected: createFormattedInputTestResult("aaa▓"), + active: true, + }, + { + name: "content overflow one over", + content: "abcd", + width: 5, + highlighted: 3, + expected: createFormattedInputTestResult("bc▓"), + active: true, + }, + { + name: "content overflow not first chunk", + content: "abcd", + width: 5, + highlighted: 4, + expected: createFormattedInputTestResult("cd▓"), + active: true, + }, + { + name: "inactive cursor", + content: "abcd", + width: 5, + highlighted: 4, + expected: createFormattedInputTestResult("cd░"), + active: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.expected, createInputChunk(tt.content, tt.width, tt.highlighted, tt.active)) + }) + } +} + +func Test_renderInputField(t *testing.T) { + tests := []struct { + name string + + field InputField + content string + highlighted int + width int + active bool + }{ + { + name: "under width inactive", + field: InputField{ + Optional: false, + Name: "a", + Description: "b", + }, + content: "hello", + highlighted: 5, + width: 14, + }, + { + name: "under width active", + field: InputField{ + Optional: false, + Name: "a", + Description: "b", + }, + content: "hello", + highlighted: 5, + width: 14, + active: true, + }, + { + name: "optional", + field: InputField{ + Optional: true, + Name: "a", + Description: "b", + }, + content: "hello", + highlighted: 5, + width: 14, + active: true, + }, + { + name: "title overflow", + field: InputField{ + Optional: false, + Name: "abcabcabcabcabc", + Description: "b", + }, + content: "hello", + highlighted: 5, + width: 14, + active: true, + }, + { + name: "title overflow optional", + field: InputField{ + Optional: true, + Name: "abcabcabcabcabc", + Description: "b", + }, + content: "hello", + highlighted: 5, + width: 14, + active: true, + }, + { + name: "description overflow", + field: InputField{ + Optional: true, + Name: "a", + Description: "abcabcabcabcabc", + }, + content: "hello", + highlighted: 5, + width: 14, + active: true, + }, + { + name: "input exact length", + field: InputField{ + Optional: true, + Name: "a", + Description: "b", + }, + content: "hello12", + highlighted: 7, + width: 14, + active: true, + }, + { + name: "input overflow", + field: InputField{ + Optional: true, + Name: "a", + Description: "b", + }, + content: "hello123", + highlighted: 7, + width: 14, + active: true, + }, + { + name: "input overflow scroll", + field: InputField{ + Optional: true, + Name: "a", + Description: "b", + }, + content: "hello123", + highlighted: 8, + width: 14, + active: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := renderInputField(tt.field, tt.content, tt.highlighted, tt.width, tt.active) + var stringRep string + if s == nil { + stringRep = "null" + } else { + stringRep = strings.Join(s, "\n") + } + if golden.Update() { + golden.Set(t, []byte(stringRep)) + } + assert.Equal(t, string(golden.Get(t)), stringRep) + }) + } +} + +func TestMultiInput(t *testing.T) { + tests := []struct { + name string + + inputs [][]byte + fields []InputField + shouldExit bool + result []string + }{ + { + name: "left right arrows", + inputs: [][]byte{ + {'a'}, + {'c'}, + {'2'}, + {'3'}, + keystrokes.LeftArrow, + keystrokes.LeftArrow, + {'1'}, + keystrokes.LeftArrow, keystrokes.LeftArrow, + keystrokes.LeftArrow, keystrokes.RightArrow, + {'b'}, + keystrokes.Enter, + }, + fields: []InputField{ + { + Name: "a", + Description: "b", + }, + }, + shouldExit: false, + result: []string{"abc123"}, + }, + { + name: "required enforcement", + inputs: [][]byte{ + keystrokes.Enter, keystrokes.DownArrow, + {'b'}, + keystrokes.UpArrow, + {'a'}, + keystrokes.Enter, + }, + fields: []InputField{ + { + Optional: false, + Name: "top", + Description: "the top item", + }, + { + Optional: true, + Name: "overflow", + Description: "the overflow item", + }, + }, + shouldExit: false, + result: []string{"a", "b"}, + }, + { + name: "overflow", + inputs: [][]byte{{'a'}, keystrokes.DownArrow, {'b'}, keystrokes.Enter}, + fields: []InputField{ + { + Optional: false, + Name: "top", + Description: "the top item", + }, + { + Optional: true, + Name: "overflow", + Description: "the overflow item", + }, + }, + shouldExit: false, + result: []string{"a", "b"}, + }, + { + name: "ctrl c", + inputs: [][]byte{keystrokes.CTRLC}, + fields: []InputField{{}}, + shouldExit: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + stdin := &StdinDripFeeder{T: t, Inputs: tt.inputs} + stdout := &MockTerminal{CustomWidth: 50} + res := MultiInput(tt.fields, stdin, stdout) + if tt.shouldExit { + assert.Equal(t, tt.shouldExit, stdout.ExitSignaled) + } else { + assert.Equal(t, tt.result, res) + } + if golden.Update() { + golden.Set(t, stdout.Buffer.Bytes()) + } + assert.Equal(t, string(golden.Get(t)), stdout.Buffer.String()) + }) + } +} diff --git a/cmd/katapult/console/selector.go b/cmd/katapult/console/selector.go index 301c421..25f1ea8 100644 --- a/cmd/katapult/console/selector.go +++ b/cmd/katapult/console/selector.go @@ -4,13 +4,10 @@ import ( "container/list" "io" "math" - "os" "strings" - "github.com/krystal/katapult-cli/internal/keystrokes" - "github.com/buger/goterm" - "golang.org/x/term" + "github.com/krystal/katapult-cli/internal/keystrokes" ) const ( @@ -21,18 +18,6 @@ const ( clarificationStringMultiple = " (Press ENTER to select items and ESC when you are done with your selections): " ) -// TerminalInterface defines a interface for a compatible terminal. Used for unit testing. -type TerminalInterface interface { - Height() int - Width() int - Print(items ...interface{}) (int, error) - Println(items ...interface{}) (int, error) - Clear() - Flush() - SignalInterrupt() - MakeRaw() (*term.State, error) -} - func intMin(x, y int) int { if x > y { return y @@ -105,7 +90,7 @@ func formatUserPrompt( ) int { var suggestionLen int if length == 0 { - // There's no matches, we should just just print the users input. + // There's no matches, we should just print the users input. _, _ = terminal.Println(query) suggestionLen = len(query) } else { @@ -186,7 +171,7 @@ func renderColumns(row []string, offset int, highlight highlight, width int, ter } // Handle a standard input. -func handleStandardInput( +func handleSelectorStandardInput( buf []byte, matchedLen int, multiple, hasColumns bool, highlightIndex *int, selectedItems *list.List, matched interface{}, query *string, terminal TerminalInterface, @@ -199,6 +184,8 @@ func handleStandardInput( return [][]string{} } return []string{} + case 9: + // Ignore this. It is the tab key. case 13: // Enter if matchedLen != 0 { @@ -274,7 +261,7 @@ func handleInput(buf []byte, multiple bool, matchedLen, n int, highlightIndex *i matched interface{}, terminal TerminalInterface) interface{} { if n == 1 { // Standard input. - return handleStandardInput(buf, matchedLen, multiple, hasColumns, highlightIndex, + return handleSelectorStandardInput(buf, matchedLen, multiple, hasColumns, highlightIndex, selectedItems, matched, query, terminal) } @@ -318,40 +305,6 @@ func renderRowItem(hasColumn bool, highlightIndex, i, width int, v interface{}, } } -type gotermTerminal struct{} - -func (gotermTerminal) Height() int { - return goterm.Height() -} - -func (gotermTerminal) Width() int { - return goterm.Width() -} - -func (gotermTerminal) Print(items ...interface{}) (int, error) { - return goterm.Print(items...) -} - -func (gotermTerminal) Clear() { - goterm.Clear() -} - -func (gotermTerminal) Println(items ...interface{}) (int, error) { - return goterm.Println(items...) -} - -func (gotermTerminal) Flush() { - goterm.Flush() -} - -func (gotermTerminal) SignalInterrupt() { - os.Exit(1) -} - -func (gotermTerminal) MakeRaw() (*term.State, error) { - return term.MakeRaw(0) -} - // items is either []string or [][]string (if columns isn't nil). //nolint:funlen,lll func selectorComponent(question string, columns []string, items interface{}, stdin io.Reader, multiple bool, terminal TerminalInterface) interface{} { @@ -460,14 +413,12 @@ func selectorComponent(question string, columns []string, items interface{}, std terminal.Flush() // Wait for user input. - raw, err := terminal.MakeRaw() + err := terminal.MakeRaw() if err != nil { panic(err) } n, _ := stdin.Read(buf) - if raw != nil { - _ = term.Restore(0, raw) - } + _ = terminal.Unraw() if x := handleInput(buf, multiple, matchedLen, n, &highlightIndex, columns != nil, selectedItems, &query, matched, terminal); x != nil { return x @@ -478,7 +429,7 @@ func selectorComponent(question string, columns []string, items interface{}, std // FuzzySelector is used to create a selector. If terminal is nil, we will default to goterm. func FuzzySelector(question string, items []string, stdin io.Reader, terminal TerminalInterface) string { if terminal == nil { - terminal = gotermTerminal{} + terminal = &gotermTerminal{} } return selectorComponent(question, nil, items, stdin, false, terminal).([]string)[0] } @@ -486,7 +437,7 @@ func FuzzySelector(question string, items []string, stdin io.Reader, terminal Te // FuzzyMultiSelector is used to create a selector with multiple items. func FuzzyMultiSelector(question string, items []string, stdin io.Reader, terminal TerminalInterface) []string { if terminal == nil { - terminal = gotermTerminal{} + terminal = &gotermTerminal{} } return selectorComponent(question, nil, items, stdin, true, terminal).([]string) } @@ -495,7 +446,7 @@ func FuzzyMultiSelector(question string, items []string, stdin io.Reader, termin func FuzzyTableSelector(question string, columns []string, items [][]string, stdin io.Reader, terminal TerminalInterface) []string { if terminal == nil { - terminal = gotermTerminal{} + terminal = &gotermTerminal{} } return selectorComponent(question, columns, items, stdin, false, terminal).([][]string)[0] } @@ -504,7 +455,7 @@ func FuzzyTableSelector(question string, columns []string, items [][]string, func FuzzyTableMultiSelector(question string, columns []string, items [][]string, stdin io.Reader, terminal TerminalInterface) [][]string { if terminal == nil { - terminal = gotermTerminal{} + terminal = &gotermTerminal{} } return selectorComponent(question, columns, items, stdin, true, terminal).([][]string) } diff --git a/cmd/katapult/console/selector_test.go b/cmd/katapult/console/selector_test.go index f90765d..cc643ea 100644 --- a/cmd/katapult/console/selector_test.go +++ b/cmd/katapult/console/selector_test.go @@ -20,7 +20,6 @@ func TestSelector(t *testing.T) { result interface{} }{ // Non-row selection - { name: "display non-row selection menu", inputs: [][]byte{ @@ -335,7 +334,7 @@ func TestSelector(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - stdin := &StdinDripFeeder{Inputs: tt.inputs} + stdin := &StdinDripFeeder{T: t, Inputs: tt.inputs} stdout := &MockTerminal{} res := selectorComponent("test", tt.columns, tt.items, stdin, tt.multiple, stdout) if tt.shouldExit { diff --git a/cmd/katapult/console/test_helpers.go b/cmd/katapult/console/test_helpers.go index 3963044..ce2b23a 100644 --- a/cmd/katapult/console/test_helpers.go +++ b/cmd/katapult/console/test_helpers.go @@ -3,14 +3,30 @@ package console import ( "bytes" "fmt" - - "golang.org/x/term" + "io" + "testing" ) +// TerminalInterface defines a interface for a compatible terminal. Used for unit testing. +type TerminalInterface interface { + Height() int + Width() int + Print(items ...interface{}) (int, error) + Sync(s string) error + Println(items ...interface{}) (int, error) + Clear() + Flush() + SignalInterrupt() + MakeRaw() error + Unraw() error + BufferInputs() bool +} + // MockTerminal is used to define a terminal mock for unit tests. type MockTerminal struct { Buffer bytes.Buffer + CustomWidth int ExitSignaled bool } @@ -21,7 +37,10 @@ func (m *MockTerminal) Height() int { // Width implements TerminalInterface. func (m *MockTerminal) Width() int { - return 200 + if m.CustomWidth == 0 { + return 200 + } + return m.CustomWidth } // Print implements TerminalInterface. @@ -29,6 +48,12 @@ func (m *MockTerminal) Print(items ...interface{}) (int, error) { return fmt.Fprint(&m.Buffer, items...) } +// Sync implements TerminalInterface. +func (m *MockTerminal) Sync(s string) error { + _, err := m.Buffer.WriteString(s) + return err +} + // Println implements TerminalInterface. func (m *MockTerminal) Println(items ...interface{}) (int, error) { return fmt.Fprintln(&m.Buffer, items...) @@ -50,18 +75,35 @@ func (m *MockTerminal) SignalInterrupt() { } // MakeRaw implements TerminalInterface. -func (m *MockTerminal) MakeRaw() (*term.State, error) { - return nil, nil +func (m *MockTerminal) MakeRaw() error { + return nil +} + +// Unraw implements TerminalInterface. +func (m *MockTerminal) Unraw() error { + return nil +} + +// BufferInputs implements TerminalInterface. +func (m *MockTerminal) BufferInputs() bool { + return false } // StdinDripFeeder is used to define a io.Reader designed to drip feed in different inputs. type StdinDripFeeder struct { + T *testing.T + Inputs [][]byte Index int } // Read implements io.Reader. func (s *StdinDripFeeder) Read(b []byte) (int, error) { + s.T.Helper() + if s.Index == len(s.Inputs) { + s.T.Fatal("Unexpected call to Read") + return 0, io.EOF + } copy(b, s.Inputs[s.Index]) l := len(s.Inputs[s.Index]) s.Index++ diff --git a/cmd/katapult/console/testdata/TestMultiInput/ctrl_c.golden b/cmd/katapult/console/testdata/TestMultiInput/ctrl_c.golden new file mode 100644 index 0000000..5d00778 --- /dev/null +++ b/cmd/katapult/console/testdata/TestMultiInput/ctrl_c.golden @@ -0,0 +1,9 @@ +┌────────────────────────────────────────────────┐ +│ *  │ +│ │ +│ ┌────────────────────────────────────────────┐ │ +│ │▓ │ │ +│ └────────────────────────────────────────────┘ │ +└────────────────────────────────────────────────┘ + + diff --git a/cmd/katapult/console/testdata/TestMultiInput/left_right_arrows.golden b/cmd/katapult/console/testdata/TestMultiInput/left_right_arrows.golden new file mode 100644 index 0000000..ac22bec --- /dev/null +++ b/cmd/katapult/console/testdata/TestMultiInput/left_right_arrows.golden @@ -0,0 +1,117 @@ +┌────────────────────────────────────────────────┐ +│ * a │ +│ b │ +│ ┌────────────────────────────────────────────┐ │ +│ │▓ │ │ +│ └────────────────────────────────────────────┘ │ +└────────────────────────────────────────────────┘ + + +┌────────────────────────────────────────────────┐ +│ * a │ +│ b │ +│ ┌────────────────────────────────────────────┐ │ +│ │a▓ │ │ +│ └────────────────────────────────────────────┘ │ +└────────────────────────────────────────────────┘ + + +┌────────────────────────────────────────────────┐ +│ * a │ +│ b │ +│ ┌────────────────────────────────────────────┐ │ +│ │ac▓ │ │ +│ └────────────────────────────────────────────┘ │ +└────────────────────────────────────────────────┘ + + +┌────────────────────────────────────────────────┐ +│ * a │ +│ b │ +│ ┌────────────────────────────────────────────┐ │ +│ │ac2▓ │ │ +│ └────────────────────────────────────────────┘ │ +└────────────────────────────────────────────────┘ + + +┌────────────────────────────────────────────────┐ +│ * a │ +│ b │ +│ ┌────────────────────────────────────────────┐ │ +│ │ac23▓ │ │ +│ └────────────────────────────────────────────┘ │ +└────────────────────────────────────────────────┘ + + +┌────────────────────────────────────────────────┐ +│ * a │ +│ b │ +│ ┌────────────────────────────────────────────┐ │ +│ │ac2▓3 │ │ +│ └────────────────────────────────────────────┘ │ +└────────────────────────────────────────────────┘ + + +┌────────────────────────────────────────────────┐ +│ * a │ +│ b │ +│ ┌────────────────────────────────────────────┐ │ +│ │ac▓23 │ │ +│ └────────────────────────────────────────────┘ │ +└────────────────────────────────────────────────┘ + + +┌────────────────────────────────────────────────┐ +│ * a │ +│ b │ +│ ┌────────────────────────────────────────────┐ │ +│ │ac1▓23 │ │ +│ └────────────────────────────────────────────┘ │ +└────────────────────────────────────────────────┘ + + +┌────────────────────────────────────────────────┐ +│ * a │ +│ b │ +│ ┌────────────────────────────────────────────┐ │ +│ │ac▓123 │ │ +│ └────────────────────────────────────────────┘ │ +└────────────────────────────────────────────────┘ + + +┌────────────────────────────────────────────────┐ +│ * a │ +│ b │ +│ ┌────────────────────────────────────────────┐ │ +│ │a▓c123 │ │ +│ └────────────────────────────────────────────┘ │ +└────────────────────────────────────────────────┘ + + +┌────────────────────────────────────────────────┐ +│ * a │ +│ b │ +│ ┌────────────────────────────────────────────┐ │ +│ │▓ac123 │ │ +│ └────────────────────────────────────────────┘ │ +└────────────────────────────────────────────────┘ + + +┌────────────────────────────────────────────────┐ +│ * a │ +│ b │ +│ ┌────────────────────────────────────────────┐ │ +│ │a▓c123 │ │ +│ └────────────────────────────────────────────┘ │ +└────────────────────────────────────────────────┘ + + +┌────────────────────────────────────────────────┐ +│ * a │ +│ b │ +│ ┌────────────────────────────────────────────┐ │ +│ │ab▓c123 │ │ +│ └────────────────────────────────────────────┘ │ +└────────────────────────────────────────────────┘ + + diff --git a/cmd/katapult/console/testdata/TestMultiInput/overflow.golden b/cmd/katapult/console/testdata/TestMultiInput/overflow.golden new file mode 100644 index 0000000..4543038 --- /dev/null +++ b/cmd/katapult/console/testdata/TestMultiInput/overflow.golden @@ -0,0 +1,36 @@ +┌────────────────────────────────────────────────┐ +│ * top │ +│ the top item │ +│ ┌────────────────────────────────────────────┐ │ +│ │▓ │ │ +│ └────────────────────────────────────────────┘ │ +└────────────────────────────────────────────────┘ +┌────────────────────────────────────────────────┐ +│ overflow │ +┌────────────────────────────────────────────────┐ +│ * top │ +│ the top item │ +│ ┌────────────────────────────────────────────┐ │ +│ │a▓ │ │ +│ └────────────────────────────────────────────┘ │ +└────────────────────────────────────────────────┘ +┌────────────────────────────────────────────────┐ +│ overflow │ +┌────────────────────────────────────────────────┐ +│ overflow │ +│ the overflow item │ +│ ┌────────────────────────────────────────────┐ │ +│ │▓ │ │ +│ └────────────────────────────────────────────┘ │ +└────────────────────────────────────────────────┘ + + +┌────────────────────────────────────────────────┐ +│ overflow │ +│ the overflow item │ +│ ┌────────────────────────────────────────────┐ │ +│ │b▓ │ │ +│ └────────────────────────────────────────────┘ │ +└────────────────────────────────────────────────┘ + + diff --git a/cmd/katapult/console/testdata/TestMultiInput/required_enforcement.golden b/cmd/katapult/console/testdata/TestMultiInput/required_enforcement.golden new file mode 100644 index 0000000..119c1b8 --- /dev/null +++ b/cmd/katapult/console/testdata/TestMultiInput/required_enforcement.golden @@ -0,0 +1,54 @@ +┌────────────────────────────────────────────────┐ +│ * top │ +│ the top item │ +│ ┌────────────────────────────────────────────┐ │ +│ │▓ │ │ +│ └────────────────────────────────────────────┘ │ +└────────────────────────────────────────────────┘ +┌────────────────────────────────────────────────┐ +│ overflow │ +┌────────────────────────────────────────────────┐ +│ * top │ +│ the top item │ +│ ┌────────────────────────────────────────────┐ │ +│ │▓ │ │ +│ └────────────────────────────────────────────┘ │ +└────────────────────────────────────────────────┘ +┌────────────────────────────────────────────────┐ +│ overflow │ +┌────────────────────────────────────────────────┐ +│ overflow │ +│ the overflow item │ +│ ┌────────────────────────────────────────────┐ │ +│ │▓ │ │ +│ └────────────────────────────────────────────┘ │ +└────────────────────────────────────────────────┘ + + +┌────────────────────────────────────────────────┐ +│ overflow │ +│ the overflow item │ +│ ┌────────────────────────────────────────────┐ │ +│ │b▓ │ │ +│ └────────────────────────────────────────────┘ │ +└────────────────────────────────────────────────┘ + + +┌────────────────────────────────────────────────┐ +│ * top │ +│ the top item │ +│ ┌────────────────────────────────────────────┐ │ +│ │▓ │ │ +│ └────────────────────────────────────────────┘ │ +└────────────────────────────────────────────────┘ +┌────────────────────────────────────────────────┐ +│ overflow │ +┌────────────────────────────────────────────────┐ +│ * top │ +│ the top item │ +│ ┌────────────────────────────────────────────┐ │ +│ │a▓ │ │ +│ └────────────────────────────────────────────┘ │ +└────────────────────────────────────────────────┘ +┌────────────────────────────────────────────────┐ +│ overflow │ diff --git a/cmd/katapult/console/testdata/Test_renderInputField/description_overflow.golden b/cmd/katapult/console/testdata/Test_renderInputField/description_overflow.golden new file mode 100644 index 0000000..f9778fe --- /dev/null +++ b/cmd/katapult/console/testdata/Test_renderInputField/description_overflow.golden @@ -0,0 +1,8 @@ +┌────────────┐ +│ a │ +│ abcabcabca │ +│ bcabc │ +│ ┌────────┐ │ +│ │hello▓ │ │ +│ └────────┘ │ +└────────────┘ \ No newline at end of file diff --git a/cmd/katapult/console/testdata/Test_renderInputField/input_exact_length.golden b/cmd/katapult/console/testdata/Test_renderInputField/input_exact_length.golden new file mode 100644 index 0000000..231a4c7 --- /dev/null +++ b/cmd/katapult/console/testdata/Test_renderInputField/input_exact_length.golden @@ -0,0 +1,7 @@ +┌────────────┐ +│ a │ +│ b │ +│ ┌────────┐ │ +│ │hello12▓│ │ +│ └────────┘ │ +└────────────┘ \ No newline at end of file diff --git a/cmd/katapult/console/testdata/Test_renderInputField/input_overflow.golden b/cmd/katapult/console/testdata/Test_renderInputField/input_overflow.golden new file mode 100644 index 0000000..231a4c7 --- /dev/null +++ b/cmd/katapult/console/testdata/Test_renderInputField/input_overflow.golden @@ -0,0 +1,7 @@ +┌────────────┐ +│ a │ +│ b │ +│ ┌────────┐ │ +│ │hello12▓│ │ +│ └────────┘ │ +└────────────┘ \ No newline at end of file diff --git a/cmd/katapult/console/testdata/Test_renderInputField/input_overflow_scroll.golden b/cmd/katapult/console/testdata/Test_renderInputField/input_overflow_scroll.golden new file mode 100644 index 0000000..3c7d978 --- /dev/null +++ b/cmd/katapult/console/testdata/Test_renderInputField/input_overflow_scroll.golden @@ -0,0 +1,7 @@ +┌────────────┐ +│ a │ +│ b │ +│ ┌────────┐ │ +│ │ello123▓│ │ +│ └────────┘ │ +└────────────┘ \ No newline at end of file diff --git a/cmd/katapult/console/testdata/Test_renderInputField/optional.golden b/cmd/katapult/console/testdata/Test_renderInputField/optional.golden new file mode 100644 index 0000000..b40c948 --- /dev/null +++ b/cmd/katapult/console/testdata/Test_renderInputField/optional.golden @@ -0,0 +1,7 @@ +┌────────────┐ +│ a │ +│ b │ +│ ┌────────┐ │ +│ │hello▓ │ │ +│ └────────┘ │ +└────────────┘ \ No newline at end of file diff --git a/cmd/katapult/console/testdata/Test_renderInputField/title_overflow.golden b/cmd/katapult/console/testdata/Test_renderInputField/title_overflow.golden new file mode 100644 index 0000000..7672ef9 --- /dev/null +++ b/cmd/katapult/console/testdata/Test_renderInputField/title_overflow.golden @@ -0,0 +1,8 @@ +┌────────────┐ +│ * [0 │ +│ mabcabc │ +│ b │ +│ ┌────────┐ │ +│ │hello▓ │ │ +│ └────────┘ │ +└────────────┘ \ No newline at end of file diff --git a/cmd/katapult/console/testdata/Test_renderInputField/title_overflow_optional.golden b/cmd/katapult/console/testdata/Test_renderInputField/title_overflow_optional.golden new file mode 100644 index 0000000..3be4e84 --- /dev/null +++ b/cmd/katapult/console/testdata/Test_renderInputField/title_overflow_optional.golden @@ -0,0 +1,8 @@ +┌────────────┐ +│ abcabcabca │ +│ bcabc │ +│ b │ +│ ┌────────┐ │ +│ │hello▓ │ │ +│ └────────┘ │ +└────────────┘ \ No newline at end of file diff --git a/cmd/katapult/console/testdata/Test_renderInputField/under_width_active.golden b/cmd/katapult/console/testdata/Test_renderInputField/under_width_active.golden new file mode 100644 index 0000000..41048ec --- /dev/null +++ b/cmd/katapult/console/testdata/Test_renderInputField/under_width_active.golden @@ -0,0 +1,7 @@ +┌────────────┐ +│ * a │ +│ b │ +│ ┌────────┐ │ +│ │hello▓ │ │ +│ └────────┘ │ +└────────────┘ \ No newline at end of file diff --git a/cmd/katapult/console/testdata/Test_renderInputField/under_width_inactive.golden b/cmd/katapult/console/testdata/Test_renderInputField/under_width_inactive.golden new file mode 100644 index 0000000..16950cf --- /dev/null +++ b/cmd/katapult/console/testdata/Test_renderInputField/under_width_inactive.golden @@ -0,0 +1,7 @@ +┌────────────┐ +│ * a │ +│ b │ +│ ┌────────┐ │ +│ │hello░ │ │ +│ └────────┘ │ +└────────────┘ \ No newline at end of file diff --git a/cmd/katapult/main.go b/cmd/katapult/main.go index 18966f1..a6c3473 100644 --- a/cmd/katapult/main.go +++ b/cmd/katapult/main.go @@ -105,7 +105,7 @@ func run() error { core.NewSSHKeysClient(cl), core.NewTagsClient(cl), core.NewVirtualMachineBuildsClient(cl), - nil), + nil, nil), ) return rootCmd.Execute() diff --git a/cmd/katapult/testdata/TestVMs_Create/dc_id_error.golden b/cmd/katapult/testdata/TestVMs_Create/dc_id_error.golden new file mode 100644 index 0000000..6b1f833 --- /dev/null +++ b/cmd/katapult/testdata/TestVMs_Create/dc_id_error.golden @@ -0,0 +1,16 @@ +-- STDOUT -- + +Usage: + vm create [flags] + +Flags: + -h, --help help for create + + + +-- BUILD SPEC -- + +{ + "OrgResult": {}, + "SpecResult": null +} diff --git a/cmd/katapult/testdata/TestVMs_Create/dc_name_error.golden b/cmd/katapult/testdata/TestVMs_Create/dc_name_error.golden new file mode 100644 index 0000000..6b1f833 --- /dev/null +++ b/cmd/katapult/testdata/TestVMs_Create/dc_name_error.golden @@ -0,0 +1,16 @@ +-- STDOUT -- + +Usage: + vm create [flags] + +Flags: + -h, --help help for create + + + +-- BUILD SPEC -- + +{ + "OrgResult": {}, + "SpecResult": null +} diff --git a/cmd/katapult/testdata/TestVMs_Create/disk_templates_throws_error.golden b/cmd/katapult/testdata/TestVMs_Create/disk_templates_throws_error.golden index ca56ca6..b5cf141 100644 --- a/cmd/katapult/testdata/TestVMs_Create/disk_templates_throws_error.golden +++ b/cmd/katapult/testdata/TestVMs_Create/disk_templates_throws_error.golden @@ -18,8 +18,8 @@ -Which VM package would you like? (Press ENTER to make your selection):  / 0 / 0GB -Name CPU Cores RAM  +Which package would you like to deploy the VM in? (Press ENTER to make your selection):  / 0 / 0GB +Name CPU Cores Memory   0 0GB  diff --git a/cmd/katapult/testdata/TestVMs_Create/distro_id_error.golden b/cmd/katapult/testdata/TestVMs_Create/distro_id_error.golden new file mode 100644 index 0000000..6b1f833 --- /dev/null +++ b/cmd/katapult/testdata/TestVMs_Create/distro_id_error.golden @@ -0,0 +1,16 @@ +-- STDOUT -- + +Usage: + vm create [flags] + +Flags: + -h, --help help for create + + + +-- BUILD SPEC -- + +{ + "OrgResult": {}, + "SpecResult": null +} diff --git a/cmd/katapult/testdata/TestVMs_Create/distro_name_error.golden b/cmd/katapult/testdata/TestVMs_Create/distro_name_error.golden new file mode 100644 index 0000000..6b1f833 --- /dev/null +++ b/cmd/katapult/testdata/TestVMs_Create/distro_name_error.golden @@ -0,0 +1,16 @@ +-- STDOUT -- + +Usage: + vm create [flags] + +Flags: + -h, --help help for create + + + +-- BUILD SPEC -- + +{ + "OrgResult": {}, + "SpecResult": null +} diff --git a/cmd/katapult/testdata/TestVMs_Create/ip_listing_throws_error.golden b/cmd/katapult/testdata/TestVMs_Create/ip_listing_throws_error.golden index a91fc40..6737a0e 100644 --- a/cmd/katapult/testdata/TestVMs_Create/ip_listing_throws_error.golden +++ b/cmd/katapult/testdata/TestVMs_Create/ip_listing_throws_error.golden @@ -18,8 +18,8 @@ -Which VM package would you like? (Press ENTER to make your selection):  / 0 / 0GB -Name CPU Cores RAM  +Which package would you like to deploy the VM in? (Press ENTER to make your selection):  / 0 / 0GB +Name CPU Cores Memory   0 0GB  @@ -27,7 +27,7 @@ -Which distribution would you like? (Press ENTER to make your selection):  +Which distribution would you like to deploy the VM in? (Press ENTER to make your selection):   diff --git a/cmd/katapult/testdata/TestVMs_Create/key_throws_error.golden b/cmd/katapult/testdata/TestVMs_Create/key_throws_error.golden index a91fc40..6737a0e 100644 --- a/cmd/katapult/testdata/TestVMs_Create/key_throws_error.golden +++ b/cmd/katapult/testdata/TestVMs_Create/key_throws_error.golden @@ -18,8 +18,8 @@ -Which VM package would you like? (Press ENTER to make your selection):  / 0 / 0GB -Name CPU Cores RAM  +Which package would you like to deploy the VM in? (Press ENTER to make your selection):  / 0 / 0GB +Name CPU Cores Memory   0 0GB  @@ -27,7 +27,7 @@ -Which distribution would you like? (Press ENTER to make your selection):  +Which distribution would you like to deploy the VM in? (Press ENTER to make your selection):   diff --git a/cmd/katapult/testdata/TestVMs_Create/org_name_error.golden b/cmd/katapult/testdata/TestVMs_Create/org_name_error.golden new file mode 100644 index 0000000..6b1f833 --- /dev/null +++ b/cmd/katapult/testdata/TestVMs_Create/org_name_error.golden @@ -0,0 +1,16 @@ +-- STDOUT -- + +Usage: + vm create [flags] + +Flags: + -h, --help help for create + + + +-- BUILD SPEC -- + +{ + "OrgResult": {}, + "SpecResult": null +} diff --git a/cmd/katapult/testdata/TestVMs_Create/org_subdomain_error.golden b/cmd/katapult/testdata/TestVMs_Create/org_subdomain_error.golden new file mode 100644 index 0000000..6b1f833 --- /dev/null +++ b/cmd/katapult/testdata/TestVMs_Create/org_subdomain_error.golden @@ -0,0 +1,16 @@ +-- STDOUT -- + +Usage: + vm create [flags] + +Flags: + -h, --help help for create + + + +-- BUILD SPEC -- + +{ + "OrgResult": {}, + "SpecResult": null +} diff --git a/cmd/katapult/testdata/TestVMs_Create/package_id_error.golden b/cmd/katapult/testdata/TestVMs_Create/package_id_error.golden new file mode 100644 index 0000000..6b1f833 --- /dev/null +++ b/cmd/katapult/testdata/TestVMs_Create/package_id_error.golden @@ -0,0 +1,16 @@ +-- STDOUT -- + +Usage: + vm create [flags] + +Flags: + -h, --help help for create + + + +-- BUILD SPEC -- + +{ + "OrgResult": {}, + "SpecResult": null +} diff --git a/cmd/katapult/testdata/TestVMs_Create/package_name_error.golden b/cmd/katapult/testdata/TestVMs_Create/package_name_error.golden new file mode 100644 index 0000000..6b1f833 --- /dev/null +++ b/cmd/katapult/testdata/TestVMs_Create/package_name_error.golden @@ -0,0 +1,16 @@ +-- STDOUT -- + +Usage: + vm create [flags] + +Flags: + -h, --help help for create + + + +-- BUILD SPEC -- + +{ + "OrgResult": {}, + "SpecResult": null +} diff --git a/cmd/katapult/testdata/TestVMs_Create/success.golden b/cmd/katapult/testdata/TestVMs_Create/success.golden deleted file mode 100644 index f78ec06..0000000 --- a/cmd/katapult/testdata/TestVMs_Create/success.golden +++ /dev/null @@ -1,236 +0,0 @@ --- STDOUT -- - -Which organization would you like to deploy the VM in? (Press ENTER to make your selection): Loge Enthusiasts / loge -Name Subdomain  -Loge Enthusiasts loge  -testing, testing, 123 test - - - - - -Which organization would you like to deploy the VM in? (Press ENTER to make your selection): testing, testing, 123 / test -Name Subdomain  -Loge Enthusiasts loge -testing, testing, 123 test  - - - - - -Which DC would you like to deploy the VM in? (Press ENTER to make your selection): hello / Pogland -Name Country  -hello Pogland  -hello United Kingdom - - - - - -Which DC would you like to deploy the VM in? (Press ENTER to make your selection): hello / United Kingdom -Name Country  -hello Pogland -hello United Kingdom  - - - - - -Which VM package would you like? (Press ENTER to make your selection):  / 0 / 0GB -Name CPU Cores RAM  - 0 0GB  -Test 100 1000GB - - - - - -Which VM package would you like? (Press ENTER to make your selection): Test / 100 / 1000GB -Name CPU Cores RAM  - 0 0GB -Test 100 1000GB  - - - - - -Which distribution would you like? (Press ENTER to make your selection):  - -Ubuntu 20.04 - - - - - - -Which distribution would you like? (Press ENTER to make your selection): Ubuntu 20.04 - -Ubuntu 20.04 - - - - - - -Please select any IP addresses you wish to add. (Press ENTER to select items and ESC when you are done with your selections):  /  - Address Reverse DNS  -[ ]   -[ ]  -[ ] 8.8.8.8 ip-8-8-8-8.test.katapult.cloud - - - - -Please select any IP addresses you wish to add. (Press ENTER to select items and ESC when you are done with your selections):  /  - Address Reverse DNS  -[ ]  -[ ]   -[ ] 8.8.8.8 ip-8-8-8-8.test.katapult.cloud - - - - -Please select any IP addresses you wish to add. (Press ENTER to select items and ESC when you are done with your selections): 8.8.8.8 / ip-8-8-8-8.test.katapult.cloud - Address Reverse DNS  -[ ]  -[ ]  -[ ] 8.8.8.8 ip-8-8-8-8.test.katapult.cloud  - - - - -Please select any IP addresses you wish to add. (Press ENTER to select items and ESC when you are done with your selections): 8.8.8.8 / ip-8-8-8-8.test.katapult.cloud - Address Reverse DNS  -[ ]  -[ ]  -[*] 8.8.8.8 ip-8-8-8-8.test.katapult.cloud  - - - - -Which organization SSH keys do you wish to add? (Press ENTER to select items and ESC when you are done with your selections):  /  - Name Fingerprint  -[ ]   -[ ]  -[ ] testing 22:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c - - - - -Which organization SSH keys do you wish to add? (Press ENTER to select items and ESC when you are done with your selections):  /  - Name Fingerprint  -[ ]  -[ ]   -[ ] testing 22:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c - - - - -Which organization SSH keys do you wish to add? (Press ENTER to select items and ESC when you are done with your selections): testing / 22:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c - Name Fingerprint  -[ ]  -[ ]  -[ ] testing 22:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c  - - - - -Which organization SSH keys do you wish to add? (Press ENTER to select items and ESC when you are done with your selections): testing / 22:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c - Name Fingerprint  -[ ]  -[ ]  -[*] testing 22:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c  - - - - -Do you wish to add any tags? (Press ENTER to select items and ESC when you are done with your selections):  -[ ]  -[ ]  -[ ] Testing - - - - - -Do you wish to add any tags? (Press ENTER to select items and ESC when you are done with your selections):  -[ ]  -[ ]  -[ ] Testing - - - - - -Do you wish to add any tags? (Press ENTER to select items and ESC when you are done with your selections): Testing -[ ]  -[ ]  -[ ] Testing - - - - - -Do you wish to add any tags? (Press ENTER to select items and ESC when you are done with your selections): Testing -[ ]  -[ ]  -[*] Testing - - - - - -What would you like the virtual machine to be called? If you want a hostname, what do you want it to be? If you want a description, what do you want it to be? - --- BUILD SPEC -- - -{ - "OrgResult": { - "id": "testing" - }, - "SpecResult": { - "data_center": { - "id": "dc_9UVoPiUQoI1cqtR0" - }, - "resources": { - "package": { - "id": "vmpkg_9UVoPiUQoI1cqtRd" - } - }, - "disk_template": { - "id": "disk_9UVoPiUQoI1cqtRd", - "options": [ - { - "key": "install_agent", - "value": "true" - } - ] - }, - "network_interfaces": [ - { - "network": { - "id": "test" - }, - "ip_address_allocations": [ - { - "type": "existing", - "ip_address": { - "id": "ip_UVoPiUQoI1cqtRf5" - } - } - ] - } - ], - "hostname": "hosttest", - "name": "nametest", - "description": "desctest", - "authorized_keys": { - "ssh_keys": [ - "key_PiUQoI1cqt43Dkf" - ] - }, - "tags": [ - "DO_NOT_PICK_IGNORE_THIS_ONE" - ] - } -} diff --git a/cmd/katapult/testdata/TestVMs_Create/success_from_full_env.golden b/cmd/katapult/testdata/TestVMs_Create/success_from_full_env.golden new file mode 100644 index 0000000..2a3fc02 --- /dev/null +++ b/cmd/katapult/testdata/TestVMs_Create/success_from_full_env.golden @@ -0,0 +1,84 @@ +-- STDOUT -- + + + +-- BUILD SPEC -- + +{ + "OrgResult": { + "id": "loge" + }, + "SpecResult": { + "data_center": { + "id": "dc_9UVoPiUQoI1cqtRd" + }, + "resources": { + "package": { + "id": "DO_NOT_PICK_IGNORE_THIS_ONE" + } + }, + "disk_template": { + "id": "DO_NOT_PICK_IGNORE_THIS_ONE", + "options": [ + { + "key": "install_agent", + "value": "true" + } + ] + }, + "network_interfaces": [ + { + "network": { + "id": "test" + }, + "ip_address_allocations": [ + { + "type": "existing", + "ip_address": { + "id": "ip_VVoPiUQoI1cqtRf5" + } + } + ] + }, + { + "network": { + "id": "test" + }, + "ip_address_allocations": [ + { + "type": "existing", + "ip_address": { + "id": "ip_VVoPiUQoI1cqtRf5" + } + } + ] + }, + { + "network": { + "id": "test" + }, + "ip_address_allocations": [ + { + "type": "existing", + "ip_address": { + "id": "ip_VVoPiUQoI1cqtRf5" + } + } + ] + } + ], + "hostname": "testing", + "name": "test", + "description": "123", + "authorized_keys": { + "ssh_keys": [ + "key_PiUQoI1cqt43Dkf", + "key_PiUQoI1cqt43Dkg", + "key_PiUQoI1cqt43Dkd", + "key_PiUQoI1cqt43Dkc", + "key_PiUQoI1cqt43Dkb", + "key_PiUQoI1cqt43Dka" + ] + } + } +} diff --git a/cmd/katapult/testdata/TestVMs_Create/success_from_full_minus_hostname_env.golden b/cmd/katapult/testdata/TestVMs_Create/success_from_full_minus_hostname_env.golden new file mode 100644 index 0000000..b514fd6 --- /dev/null +++ b/cmd/katapult/testdata/TestVMs_Create/success_from_full_minus_hostname_env.golden @@ -0,0 +1,156 @@ +-- STDOUT -- + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +│ The hostname of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +│ The hostname of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │t▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +│ The hostname of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │te▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +│ The hostname of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │tes▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +│ The hostname of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │test▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +│ The hostname of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │testi▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +│ The hostname of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │testin▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +│ The hostname of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │testing▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + + + +-- BUILD SPEC -- + +{ + "OrgResult": { + "id": "loge" + }, + "SpecResult": { + "data_center": { + "id": "dc_9UVoPiUQoI1cqtRd" + }, + "resources": { + "package": { + "id": "DO_NOT_PICK_IGNORE_THIS_ONE" + } + }, + "disk_template": { + "id": "DO_NOT_PICK_IGNORE_THIS_ONE", + "options": [ + { + "key": "install_agent", + "value": "true" + } + ] + }, + "network_interfaces": [ + { + "network": { + "id": "test" + }, + "ip_address_allocations": [ + { + "type": "existing", + "ip_address": { + "id": "ip_VVoPiUQoI1cqtRf5" + } + } + ] + }, + { + "network": { + "id": "test" + }, + "ip_address_allocations": [ + { + "type": "existing", + "ip_address": { + "id": "ip_VVoPiUQoI1cqtRf5" + } + } + ] + }, + { + "network": { + "id": "test" + }, + "ip_address_allocations": [ + { + "type": "existing", + "ip_address": { + "id": "ip_VVoPiUQoI1cqtRf5" + } + } + ] + } + ], + "hostname": "testing", + "name": "test", + "description": "123", + "authorized_keys": { + "ssh_keys": [ + "key_PiUQoI1cqt43Dkf", + "key_PiUQoI1cqt43Dkg", + "key_PiUQoI1cqt43Dkd", + "key_PiUQoI1cqt43Dkc", + "key_PiUQoI1cqt43Dkb", + "key_PiUQoI1cqt43Dka" + ] + } + } +} diff --git a/cmd/katapult/testdata/TestVMs_Create/success_with_dc_name_env.golden b/cmd/katapult/testdata/TestVMs_Create/success_with_dc_name_env.golden new file mode 100644 index 0000000..b6e8f91 --- /dev/null +++ b/cmd/katapult/testdata/TestVMs_Create/success_with_dc_name_env.golden @@ -0,0 +1,461 @@ +-- STDOUT -- + +Which organization would you like to deploy the VM in? (Press ENTER to make your selection): Loge Enthusiasts / loge +Name Subdomain  +Loge Enthusiasts loge  +testing, testing, 123 test + + + + + +Which organization would you like to deploy the VM in? (Press ENTER to make your selection): testing, testing, 123 / test +Name Subdomain  +Loge Enthusiasts loge +testing, testing, 123 test  + + + + + +Which package would you like to deploy the VM in? (Press ENTER to make your selection):  / 0 / 0GB +Name CPU Cores Memory  + 0 0GB  +Test 100 1000GB + + + + + +Which package would you like to deploy the VM in? (Press ENTER to make your selection): Test / 100 / 1000GB +Name CPU Cores Memory  + 0 0GB +Test 100 1000GB  + + + + + +Which distribution would you like to deploy the VM in? (Press ENTER to make your selection):  + +Ubuntu 20.04 + + + + + + +Which distribution would you like to deploy the VM in? (Press ENTER to make your selection): Ubuntu 20.04 + +Ubuntu 20.04 + + + + + + +Please select any IP addresses you wish to add. (Press ENTER to select items and ESC when you are done with your selections):  /  + Address Reverse DNS  +[ ]   +[ ]  +[ ] 8.8.8.8 ip-8-8-8-8.test.katapult.cloud +[ ] 1.1.1.1 ip-1-1-1-1.test.katapult.cloud +[ ] 1.1.1.2 ip-1-1-1-2.test.katapult.cloud +[ ] 1.1.1.3 ip-1-1-1-3.test.katapult.cloud + +Please select any IP addresses you wish to add. (Press ENTER to select items and ESC when you are done with your selections):  /  + Address Reverse DNS  +[ ]  +[ ]   +[ ] 8.8.8.8 ip-8-8-8-8.test.katapult.cloud +[ ] 1.1.1.1 ip-1-1-1-1.test.katapult.cloud +[ ] 1.1.1.2 ip-1-1-1-2.test.katapult.cloud +[ ] 1.1.1.3 ip-1-1-1-3.test.katapult.cloud + +Please select any IP addresses you wish to add. (Press ENTER to select items and ESC when you are done with your selections): 8.8.8.8 / ip-8-8-8-8.test.katapult.cloud + Address Reverse DNS  +[ ]  +[ ]  +[ ] 8.8.8.8 ip-8-8-8-8.test.katapult.cloud  +[ ] 1.1.1.1 ip-1-1-1-1.test.katapult.cloud +[ ] 1.1.1.2 ip-1-1-1-2.test.katapult.cloud +[ ] 1.1.1.3 ip-1-1-1-3.test.katapult.cloud + +Please select any IP addresses you wish to add. (Press ENTER to select items and ESC when you are done with your selections): 8.8.8.8 / ip-8-8-8-8.test.katapult.cloud + Address Reverse DNS  +[ ]  +[ ]  +[*] 8.8.8.8 ip-8-8-8-8.test.katapult.cloud  +[ ] 1.1.1.1 ip-1-1-1-1.test.katapult.cloud +[ ] 1.1.1.2 ip-1-1-1-2.test.katapult.cloud +[ ] 1.1.1.3 ip-1-1-1-3.test.katapult.cloud + +Which organization SSH keys do you wish to add? (Press ENTER to select items and ESC when you are done with your selections):  /  + Name Fingerprint  +[ ]   +[ ]  +[ ] testing 22:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing1 23:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing2 24:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing3 25:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing4 26:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +Which organization SSH keys do you wish to add? (Press ENTER to select items and ESC when you are done with your selections):  /  + Name Fingerprint  +[ ]  +[ ]   +[ ] testing 22:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing1 23:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing2 24:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing3 25:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing4 26:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +Which organization SSH keys do you wish to add? (Press ENTER to select items and ESC when you are done with your selections): testing / 22:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c + Name Fingerprint  +[ ]  +[ ]  +[ ] testing 22:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c  +[ ] testing1 23:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing2 24:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing3 25:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing4 26:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +Which organization SSH keys do you wish to add? (Press ENTER to select items and ESC when you are done with your selections): testing / 22:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c + Name Fingerprint  +[ ]  +[ ]  +[*] testing 22:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c  +[ ] testing1 23:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing2 24:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing3 25:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing4 26:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +Do you wish to add any tags? (Press ENTER to select items and ESC when you are done with your selections): A +[ ] A +[ ] B +[ ] Testing +[ ] Testing 1 +[ ] Testing 2 +[ ] Testing 3 +[ ] Testing 4 + +Do you wish to add any tags? (Press ENTER to select items and ESC when you are done with your selections): B +[ ] A +[ ] B +[ ] Testing +[ ] Testing 1 +[ ] Testing 2 +[ ] Testing 3 +[ ] Testing 4 + +Do you wish to add any tags? (Press ENTER to select items and ESC when you are done with your selections): Testing +[ ] A +[ ] B +[ ] Testing +[ ] Testing 1 +[ ] Testing 2 +[ ] Testing 3 +[ ] Testing 4 + +Do you wish to add any tags? (Press ENTER to select items and ESC when you are done with your selections): Testing +[ ] A +[ ] B +[*] Testing +[ ] Testing 1 +[ ] Testing 2 +[ ] Testing 3 +[ ] Testing 4 + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Name │ +│ The name of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Name │ +│ The name of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │n▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Name │ +│ The name of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │na▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Name │ +│ The name of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │nam▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Name │ +│ The name of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │name▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Name │ +│ The name of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │namet▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Name │ +│ The name of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │namete▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Name │ +│ The name of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │nametes▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Name │ +│ The name of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │nametest▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +│ The hostname of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +│ The hostname of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │h▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +│ The hostname of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │ho▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +│ The hostname of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │hos▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +│ The hostname of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │host▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +│ The hostname of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │hostt▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +│ The hostname of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │hostte▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +│ The hostname of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │hosttes▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +│ The hostname of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │hosttest▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +│ The description of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +│ The description of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │d▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +│ The description of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │de▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +│ The description of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │des▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +│ The description of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │desc▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +│ The description of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │desct▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +│ The description of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │descte▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +│ The description of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │desctes▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +│ The description of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │desctest▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + + + +-- BUILD SPEC -- + +{ + "OrgResult": { + "id": "testing" + }, + "SpecResult": { + "data_center": { + "id": "dc_9UVoPiUQoI1cqtRd" + }, + "resources": { + "package": { + "id": "vmpkg_9UVoPiUQoI1cqtRd" + } + }, + "disk_template": { + "id": "disk_9UVoPiUQoI1cqtRd", + "options": [ + { + "key": "install_agent", + "value": "true" + } + ] + }, + "network_interfaces": [ + { + "network": { + "id": "test" + }, + "ip_address_allocations": [ + { + "type": "existing", + "ip_address": { + "id": "ip_UVoPiUQoI1cqtRf5" + } + } + ] + } + ], + "hostname": "hosttest", + "name": "nametest", + "description": "desctest", + "authorized_keys": { + "ssh_keys": [ + "key_PiUQoI1cqt43Dkf" + ] + }, + "tags": [ + "tag_PiUQoI1cqt43gei" + ] + } +} diff --git a/cmd/katapult/testdata/TestVMs_Create/success_with_distribution_name_env.golden b/cmd/katapult/testdata/TestVMs_Create/success_with_distribution_name_env.golden new file mode 100644 index 0000000..6ee48b3 --- /dev/null +++ b/cmd/katapult/testdata/TestVMs_Create/success_with_distribution_name_env.golden @@ -0,0 +1,461 @@ +-- STDOUT -- + +Which organization would you like to deploy the VM in? (Press ENTER to make your selection): Loge Enthusiasts / loge +Name Subdomain  +Loge Enthusiasts loge  +testing, testing, 123 test + + + + + +Which organization would you like to deploy the VM in? (Press ENTER to make your selection): testing, testing, 123 / test +Name Subdomain  +Loge Enthusiasts loge +testing, testing, 123 test  + + + + + +Which DC would you like to deploy the VM in? (Press ENTER to make your selection): hello / Pogland +Name Country  +hello Pogland  +hello United Kingdom + + + + + +Which DC would you like to deploy the VM in? (Press ENTER to make your selection): hello / United Kingdom +Name Country  +hello Pogland +hello United Kingdom  + + + + + +Which package would you like to deploy the VM in? (Press ENTER to make your selection):  / 0 / 0GB +Name CPU Cores Memory  + 0 0GB  +Test 100 1000GB + + + + + +Which package would you like to deploy the VM in? (Press ENTER to make your selection): Test / 100 / 1000GB +Name CPU Cores Memory  + 0 0GB +Test 100 1000GB  + + + + + +Please select any IP addresses you wish to add. (Press ENTER to select items and ESC when you are done with your selections):  /  + Address Reverse DNS  +[ ]   +[ ]  +[ ] 8.8.8.8 ip-8-8-8-8.test.katapult.cloud +[ ] 1.1.1.1 ip-1-1-1-1.test.katapult.cloud +[ ] 1.1.1.2 ip-1-1-1-2.test.katapult.cloud +[ ] 1.1.1.3 ip-1-1-1-3.test.katapult.cloud + +Please select any IP addresses you wish to add. (Press ENTER to select items and ESC when you are done with your selections):  /  + Address Reverse DNS  +[ ]  +[ ]   +[ ] 8.8.8.8 ip-8-8-8-8.test.katapult.cloud +[ ] 1.1.1.1 ip-1-1-1-1.test.katapult.cloud +[ ] 1.1.1.2 ip-1-1-1-2.test.katapult.cloud +[ ] 1.1.1.3 ip-1-1-1-3.test.katapult.cloud + +Please select any IP addresses you wish to add. (Press ENTER to select items and ESC when you are done with your selections): 8.8.8.8 / ip-8-8-8-8.test.katapult.cloud + Address Reverse DNS  +[ ]  +[ ]  +[ ] 8.8.8.8 ip-8-8-8-8.test.katapult.cloud  +[ ] 1.1.1.1 ip-1-1-1-1.test.katapult.cloud +[ ] 1.1.1.2 ip-1-1-1-2.test.katapult.cloud +[ ] 1.1.1.3 ip-1-1-1-3.test.katapult.cloud + +Please select any IP addresses you wish to add. (Press ENTER to select items and ESC when you are done with your selections): 8.8.8.8 / ip-8-8-8-8.test.katapult.cloud + Address Reverse DNS  +[ ]  +[ ]  +[*] 8.8.8.8 ip-8-8-8-8.test.katapult.cloud  +[ ] 1.1.1.1 ip-1-1-1-1.test.katapult.cloud +[ ] 1.1.1.2 ip-1-1-1-2.test.katapult.cloud +[ ] 1.1.1.3 ip-1-1-1-3.test.katapult.cloud + +Which organization SSH keys do you wish to add? (Press ENTER to select items and ESC when you are done with your selections):  /  + Name Fingerprint  +[ ]   +[ ]  +[ ] testing 22:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing1 23:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing2 24:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing3 25:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing4 26:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +Which organization SSH keys do you wish to add? (Press ENTER to select items and ESC when you are done with your selections):  /  + Name Fingerprint  +[ ]  +[ ]   +[ ] testing 22:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing1 23:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing2 24:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing3 25:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing4 26:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +Which organization SSH keys do you wish to add? (Press ENTER to select items and ESC when you are done with your selections): testing / 22:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c + Name Fingerprint  +[ ]  +[ ]  +[ ] testing 22:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c  +[ ] testing1 23:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing2 24:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing3 25:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing4 26:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +Which organization SSH keys do you wish to add? (Press ENTER to select items and ESC when you are done with your selections): testing / 22:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c + Name Fingerprint  +[ ]  +[ ]  +[*] testing 22:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c  +[ ] testing1 23:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing2 24:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing3 25:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing4 26:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +Do you wish to add any tags? (Press ENTER to select items and ESC when you are done with your selections): A +[ ] A +[ ] B +[ ] Testing +[ ] Testing 1 +[ ] Testing 2 +[ ] Testing 3 +[ ] Testing 4 + +Do you wish to add any tags? (Press ENTER to select items and ESC when you are done with your selections): B +[ ] A +[ ] B +[ ] Testing +[ ] Testing 1 +[ ] Testing 2 +[ ] Testing 3 +[ ] Testing 4 + +Do you wish to add any tags? (Press ENTER to select items and ESC when you are done with your selections): Testing +[ ] A +[ ] B +[ ] Testing +[ ] Testing 1 +[ ] Testing 2 +[ ] Testing 3 +[ ] Testing 4 + +Do you wish to add any tags? (Press ENTER to select items and ESC when you are done with your selections): Testing +[ ] A +[ ] B +[*] Testing +[ ] Testing 1 +[ ] Testing 2 +[ ] Testing 3 +[ ] Testing 4 + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Name │ +│ The name of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Name │ +│ The name of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │n▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Name │ +│ The name of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │na▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Name │ +│ The name of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │nam▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Name │ +│ The name of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │name▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Name │ +│ The name of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │namet▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Name │ +│ The name of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │namete▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Name │ +│ The name of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │nametes▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Name │ +│ The name of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │nametest▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +│ The hostname of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +│ The hostname of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │h▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +│ The hostname of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │ho▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +│ The hostname of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │hos▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +│ The hostname of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │host▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +│ The hostname of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │hostt▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +│ The hostname of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │hostte▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +│ The hostname of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │hosttes▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +│ The hostname of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │hosttest▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +│ The description of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +│ The description of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │d▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +│ The description of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │de▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +│ The description of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │des▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +│ The description of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │desc▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +│ The description of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │desct▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +│ The description of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │descte▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +│ The description of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │desctes▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +│ The description of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │desctest▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + + + +-- BUILD SPEC -- + +{ + "OrgResult": { + "id": "testing" + }, + "SpecResult": { + "data_center": { + "id": "dc_9UVoPiUQoI1cqtR0" + }, + "resources": { + "package": { + "id": "vmpkg_9UVoPiUQoI1cqtRd" + } + }, + "disk_template": { + "id": "disk_9UVoPiUQoI1cqtRd", + "options": [ + { + "key": "install_agent", + "value": "true" + } + ] + }, + "network_interfaces": [ + { + "network": { + "id": "test" + }, + "ip_address_allocations": [ + { + "type": "existing", + "ip_address": { + "id": "ip_UVoPiUQoI1cqtRf5" + } + } + ] + } + ], + "hostname": "hosttest", + "name": "nametest", + "description": "desctest", + "authorized_keys": { + "ssh_keys": [ + "key_PiUQoI1cqt43Dkf" + ] + }, + "tags": [ + "tag_PiUQoI1cqt43gei" + ] + } +} diff --git a/cmd/katapult/testdata/TestVMs_Create/success_with_no_env.golden b/cmd/katapult/testdata/TestVMs_Create/success_with_no_env.golden new file mode 100644 index 0000000..24e24aa --- /dev/null +++ b/cmd/katapult/testdata/TestVMs_Create/success_with_no_env.golden @@ -0,0 +1,479 @@ +-- STDOUT -- + +Which organization would you like to deploy the VM in? (Press ENTER to make your selection): Loge Enthusiasts / loge +Name Subdomain  +Loge Enthusiasts loge  +testing, testing, 123 test + + + + + +Which organization would you like to deploy the VM in? (Press ENTER to make your selection): testing, testing, 123 / test +Name Subdomain  +Loge Enthusiasts loge +testing, testing, 123 test  + + + + + +Which DC would you like to deploy the VM in? (Press ENTER to make your selection): hello / Pogland +Name Country  +hello Pogland  +hello United Kingdom + + + + + +Which DC would you like to deploy the VM in? (Press ENTER to make your selection): hello / United Kingdom +Name Country  +hello Pogland +hello United Kingdom  + + + + + +Which package would you like to deploy the VM in? (Press ENTER to make your selection):  / 0 / 0GB +Name CPU Cores Memory  + 0 0GB  +Test 100 1000GB + + + + + +Which package would you like to deploy the VM in? (Press ENTER to make your selection): Test / 100 / 1000GB +Name CPU Cores Memory  + 0 0GB +Test 100 1000GB  + + + + + +Which distribution would you like to deploy the VM in? (Press ENTER to make your selection):  + +Ubuntu 20.04 + + + + + + +Which distribution would you like to deploy the VM in? (Press ENTER to make your selection): Ubuntu 20.04 + +Ubuntu 20.04 + + + + + + +Please select any IP addresses you wish to add. (Press ENTER to select items and ESC when you are done with your selections):  /  + Address Reverse DNS  +[ ]   +[ ]  +[ ] 8.8.8.8 ip-8-8-8-8.test.katapult.cloud +[ ] 1.1.1.1 ip-1-1-1-1.test.katapult.cloud +[ ] 1.1.1.2 ip-1-1-1-2.test.katapult.cloud +[ ] 1.1.1.3 ip-1-1-1-3.test.katapult.cloud + +Please select any IP addresses you wish to add. (Press ENTER to select items and ESC when you are done with your selections):  /  + Address Reverse DNS  +[ ]  +[ ]   +[ ] 8.8.8.8 ip-8-8-8-8.test.katapult.cloud +[ ] 1.1.1.1 ip-1-1-1-1.test.katapult.cloud +[ ] 1.1.1.2 ip-1-1-1-2.test.katapult.cloud +[ ] 1.1.1.3 ip-1-1-1-3.test.katapult.cloud + +Please select any IP addresses you wish to add. (Press ENTER to select items and ESC when you are done with your selections): 8.8.8.8 / ip-8-8-8-8.test.katapult.cloud + Address Reverse DNS  +[ ]  +[ ]  +[ ] 8.8.8.8 ip-8-8-8-8.test.katapult.cloud  +[ ] 1.1.1.1 ip-1-1-1-1.test.katapult.cloud +[ ] 1.1.1.2 ip-1-1-1-2.test.katapult.cloud +[ ] 1.1.1.3 ip-1-1-1-3.test.katapult.cloud + +Please select any IP addresses you wish to add. (Press ENTER to select items and ESC when you are done with your selections): 8.8.8.8 / ip-8-8-8-8.test.katapult.cloud + Address Reverse DNS  +[ ]  +[ ]  +[*] 8.8.8.8 ip-8-8-8-8.test.katapult.cloud  +[ ] 1.1.1.1 ip-1-1-1-1.test.katapult.cloud +[ ] 1.1.1.2 ip-1-1-1-2.test.katapult.cloud +[ ] 1.1.1.3 ip-1-1-1-3.test.katapult.cloud + +Which organization SSH keys do you wish to add? (Press ENTER to select items and ESC when you are done with your selections):  /  + Name Fingerprint  +[ ]   +[ ]  +[ ] testing 22:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing1 23:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing2 24:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing3 25:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing4 26:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +Which organization SSH keys do you wish to add? (Press ENTER to select items and ESC when you are done with your selections):  /  + Name Fingerprint  +[ ]  +[ ]   +[ ] testing 22:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing1 23:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing2 24:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing3 25:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing4 26:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +Which organization SSH keys do you wish to add? (Press ENTER to select items and ESC when you are done with your selections): testing / 22:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c + Name Fingerprint  +[ ]  +[ ]  +[ ] testing 22:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c  +[ ] testing1 23:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing2 24:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing3 25:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing4 26:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +Which organization SSH keys do you wish to add? (Press ENTER to select items and ESC when you are done with your selections): testing / 22:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c + Name Fingerprint  +[ ]  +[ ]  +[*] testing 22:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c  +[ ] testing1 23:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing2 24:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing3 25:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing4 26:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +Do you wish to add any tags? (Press ENTER to select items and ESC when you are done with your selections): A +[ ] A +[ ] B +[ ] Testing +[ ] Testing 1 +[ ] Testing 2 +[ ] Testing 3 +[ ] Testing 4 + +Do you wish to add any tags? (Press ENTER to select items and ESC when you are done with your selections): B +[ ] A +[ ] B +[ ] Testing +[ ] Testing 1 +[ ] Testing 2 +[ ] Testing 3 +[ ] Testing 4 + +Do you wish to add any tags? (Press ENTER to select items and ESC when you are done with your selections): Testing +[ ] A +[ ] B +[ ] Testing +[ ] Testing 1 +[ ] Testing 2 +[ ] Testing 3 +[ ] Testing 4 + +Do you wish to add any tags? (Press ENTER to select items and ESC when you are done with your selections): Testing +[ ] A +[ ] B +[*] Testing +[ ] Testing 1 +[ ] Testing 2 +[ ] Testing 3 +[ ] Testing 4 + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Name │ +│ The name of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Name │ +│ The name of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │n▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Name │ +│ The name of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │na▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Name │ +│ The name of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │nam▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Name │ +│ The name of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │name▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Name │ +│ The name of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │namet▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Name │ +│ The name of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │namete▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Name │ +│ The name of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │nametes▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Name │ +│ The name of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │nametest▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +│ The hostname of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +│ The hostname of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │h▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +│ The hostname of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │ho▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +│ The hostname of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │hos▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +│ The hostname of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │host▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +│ The hostname of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │hostt▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +│ The hostname of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │hostte▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +│ The hostname of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │hosttes▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +│ The hostname of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │hosttest▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +│ The description of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +│ The description of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │d▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +│ The description of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │de▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +│ The description of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │des▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +│ The description of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │desc▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +│ The description of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │desct▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +│ The description of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │descte▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +│ The description of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │desctes▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +│ The description of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │desctest▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + + + +-- BUILD SPEC -- + +{ + "OrgResult": { + "id": "testing" + }, + "SpecResult": { + "data_center": { + "id": "dc_9UVoPiUQoI1cqtR0" + }, + "resources": { + "package": { + "id": "vmpkg_9UVoPiUQoI1cqtRd" + } + }, + "disk_template": { + "id": "disk_9UVoPiUQoI1cqtRd", + "options": [ + { + "key": "install_agent", + "value": "true" + } + ] + }, + "network_interfaces": [ + { + "network": { + "id": "test" + }, + "ip_address_allocations": [ + { + "type": "existing", + "ip_address": { + "id": "ip_UVoPiUQoI1cqtRf5" + } + } + ] + } + ], + "hostname": "hosttest", + "name": "nametest", + "description": "desctest", + "authorized_keys": { + "ssh_keys": [ + "key_PiUQoI1cqt43Dkf" + ] + }, + "tags": [ + "tag_PiUQoI1cqt43gei" + ] + } +} diff --git a/cmd/katapult/testdata/TestVMs_Create/success_with_org_name_env.golden b/cmd/katapult/testdata/TestVMs_Create/success_with_org_name_env.golden new file mode 100644 index 0000000..80e9d8f --- /dev/null +++ b/cmd/katapult/testdata/TestVMs_Create/success_with_org_name_env.golden @@ -0,0 +1,461 @@ +-- STDOUT -- + +Which DC would you like to deploy the VM in? (Press ENTER to make your selection): hello / Pogland +Name Country  +hello Pogland  +hello United Kingdom + + + + + +Which DC would you like to deploy the VM in? (Press ENTER to make your selection): hello / United Kingdom +Name Country  +hello Pogland +hello United Kingdom  + + + + + +Which package would you like to deploy the VM in? (Press ENTER to make your selection):  / 0 / 0GB +Name CPU Cores Memory  + 0 0GB  +Test 100 1000GB + + + + + +Which package would you like to deploy the VM in? (Press ENTER to make your selection): Test / 100 / 1000GB +Name CPU Cores Memory  + 0 0GB +Test 100 1000GB  + + + + + +Which distribution would you like to deploy the VM in? (Press ENTER to make your selection):  + +Ubuntu 20.04 + + + + + + +Which distribution would you like to deploy the VM in? (Press ENTER to make your selection): Ubuntu 20.04 + +Ubuntu 20.04 + + + + + + +Please select any IP addresses you wish to add. (Press ENTER to select items and ESC when you are done with your selections):  /  + Address Reverse DNS  +[ ]   +[ ]  +[ ] 8.8.8.8 ip-8-8-8-8.test.katapult.cloud +[ ] 1.1.1.1 ip-1-1-1-1.test.katapult.cloud +[ ] 1.1.1.2 ip-1-1-1-2.test.katapult.cloud +[ ] 1.1.1.3 ip-1-1-1-3.test.katapult.cloud + +Please select any IP addresses you wish to add. (Press ENTER to select items and ESC when you are done with your selections):  /  + Address Reverse DNS  +[ ]  +[ ]   +[ ] 8.8.8.8 ip-8-8-8-8.test.katapult.cloud +[ ] 1.1.1.1 ip-1-1-1-1.test.katapult.cloud +[ ] 1.1.1.2 ip-1-1-1-2.test.katapult.cloud +[ ] 1.1.1.3 ip-1-1-1-3.test.katapult.cloud + +Please select any IP addresses you wish to add. (Press ENTER to select items and ESC when you are done with your selections): 8.8.8.8 / ip-8-8-8-8.test.katapult.cloud + Address Reverse DNS  +[ ]  +[ ]  +[ ] 8.8.8.8 ip-8-8-8-8.test.katapult.cloud  +[ ] 1.1.1.1 ip-1-1-1-1.test.katapult.cloud +[ ] 1.1.1.2 ip-1-1-1-2.test.katapult.cloud +[ ] 1.1.1.3 ip-1-1-1-3.test.katapult.cloud + +Please select any IP addresses you wish to add. (Press ENTER to select items and ESC when you are done with your selections): 8.8.8.8 / ip-8-8-8-8.test.katapult.cloud + Address Reverse DNS  +[ ]  +[ ]  +[*] 8.8.8.8 ip-8-8-8-8.test.katapult.cloud  +[ ] 1.1.1.1 ip-1-1-1-1.test.katapult.cloud +[ ] 1.1.1.2 ip-1-1-1-2.test.katapult.cloud +[ ] 1.1.1.3 ip-1-1-1-3.test.katapult.cloud + +Which organization SSH keys do you wish to add? (Press ENTER to select items and ESC when you are done with your selections):  /  + Name Fingerprint  +[ ]   +[ ]  +[ ] testing 22:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing1 23:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing2 24:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing3 25:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing4 26:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +Which organization SSH keys do you wish to add? (Press ENTER to select items and ESC when you are done with your selections):  /  + Name Fingerprint  +[ ]  +[ ]   +[ ] testing 22:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing1 23:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing2 24:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing3 25:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing4 26:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +Which organization SSH keys do you wish to add? (Press ENTER to select items and ESC when you are done with your selections): testing / 22:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c + Name Fingerprint  +[ ]  +[ ]  +[ ] testing 22:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c  +[ ] testing1 23:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing2 24:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing3 25:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing4 26:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +Which organization SSH keys do you wish to add? (Press ENTER to select items and ESC when you are done with your selections): testing / 22:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c + Name Fingerprint  +[ ]  +[ ]  +[*] testing 22:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c  +[ ] testing1 23:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing2 24:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing3 25:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing4 26:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +Do you wish to add any tags? (Press ENTER to select items and ESC when you are done with your selections): A +[ ] A +[ ] B +[ ] Testing +[ ] Testing 1 +[ ] Testing 2 +[ ] Testing 3 +[ ] Testing 4 + +Do you wish to add any tags? (Press ENTER to select items and ESC when you are done with your selections): B +[ ] A +[ ] B +[ ] Testing +[ ] Testing 1 +[ ] Testing 2 +[ ] Testing 3 +[ ] Testing 4 + +Do you wish to add any tags? (Press ENTER to select items and ESC when you are done with your selections): Testing +[ ] A +[ ] B +[ ] Testing +[ ] Testing 1 +[ ] Testing 2 +[ ] Testing 3 +[ ] Testing 4 + +Do you wish to add any tags? (Press ENTER to select items and ESC when you are done with your selections): Testing +[ ] A +[ ] B +[*] Testing +[ ] Testing 1 +[ ] Testing 2 +[ ] Testing 3 +[ ] Testing 4 + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Name │ +│ The name of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Name │ +│ The name of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │n▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Name │ +│ The name of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │na▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Name │ +│ The name of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │nam▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Name │ +│ The name of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │name▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Name │ +│ The name of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │namet▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Name │ +│ The name of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │namete▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Name │ +│ The name of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │nametes▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Name │ +│ The name of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │nametest▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +│ The hostname of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +│ The hostname of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │h▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +│ The hostname of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │ho▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +│ The hostname of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │hos▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +│ The hostname of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │host▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +│ The hostname of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │hostt▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +│ The hostname of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │hostte▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +│ The hostname of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │hosttes▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +│ The hostname of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │hosttest▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +│ The description of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +│ The description of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │d▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +│ The description of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │de▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +│ The description of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │des▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +│ The description of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │desc▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +│ The description of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │desct▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +│ The description of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │descte▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +│ The description of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │desctes▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +│ The description of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │desctest▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + + + +-- BUILD SPEC -- + +{ + "OrgResult": { + "id": "loge" + }, + "SpecResult": { + "data_center": { + "id": "dc_9UVoPiUQoI1cqtR0" + }, + "resources": { + "package": { + "id": "vmpkg_9UVoPiUQoI1cqtRd" + } + }, + "disk_template": { + "id": "disk_9UVoPiUQoI1cqtRd", + "options": [ + { + "key": "install_agent", + "value": "true" + } + ] + }, + "network_interfaces": [ + { + "network": { + "id": "test" + }, + "ip_address_allocations": [ + { + "type": "existing", + "ip_address": { + "id": "ip_UVoPiUQoI1cqtRf5" + } + } + ] + } + ], + "hostname": "hosttest", + "name": "nametest", + "description": "desctest", + "authorized_keys": { + "ssh_keys": [ + "key_PiUQoI1cqt43Dkf" + ] + }, + "tags": [ + "tag_PiUQoI1cqt43gei" + ] + } +} diff --git a/cmd/katapult/testdata/TestVMs_Create/success_with_package_name_env.golden b/cmd/katapult/testdata/TestVMs_Create/success_with_package_name_env.golden new file mode 100644 index 0000000..2ecb5ed --- /dev/null +++ b/cmd/katapult/testdata/TestVMs_Create/success_with_package_name_env.golden @@ -0,0 +1,461 @@ +-- STDOUT -- + +Which organization would you like to deploy the VM in? (Press ENTER to make your selection): Loge Enthusiasts / loge +Name Subdomain  +Loge Enthusiasts loge  +testing, testing, 123 test + + + + + +Which organization would you like to deploy the VM in? (Press ENTER to make your selection): testing, testing, 123 / test +Name Subdomain  +Loge Enthusiasts loge +testing, testing, 123 test  + + + + + +Which DC would you like to deploy the VM in? (Press ENTER to make your selection): hello / Pogland +Name Country  +hello Pogland  +hello United Kingdom + + + + + +Which DC would you like to deploy the VM in? (Press ENTER to make your selection): hello / United Kingdom +Name Country  +hello Pogland +hello United Kingdom  + + + + + +Which distribution would you like to deploy the VM in? (Press ENTER to make your selection):  + +Ubuntu 20.04 + + + + + + +Which distribution would you like to deploy the VM in? (Press ENTER to make your selection): Ubuntu 20.04 + +Ubuntu 20.04 + + + + + + +Please select any IP addresses you wish to add. (Press ENTER to select items and ESC when you are done with your selections):  /  + Address Reverse DNS  +[ ]   +[ ]  +[ ] 8.8.8.8 ip-8-8-8-8.test.katapult.cloud +[ ] 1.1.1.1 ip-1-1-1-1.test.katapult.cloud +[ ] 1.1.1.2 ip-1-1-1-2.test.katapult.cloud +[ ] 1.1.1.3 ip-1-1-1-3.test.katapult.cloud + +Please select any IP addresses you wish to add. (Press ENTER to select items and ESC when you are done with your selections):  /  + Address Reverse DNS  +[ ]  +[ ]   +[ ] 8.8.8.8 ip-8-8-8-8.test.katapult.cloud +[ ] 1.1.1.1 ip-1-1-1-1.test.katapult.cloud +[ ] 1.1.1.2 ip-1-1-1-2.test.katapult.cloud +[ ] 1.1.1.3 ip-1-1-1-3.test.katapult.cloud + +Please select any IP addresses you wish to add. (Press ENTER to select items and ESC when you are done with your selections): 8.8.8.8 / ip-8-8-8-8.test.katapult.cloud + Address Reverse DNS  +[ ]  +[ ]  +[ ] 8.8.8.8 ip-8-8-8-8.test.katapult.cloud  +[ ] 1.1.1.1 ip-1-1-1-1.test.katapult.cloud +[ ] 1.1.1.2 ip-1-1-1-2.test.katapult.cloud +[ ] 1.1.1.3 ip-1-1-1-3.test.katapult.cloud + +Please select any IP addresses you wish to add. (Press ENTER to select items and ESC when you are done with your selections): 8.8.8.8 / ip-8-8-8-8.test.katapult.cloud + Address Reverse DNS  +[ ]  +[ ]  +[*] 8.8.8.8 ip-8-8-8-8.test.katapult.cloud  +[ ] 1.1.1.1 ip-1-1-1-1.test.katapult.cloud +[ ] 1.1.1.2 ip-1-1-1-2.test.katapult.cloud +[ ] 1.1.1.3 ip-1-1-1-3.test.katapult.cloud + +Which organization SSH keys do you wish to add? (Press ENTER to select items and ESC when you are done with your selections):  /  + Name Fingerprint  +[ ]   +[ ]  +[ ] testing 22:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing1 23:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing2 24:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing3 25:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing4 26:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +Which organization SSH keys do you wish to add? (Press ENTER to select items and ESC when you are done with your selections):  /  + Name Fingerprint  +[ ]  +[ ]   +[ ] testing 22:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing1 23:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing2 24:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing3 25:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing4 26:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +Which organization SSH keys do you wish to add? (Press ENTER to select items and ESC when you are done with your selections): testing / 22:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c + Name Fingerprint  +[ ]  +[ ]  +[ ] testing 22:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c  +[ ] testing1 23:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing2 24:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing3 25:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing4 26:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +Which organization SSH keys do you wish to add? (Press ENTER to select items and ESC when you are done with your selections): testing / 22:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c + Name Fingerprint  +[ ]  +[ ]  +[*] testing 22:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c  +[ ] testing1 23:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing2 24:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing3 25:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +[ ] testing4 26:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c +Do you wish to add any tags? (Press ENTER to select items and ESC when you are done with your selections): A +[ ] A +[ ] B +[ ] Testing +[ ] Testing 1 +[ ] Testing 2 +[ ] Testing 3 +[ ] Testing 4 + +Do you wish to add any tags? (Press ENTER to select items and ESC when you are done with your selections): B +[ ] A +[ ] B +[ ] Testing +[ ] Testing 1 +[ ] Testing 2 +[ ] Testing 3 +[ ] Testing 4 + +Do you wish to add any tags? (Press ENTER to select items and ESC when you are done with your selections): Testing +[ ] A +[ ] B +[ ] Testing +[ ] Testing 1 +[ ] Testing 2 +[ ] Testing 3 +[ ] Testing 4 + +Do you wish to add any tags? (Press ENTER to select items and ESC when you are done with your selections): Testing +[ ] A +[ ] B +[*] Testing +[ ] Testing 1 +[ ] Testing 2 +[ ] Testing 3 +[ ] Testing 4 + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Name │ +│ The name of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Name │ +│ The name of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │n▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Name │ +│ The name of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │na▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Name │ +│ The name of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │nam▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Name │ +│ The name of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │name▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Name │ +│ The name of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │namet▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Name │ +│ The name of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │namete▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Name │ +│ The name of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │nametes▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Name │ +│ The name of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │nametest▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +│ The hostname of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +│ The hostname of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │h▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +│ The hostname of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │ho▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +│ The hostname of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │hos▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +│ The hostname of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │host▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +│ The hostname of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │hostt▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +│ The hostname of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │hostte▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +│ The hostname of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │hosttes▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +│ The hostname of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │hosttest▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +│ The description of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +│ The description of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │d▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +│ The description of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │de▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +│ The description of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │des▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +│ The description of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │desc▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +│ The description of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │desct▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +│ The description of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │descte▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +│ The description of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │desctes▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Description │ +│ The description of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │desctest▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + + + +-- BUILD SPEC -- + +{ + "OrgResult": { + "id": "testing" + }, + "SpecResult": { + "data_center": { + "id": "dc_9UVoPiUQoI1cqtR0" + }, + "resources": { + "package": { + "id": "vmpkg_9UVoPiUQoI1cqtRd" + } + }, + "disk_template": { + "id": "disk_9UVoPiUQoI1cqtRd", + "options": [ + { + "key": "install_agent", + "value": "true" + } + ] + }, + "network_interfaces": [ + { + "network": { + "id": "test" + }, + "ip_address_allocations": [ + { + "type": "existing", + "ip_address": { + "id": "ip_UVoPiUQoI1cqtRf5" + } + } + ] + } + ], + "hostname": "hosttest", + "name": "nametest", + "description": "desctest", + "authorized_keys": { + "ssh_keys": [ + "key_PiUQoI1cqt43Dkf" + ] + }, + "tags": [ + "tag_PiUQoI1cqt43gei" + ] + } +} diff --git a/cmd/katapult/testdata/TestVMs_Create/tag_listing_throws_error.golden b/cmd/katapult/testdata/TestVMs_Create/tag_listing_throws_error.golden index a91fc40..6737a0e 100644 --- a/cmd/katapult/testdata/TestVMs_Create/tag_listing_throws_error.golden +++ b/cmd/katapult/testdata/TestVMs_Create/tag_listing_throws_error.golden @@ -18,8 +18,8 @@ -Which VM package would you like? (Press ENTER to make your selection):  / 0 / 0GB -Name CPU Cores RAM  +Which package would you like to deploy the VM in? (Press ENTER to make your selection):  / 0 / 0GB +Name CPU Cores Memory   0 0GB  @@ -27,7 +27,7 @@ -Which distribution would you like? (Press ENTER to make your selection):  +Which distribution would you like to deploy the VM in? (Press ENTER to make your selection):   diff --git a/cmd/katapult/testdata/TestVMs_Create/vm_creator_throws_error.golden b/cmd/katapult/testdata/TestVMs_Create/vm_creator_throws_error.golden index 220e913..4f89b7c 100644 --- a/cmd/katapult/testdata/TestVMs_Create/vm_creator_throws_error.golden +++ b/cmd/katapult/testdata/TestVMs_Create/vm_creator_throws_error.golden @@ -18,8 +18,8 @@ -Which VM package would you like? (Press ENTER to make your selection):  / 0 / 0GB -Name CPU Cores RAM  +Which package would you like to deploy the VM in? (Press ENTER to make your selection):  / 0 / 0GB +Name CPU Cores Memory   0 0GB  @@ -27,7 +27,7 @@ -Which distribution would you like? (Press ENTER to make your selection):  +Which distribution would you like to deploy the VM in? (Press ENTER to make your selection):   @@ -36,7 +36,25 @@ -What would you like the virtual machine to be called? If you want a hostname, what do you want it to be? If you want a description, what do you want it to be? Usage: +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Name │ +│ The name of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Name │ +│ The name of the virtual machine. │ +│ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ +│ │a▓ │ │ +│ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Hostname │ +Usage: vm create [flags] Flags: diff --git a/cmd/katapult/vms.go b/cmd/katapult/vms.go index ce3b6cb..914b0d8 100644 --- a/cmd/katapult/vms.go +++ b/cmd/katapult/vms.go @@ -1,13 +1,13 @@ package main import ( - "bufio" "context" "errors" "fmt" + "os" "strconv" + "strings" - "github.com/buger/goterm" "github.com/krystal/go-katapult" "github.com/krystal/go-katapult/buildspec" "github.com/krystal/go-katapult/core" @@ -21,27 +21,22 @@ type virtualMachinesClient interface { org core.OrganizationRef, opts *core.ListOptions, ) ([]*core.VirtualMachine, *katapult.Response, error) - Delete( ctx context.Context, ref core.VirtualMachineRef, ) (*core.TrashObject, *katapult.Response, error) - Shutdown( ctx context.Context, ref core.VirtualMachineRef, ) (*core.Task, *katapult.Response, error) - Start( ctx context.Context, ref core.VirtualMachineRef, ) (*core.Task, *katapult.Response, error) - Stop( ctx context.Context, ref core.VirtualMachineRef, ) (*core.Task, *katapult.Response, error) - Reset( ctx context.Context, ref core.VirtualMachineRef, @@ -362,6 +357,40 @@ type virtualMachinesBuilderClient interface { ) (*core.VirtualMachineBuild, *katapult.Response, error) } +// a function for splitting strings by comma but disallowing empty strings. +func scnz(heystack string) []string { + s := strings.Split(heystack, ",") + nonBlank := make([]string, 0, len(s)) + for _, v := range s { + v = strings.TrimSpace(v) + if v != "" { + nonBlank = append(nonBlank, v) + } + } + return nonBlank +} + +type envGetter interface { + Get(key string) string +} + +type osGetter struct{} + +func (osGetter) Get(key string) string { + return os.Getenv(key) +} + +type mapGetter struct { + m map[string]string +} + +func (m mapGetter) Get(key string) string { + if m.m == nil { + return "" + } + return m.m[key] +} + //nolint:funlen,gocyclo func virtualMachinesCreateCmd( orgsClient organisationsListClient, @@ -373,14 +402,19 @@ func virtualMachinesCreateCmd( tagsClient tagsClient, vmBuilderClient virtualMachinesBuilderClient, terminal console.TerminalInterface, + envs envGetter, ) *cobra.Command { + // Handle the env getter. + if envs == nil { + envs = osGetter{} + } + + // Defines the command. cmd := &cobra.Command{ Use: "create", Short: "Allows you to create a VM.", Long: "Allows you to create a VM.", RunE: func(cmd *cobra.Command, args []string) error { - // TODO: Accept argument from env var. - // List the organizations. orgs, _, err := orgsClient.List(cmd.Context()) if err != nil { @@ -388,15 +422,38 @@ func virtualMachinesCreateCmd( } // Create a fuzzy searcher for organizations. - orgRows := make([][]string, len(orgs)) - for i, org := range orgs { - orgRows[i] = []string{org.Name, org.SubDomain} + var org *core.Organization + orgNameEnv := envs.Get("KATAPULT_ORG_NAME") + orgSubDomainEnv := envs.Get("KATAPULT_ORG_SUBDOMAIN") + if orgNameEnv == "" && orgSubDomainEnv == "" { + orgRows := make([][]string, len(orgs)) + for i, potentialOrg := range orgs { + orgRows[i] = []string{potentialOrg.Name, potentialOrg.SubDomain} + } + orgArr := console.FuzzyTableSelector( + "Which organization would you like to deploy the VM in?", + []string{"Name", "Subdomain"}, orgRows, cmd.InOrStdin(), terminal) + index := getArrayIndex(orgArr, orgRows) + org = orgs[index] + } else { + subdomain := orgNameEnv == "" + for _, potentialOrg := range orgs { + if subdomain { + if potentialOrg.SubDomain == orgSubDomainEnv { + org = potentialOrg + break + } + } else { + if potentialOrg.Name == orgNameEnv { + org = potentialOrg + break + } + } + } + if org == nil { + return errors.New("the org name/subdomain in your org env variable not attached to your user") + } } - orgArr := console.FuzzyTableSelector( - "Which organization would you like to deploy the VM in?", - []string{"Name", "Subdomain"}, orgRows, cmd.InOrStdin(), terminal) - index := getArrayIndex(orgArr, orgRows) - org := orgs[index] // List the datacenters. dcs, _, err := dcsClient.List(cmd.Context()) @@ -405,33 +462,63 @@ func virtualMachinesCreateCmd( } // Create a fuzzy searcher for data centers. - dcRows := make([][]string, len(dcs)) - for i, dc := range dcs { - dcRows[i] = []string{dc.Name, dc.Country.Name} + dcNameEnv := envs.Get("KATAPULT_DC_NAME") + dcIDEnv := envs.Get("KATAPULT_DC_ID") + var dc *core.DataCenter + if dcNameEnv == "" && dcIDEnv == "" { + dcRows := make([][]string, len(dcs)) + for i, dc := range dcs { + dcRows[i] = []string{dc.Name, dc.Country.Name} + } + dcArr := console.FuzzyTableSelector( + "Which DC would you like to deploy the VM in?", []string{"Name", "Country"}, dcRows, + cmd.InOrStdin(), terminal) + index := getArrayIndex(dcArr, dcRows) + dc = dcs[index] + } else { + for _, potentialDC := range dcs { + if potentialDC.Name == dcNameEnv || potentialDC.ID == dcIDEnv { + dc = potentialDC + break + } + } + if dc == nil { + return errors.New("the dc name/id in your dc env variable not attached to your user") + } } - dcArr := console.FuzzyTableSelector( - "Which DC would you like to deploy the VM in?", []string{"Name", "Country"}, dcRows, - cmd.InOrStdin(), terminal) - index = getArrayIndex(dcArr, dcRows) - dc := dcs[index] - // List the packages. + // Select the package. packages, err := listAllVMPackages(cmd.Context(), vmPackagesClient) if err != nil { return err } - packageRows := make([][]string, len(packages)) - for i, packageItem := range packages { - packageRows[i] = []string{ - packageItem.Name, strconv.Itoa(packageItem.CPUCores), - strconv.Itoa(packageItem.MemoryInGB) + "GB", + vmPackageNameEnv := envs.Get("KATAPULT_PACKAGE_NAME") + vmPackageIDEnv := envs.Get("KATAPULT_PACKAGE_ID") + var packageResult *core.VirtualMachinePackage + if vmPackageNameEnv == "" && vmPackageIDEnv == "" { + packageRows := make([][]string, len(packages)) + for i, packageItem := range packages { + packageRows[i] = []string{ + packageItem.Name, strconv.Itoa(packageItem.CPUCores), + strconv.Itoa(packageItem.MemoryInGB) + "GB", + } + } + packageArr := console.FuzzyTableSelector( + "Which package would you like to deploy the VM in?", + []string{"Name", "CPU Cores", "Memory"}, packageRows, cmd.InOrStdin(), terminal) + index := getArrayIndex(packageArr, packageRows) + packageResult = packages[index] + } else { + for _, potentialPackage := range packages { + if potentialPackage.Name == vmPackageNameEnv || potentialPackage.ID == vmPackageIDEnv { + packageResult = potentialPackage + break + } + } + if packageResult == nil { + return errors.New("the package name/slug in your package env variable not attached to your user") } } - packageArr := console.FuzzyTableSelector( - "Which VM package would you like?", []string{"Name", "CPU Cores", "RAM"}, packageRows, - cmd.InOrStdin(), terminal) - index = getArrayIndex(packageArr, packageRows) - packageResult := packages[index] // Ask about the distribution. distributions, err := listAllDiskTemplates( @@ -439,15 +526,32 @@ func virtualMachinesCreateCmd( if err != nil { return err } - distributionStrs := make([]string, len(distributions)) - for i, distribution := range distributions { - distributionStrs[i] = distribution.Name + distributionNameEnv := envs.Get("KATAPULT_DISTRIBUTION_NAME") + distributionIDEnv := envs.Get("KATAPULT_DISTRIBUTION_ID") + var distribution *core.DiskTemplate + if distributionNameEnv == "" && distributionIDEnv == "" { + distributionStrs := make([]string, len(distributions)) + for i, distributionItem := range distributions { + distributionStrs[i] = distributionItem.Name + } + distributionStr := console.FuzzySelector( + "Which distribution would you like to deploy the VM in?", + distributionStrs, cmd.InOrStdin(), terminal) + index := getStringIndex(distributionStr, distributionStrs) + distribution = distributions[index] + } else { + for _, potentialDistribution := range distributions { + if potentialDistribution.Name == distributionNameEnv || + potentialDistribution.ID == distributionIDEnv { + distribution = potentialDistribution + break + } + } + if distribution == nil { + return errors.New("the distribution name/slug in your distribution env variables not " + + "attached to your user") + } } - distributionStr := console.FuzzySelector( - "Which distribution would you like?", distributionStrs, - cmd.InOrStdin(), terminal) - index = getStringIndex(distributionStr, distributionStrs) - distribution := distributions[index] // Handle networking if there's IP addresses. ips, err := listAllIPAddresses(cmd.Context(), core.OrganizationRef{ID: org.ID}, ipAddressesClient) @@ -456,23 +560,35 @@ func virtualMachinesCreateCmd( } allIps := ips ips = make([]*core.IPAddress, 0) + selectedIps := []*core.IPAddress{} for _, v := range allIps { if v.AllocationID == "" { ips = append(ips, v) } } - selectedIps := []*core.IPAddress{} - if len(ips) != 0 { - ipRows := make([][]string, len(ips)) - for i, ip := range ips { - ipRows[i] = []string{ip.Address, ip.ReverseDNS} + ipsEnv := envs.Get("KATAPULT_IP_ADDRESSES") + if ipsEnv == "" { + if len(ips) != 0 { + ipRows := make([][]string, len(ips)) + for i, ip := range ips { + ipRows[i] = []string{ip.Address, ip.ReverseDNS} + } + selectedIPRows := console.FuzzyTableMultiSelector( + "Please select any IP addresses you wish to add.", + []string{"Address", "Reverse DNS"}, ipRows, cmd.InOrStdin(), terminal) + selectedIps = make([]*core.IPAddress, len(selectedIPRows)) + for i, arr := range selectedIPRows { + selectedIps[i] = ips[getArrayIndex(arr, ipRows)] + } } - selectedIPRows := console.FuzzyTableMultiSelector( - "Please select any IP addresses you wish to add.", - []string{"Address", "Reverse DNS"}, ipRows, cmd.InOrStdin(), terminal) - selectedIps = make([]*core.IPAddress, len(selectedIPRows)) - for i, arr := range selectedIPRows { - selectedIps[i] = ips[getArrayIndex(arr, ipRows)] + } else { + for _, ipStr := range scnz(ipsEnv) { + for _, ip := range ips { + if ip.Address == ipStr { + selectedIps = append(selectedIps, ip) + break + } + } } } @@ -482,17 +598,48 @@ func virtualMachinesCreateCmd( return err } keyIds := []string{} - if len(keys) != 0 { - keyRows := make([][]string, len(keys)) - for i, key := range keys { - keyRows[i] = []string{key.Name, key.Fingerprint} + sshKeyIdsEnvSplit := scnz(envs.Get("KATAPULT_SSH_KEY_IDS")) + sshKeyNamesEnvSplit := scnz(envs.Get("KATAPULT_SSH_KEY_NAMES")) + sshKeyFingerprintsEnvSplit := scnz(envs.Get("KATAPULT_SSH_KEY_FINGERPRINTS")) + if len(sshKeyIdsEnvSplit) == 0 && len(sshKeyNamesEnvSplit) == 0 { + if len(keys) != 0 { + keyRows := make([][]string, len(keys)) + for i, key := range keys { + keyRows[i] = []string{key.Name, key.Fingerprint} + } + selectedKeys := console.FuzzyTableMultiSelector( + "Which organization SSH keys do you wish to add?", []string{"Name", "Fingerprint"}, + keyRows, cmd.InOrStdin(), terminal) + keyIds = make([]string, len(selectedKeys)) + for i, arr := range selectedKeys { + keyIds[i] = keys[getArrayIndex(arr, keyRows)].ID + } } - selectedKeys := console.FuzzyTableMultiSelector( - "Which organization SSH keys do you wish to add?", []string{"Name", "Fingerprint"}, - keyRows, cmd.InOrStdin(), terminal) - keyIds = make([]string, len(selectedKeys)) - for i, arr := range selectedKeys { - keyIds[i] = keys[getArrayIndex(arr, keyRows)].ID + } else { + for _, key := range keys { + for _, x := range sshKeyIdsEnvSplit { + if key.ID == x { + keyIds = append(keyIds, key.ID) + goto endOfKeys + } + } + + for _, x := range sshKeyFingerprintsEnvSplit { + if key.Fingerprint == x { + keyIds = append(keyIds, key.ID) + goto endOfKeys + } + } + + for _, x := range sshKeyNamesEnvSplit { + if key.Name == x { + keyIds = append(keyIds, key.ID) + goto endOfKeys + } + } + + // This is more efficient than using a boolean here, even if it looks a bit weird. + endOfKeys: } } @@ -501,38 +648,126 @@ func virtualMachinesCreateCmd( if err != nil { return err } - tagStrs := make([]string, len(tags)) - for i, v := range tags { - tagStrs[i] = v.Name - } tagIds := []string{} - if len(tags) != 0 { - selectedTags := console.FuzzyMultiSelector( - "Do you wish to add any tags?", tagStrs, cmd.InOrStdin(), terminal) - tagIds = make([]string, len(selectedTags)) - for i, tagName := range selectedTags { - tagIds[i] = tags[getStringIndex(tagName, selectedTags)].ID + tagNamesEnvSplit := scnz(envs.Get("KATAPULT_TAG_NAMES")) + tagIdsEnvSplit := scnz(envs.Get("KATAPULT_TAG_IDS")) + if len(tagNamesEnvSplit) == 0 && len(tagIdsEnvSplit) == 0 { + if len(tags) != 0 { + tagStrs := make([]string, len(tags)) + for i, v := range tags { + tagStrs[i] = v.Name + } + selectedTags := console.FuzzyMultiSelector( + "Do you wish to add any tags?", tagStrs, cmd.InOrStdin(), terminal) + tagIds = make([]string, len(selectedTags)) + for i, tagName := range selectedTags { + for _, v := range tags { + if v.Name == tagName { + tagIds[i] = v.ID + break + } + } + } + } + } else { + // Go through the tag ID's environment variable. + for _, id := range tagIdsEnvSplit { + for _, t := range tags { + if t.ID == id { + // The tag exists. Jump to the end post the error. + goto endOfTagIds + } + } + + // Handle if a tag doesn't exist. + return fmt.Errorf("the tag with the ID %s doesn't exist", id) + + // This is past the error ready for the next iteration. + endOfTagIds: } - } - // Clear the terminal. - goterm.Clear() - goterm.Flush() + // Go through the tag names environment variable. + for _, name := range tagNamesEnvSplit { + for _, t := range tags { + if t.Name == name { + // The tag exists. Jump to the end post the error. + goto endOfTagNames + } + } - // Get the buffered stdin. - bufferedStdin := bufio.NewReader(cmd.InOrStdin()) + // Handle if a tag doesn't exist. + return fmt.Errorf("the tag with the name %s doesn't exist", name) - // Ask for the name. - name := console.Question( - "What would you like the virtual machine to be called?", false, bufferedStdin, cmd.OutOrStdout()) + // This is past the error ready for the next iteration. + endOfTagNames: + } + } - // Ask for the hostname. - hostname := console.Question( - "If you want a hostname, what do you want it to be?", true, bufferedStdin, cmd.OutOrStdout()) + // Figure out all the fields we need. + fields := make([]console.InputField, 0, 3) + type tracked int + const ( + nameTrack = tracked(1 << iota) + hostnameTrack + descriptionTrack + ) + var tracker tracked + + // Check if we need to allow input of the name. + name := envs.Get("KATAPULT_NAME") + if name == "" { + tracker |= nameTrack + fields = append(fields, console.InputField{ + Optional: true, + Name: "Name", + Description: "The name of the virtual machine.", + }) + } - // Ask for the description. - description := console.Question( - "If you want a description, what do you want it to be?", true, bufferedStdin, cmd.OutOrStdout()) + // Check if we need to allow input of the description. + hostname := envs.Get("KATAPULT_HOSTNAME") + if hostname == "" { + tracker |= hostnameTrack + fields = append(fields, console.InputField{ + Optional: true, + Name: "Hostname", + Description: "The hostname of the virtual machine.", + }) + } + + // Check if we need to allow input of the description. + desc := envs.Get("KATAPULT_DESCRIPTION") + if desc == "" { + tracker |= descriptionTrack + fields = append(fields, console.InputField{ + Optional: true, + Name: "Description", + Description: "The description of the virtual machine.", + }) + } + + // Ask for the remainder of the information. + if len(fields) != 0 { + results := console.MultiInput(fields, cmd.InOrStdin(), terminal) + if results == nil { + os.Exit(1) + } + index := 0 + pluck := func() string { + x := results[index] + index++ + return x + } + if tracker&nameTrack != 0 { + name = pluck() + } + if tracker&hostnameTrack != 0 { + hostname = pluck() + } + if tracker&descriptionTrack != 0 { + desc = pluck() + } + } // Build the virtual machine spec. ifaces := make([]*buildspec.NetworkInterface, len(selectedIps)) @@ -560,14 +795,15 @@ func virtualMachinesCreateCmd( }, }}, NetworkInterfaces: ifaces, - Hostname: hostname, Name: name, - Description: description, + Hostname: hostname, + Description: desc, AuthorizedKeys: &buildspec.AuthorizedKeys{SSHKeys: keyIds}, Tags: tagIds, } // ✨ Build the virtual machine. + // TODO: add wait _, _, err = vmBuilderClient.CreateFromSpec(cmd.Context(), core.OrganizationRef{ID: org.ID}, spec) if err != nil { return err @@ -590,7 +826,8 @@ func virtualMachinesCmd(vmClient virtualMachinesClient, sshKeysClient sshKeysListClient, tagsClient tagsClient, vmBuilderClient virtualMachinesBuilderClient, - terminal console.TerminalInterface) *cobra.Command { + terminal console.TerminalInterface, + envs envGetter) *cobra.Command { cmd := &cobra.Command{ Use: "vm", Aliases: []string{"vms", "virtual-machines", "virtual_machines"}, @@ -606,7 +843,7 @@ func virtualMachinesCmd(vmClient virtualMachinesClient, virtualMachinesResetCmd(vmClient), virtualMachinesCreateCmd(orgsClient, dcsClient, vmPackagesClient, diskTemplatesClient, ipAddressesClient, sshKeysClient, - tagsClient, vmBuilderClient, terminal)) + tagsClient, vmBuilderClient, terminal, envs)) return cmd } diff --git a/cmd/katapult/vms_test.go b/cmd/katapult/vms_test.go index e24e5dc..8228e3b 100644 --- a/cmd/katapult/vms_test.go +++ b/cmd/katapult/vms_test.go @@ -297,7 +297,7 @@ func TestVMs_List(t *testing.T) { cmd := virtualMachinesCmd( &vmsClient{organizationIDPages: tt.id, organizationSubdomainPages: tt.subdomains}, nil, nil, nil, nil, nil, nil, - nil, nil, nil) + nil, nil, nil, nil) cmd.SetArgs(tt.args) assertCobraCommand(t, cmd, tt.wantErr, tt.stderr) }) @@ -390,7 +390,7 @@ func TestVMs_Poweroff(t *testing.T) { if tt.poweredDown != nil { client.togglePowerState(tt.poweredDown.key, tt.poweredDown.fqdn) } - cmd := virtualMachinesCmd(client, nil, nil, nil, nil, nil, nil, nil, nil, nil) + cmd := virtualMachinesCmd(client, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil) cmd.SetArgs(tt.args) assertCobraCommand(t, cmd, tt.wantErr, tt.stderr) if tt.validate != nil { @@ -486,7 +486,7 @@ func TestVMs_Stop(t *testing.T) { if tt.poweredDown != nil { client.togglePowerState(tt.poweredDown.key, tt.poweredDown.fqdn) } - cmd := virtualMachinesCmd(client, nil, nil, nil, nil, nil, nil, nil, nil, nil) + cmd := virtualMachinesCmd(client, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil) cmd.SetArgs(tt.args) assertCobraCommand(t, cmd, tt.wantErr, tt.stderr) if tt.validate != nil { @@ -582,7 +582,7 @@ func TestVMs_Start(t *testing.T) { if tt.poweredDown != nil { client.togglePowerState(tt.poweredDown.key, tt.poweredDown.fqdn) } - cmd := virtualMachinesCmd(client, nil, nil, nil, nil, nil, nil, nil, nil, nil) + cmd := virtualMachinesCmd(client, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil) cmd.SetArgs(tt.args) assertCobraCommand(t, cmd, tt.wantErr, tt.stderr) if tt.validate != nil { @@ -678,7 +678,7 @@ func TestVMs_Reset(t *testing.T) { if tt.poweredDown != nil { client.togglePowerState(tt.poweredDown.key, tt.poweredDown.fqdn) } - cmd := virtualMachinesCmd(client, nil, nil, nil, nil, nil, nil, nil, nil, nil) + cmd := virtualMachinesCmd(client, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil) cmd.SetArgs(tt.args) assertCobraCommand(t, cmd, tt.wantErr, tt.stderr) if tt.validate != nil { @@ -893,10 +893,209 @@ func (m *mockVMBuilderClient) CreateFromSpec(_ context.Context, org core.Organiz return nil, nil, nil } +var successPackages = []*core.VirtualMachinePackage{ + {ID: "DO_NOT_PICK_IGNORE_THIS_ONE"}, + { + ID: "vmpkg_9UVoPiUQoI1cqtRd", + Name: "Test", + Permalink: "testing", + CPUCores: 100, + IPv4Addresses: 10, + MemoryInGB: 1000, + StorageInGB: 20, + }, +} + +var successDiskTemplates = []*core.DiskTemplate{ + {ID: "DO_NOT_PICK_IGNORE_THIS_ONE"}, + { + ID: "disk_9UVoPiUQoI1cqtRd", + Name: "Ubuntu 20.04", + Description: "testing", + Permalink: "ubuntu-20-04", + Universal: true, + LatestVersion: &core.DiskTemplateVersion{ + ID: "versopn+9UVoPiUQoI1cqtRd", + Number: 1, + Stable: true, + SizeInGB: 5, + }, + OperatingSystem: &core.OperatingSystem{ + ID: "ubuntu", + Name: "Ubuntu", + }, + }, +} + +var mockIPPages = ipPages{ + { + {ID: "DO_NOT_PICK_IGNORE_THIS_ONE"}, + }, + { + {ID: "DO_NOT_PICK_IGNORE_THIS_ONE_2"}, + { + ID: "ip_UVoPiUQoI1cqtRf5", + Address: "8.8.8.8", + ReverseDNS: "ip-8-8-8-8.test.katapult.cloud", + VIP: true, + Label: "testing", + AddressWithMask: "8.8.8.8", + Network: &core.Network{ + ID: "test", + Name: "testing", + Permalink: "testing-123", + DataCenter: fixtureDataCenters[1], + }, + }, + { + ID: "ip_VVoPiUQoI1cqtRf5", + Address: "1.1.1.1", + ReverseDNS: "ip-1-1-1-1.test.katapult.cloud", + VIP: true, + Label: "testing2", + AddressWithMask: "1.1.1.1", + Network: &core.Network{ + ID: "test", + Name: "testing", + Permalink: "testing-123", + DataCenter: fixtureDataCenters[1], + }, + }, + { + ID: "ip_VVoPiUQoI1cqtRf5", + Address: "1.1.1.2", + ReverseDNS: "ip-1-1-1-2.test.katapult.cloud", + VIP: true, + Label: "testing3", + AddressWithMask: "1.1.1.2", + Network: &core.Network{ + ID: "test", + Name: "testing", + Permalink: "testing-123", + DataCenter: fixtureDataCenters[1], + }, + }, + { + ID: "ip_VVoPiUQoI1cqtRf5", + Address: "1.1.1.3", + ReverseDNS: "ip-1-1-1-3.test.katapult.cloud", + VIP: true, + Label: "testing4", + AddressWithMask: "1.1.1.3", + Network: &core.Network{ + ID: "test", + Name: "testing", + Permalink: "testing-123", + DataCenter: fixtureDataCenters[1], + }, + }, + }, +} + +var successIPPages = map[string]ipPages{ + "testing": mockIPPages, + "loge": mockIPPages, +} + +var mockSSHPages = sshPages{ + { + {ID: "DO_NOT_PICK_IGNORE_THIS_ONE"}, + }, + { + {ID: "DO_NOT_PICK_IGNORE_THIS_ONE_2"}, + { + ID: "key_PiUQoI1cqt43Dkf", + Name: "testing", + Fingerprint: "22:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c", + }, + { + ID: "key_PiUQoI1cqt43Dkg", + Name: "testing1", + Fingerprint: "23:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c", + }, + { + ID: "key_PiUQoI1cqt43Dke", + Name: "testing2", + Fingerprint: "24:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c", + }, + { + ID: "key_PiUQoI1cqt43Dkd", + Name: "testing3", + Fingerprint: "25:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c", + }, + { + ID: "key_PiUQoI1cqt43Dkc", + Name: "testing4", + Fingerprint: "26:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c", + }, + { + ID: "key_PiUQoI1cqt43Dkb", + Name: "testing5", + Fingerprint: "27:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c", + }, + { + ID: "key_PiUQoI1cqt43Dka", + Name: "testing6", + Fingerprint: "28:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c", + }, + }, +} + +var successKeyPages = map[string]sshPages{ + "testing": mockSSHPages, + "loge": mockSSHPages, +} + +var mockTagPages = tagPages{ + { + {Name: "A", ID: "DO_NOT_PICK_IGNORE_THIS_ONE"}, + }, + { + {Name: "B", ID: "DO_NOT_PICK_IGNORE_THIS_ONE_2"}, + { + ID: "tag_PiUQoI1cqt43gei", + Name: "Testing", + Color: "fffff", + CreatedAt: timestamp.Unix(1, 0), + }, + { + ID: "tag_PiUQoI1cqt43gea", + Name: "Testing 1", + Color: "fffff", + CreatedAt: timestamp.Unix(1, 0), + }, + { + ID: "tag_PiUQoI1cqt43geb", + Name: "Testing 2", + Color: "fffff", + CreatedAt: timestamp.Unix(1, 0), + }, + { + ID: "tag_PiUQoI1cqt43gec", + Name: "Testing 3", + Color: "fffff", + CreatedAt: timestamp.Unix(1, 0), + }, + { + ID: "tag_PiUQoI1cqt43ged", + Name: "Testing 4", + Color: "fffff", + CreatedAt: timestamp.Unix(1, 0), + }, + }, +} + +var successTagPages = map[string]tagPages{ + "testing": mockTagPages, + "loge": mockTagPages, +} + func TestVMs_Create(t *testing.T) { tests := []struct { name string + envs map[string]string + orgs []*core.Organization orgsThrows string @@ -926,100 +1125,218 @@ func TestVMs_Create(t *testing.T) { stderr string wantErr string }{ - // Success - - { - name: "success", - orgs: fixtureOrganizations, - dcs: fixtureDataCenters, - packages: []*core.VirtualMachinePackage{ - {ID: "DO_NOT_PICK_IGNORE_THIS_ONE"}, - { - ID: "vmpkg_9UVoPiUQoI1cqtRd", - Name: "Test", - Permalink: "testing", - CPUCores: 100, - IPv4Addresses: 10, - MemoryInGB: 1000, - StorageInGB: 20, - }, + // Successes + { + name: "success with no env", + orgs: fixtureOrganizations, + dcs: fixtureDataCenters, + packages: successPackages, + expectedRef: core.OrganizationRef{ID: "testing"}, + diskTemplates: successDiskTemplates, + ipIDPages: successIPPages, + keysIDPages: successKeyPages, + tagIDPages: successTagPages, + inputs: [][]byte{ + // Organization selection. + keystrokes.DownArrow, keystrokes.Enter, + + // Data center selection. + keystrokes.DownArrow, keystrokes.Enter, + + // Package selection. + keystrokes.DownArrow, keystrokes.Enter, + + // Distro selection. + keystrokes.DownArrow, keystrokes.Enter, + + // IP address selection. + keystrokes.DownArrow, keystrokes.DownArrow, keystrokes.Enter, keystrokes.Escape, + + // Key selection. + keystrokes.DownArrow, keystrokes.DownArrow, keystrokes.Enter, keystrokes.Escape, + + // Tag selection. + keystrokes.DownArrow, keystrokes.DownArrow, keystrokes.Enter, keystrokes.Escape, + + // Name field. + {'n'}, + {'a'}, + {'m'}, + {'e'}, + {'t'}, + {'e'}, + {'s'}, + {'t'}, + keystrokes.DownArrow, + + // Hostname field. + {'h'}, + {'o'}, + {'s'}, + {'t'}, + {'t'}, + {'e'}, + {'s'}, + {'t'}, + keystrokes.DownArrow, + + // Description field. + {'d'}, + {'e'}, + {'s'}, + {'c'}, + {'t'}, + {'e'}, + {'s'}, + {'t'}, + keystrokes.Enter, }, - expectedRef: core.OrganizationRef{ID: "testing"}, - diskTemplates: []*core.DiskTemplate{ - {ID: "DO_NOT_PICK_IGNORE_THIS_ONE"}, - { - ID: "disk_9UVoPiUQoI1cqtRd", - Name: "Ubuntu 20.04", - Description: "testing", - Permalink: "ubuntu-20-04", - Universal: true, - LatestVersion: &core.DiskTemplateVersion{ - ID: "versopn+9UVoPiUQoI1cqtRd", - Number: 1, - Stable: true, - SizeInGB: 5, - }, - OperatingSystem: &core.OperatingSystem{ - ID: "ubuntu", - Name: "Ubuntu", - }, - }, + }, + { + name: "success with org name env", + envs: map[string]string{ + "KATAPULT_ORG_NAME": "Loge Enthusiasts", }, - ipIDPages: map[string]ipPages{ - "testing": { - { - {ID: "DO_NOT_PICK_IGNORE_THIS_ONE"}, - }, - { - {ID: "DO_NOT_PICK_IGNORE_THIS_ONE_2"}, - { - ID: "ip_UVoPiUQoI1cqtRf5", - Address: "8.8.8.8", - ReverseDNS: "ip-8-8-8-8.test.katapult.cloud", - VIP: true, - Label: "testing", - AddressWithMask: "8.8.8.8", - Network: &core.Network{ - ID: "test", - Name: "testing", - Permalink: "testing-123", - DataCenter: fixtureDataCenters[1], - }, - }, - }, - }, + orgs: fixtureOrganizations, + dcs: fixtureDataCenters, + packages: successPackages, + expectedRef: core.OrganizationRef{ID: "loge"}, + diskTemplates: successDiskTemplates, + ipIDPages: successIPPages, + keysIDPages: successKeyPages, + tagIDPages: successTagPages, + inputs: [][]byte{ + // Data center selection. + keystrokes.DownArrow, keystrokes.Enter, + + // Package selection. + keystrokes.DownArrow, keystrokes.Enter, + + // Distro selection. + keystrokes.DownArrow, keystrokes.Enter, + + // IP address selection. + keystrokes.DownArrow, keystrokes.DownArrow, keystrokes.Enter, keystrokes.Escape, + + // Key selection. + keystrokes.DownArrow, keystrokes.DownArrow, keystrokes.Enter, keystrokes.Escape, + + // Tag selection. + keystrokes.DownArrow, keystrokes.DownArrow, keystrokes.Enter, keystrokes.Escape, + + // Name field. + {'n'}, + {'a'}, + {'m'}, + {'e'}, + {'t'}, + {'e'}, + {'s'}, + {'t'}, + keystrokes.DownArrow, + + // Hostname field. + {'h'}, + {'o'}, + {'s'}, + {'t'}, + {'t'}, + {'e'}, + {'s'}, + {'t'}, + keystrokes.DownArrow, + + // Description field. + {'d'}, + {'e'}, + {'s'}, + {'c'}, + {'t'}, + {'e'}, + {'s'}, + {'t'}, + keystrokes.Enter, }, - keysIDPages: map[string]sshPages{ - "testing": { - { - {ID: "DO_NOT_PICK_IGNORE_THIS_ONE"}, - }, - { - {ID: "DO_NOT_PICK_IGNORE_THIS_ONE_2"}, - { - ID: "key_PiUQoI1cqt43Dkf", - Name: "testing", - Fingerprint: "22:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c", - }, - }, - }, + }, + { + name: "success with dc name env", + envs: map[string]string{ + "KATAPULT_DC_NAME": "hello", }, - tagIDPages: map[string]tagPages{ - "testing": { - { - {ID: "DO_NOT_PICK_IGNORE_THIS_ONE"}, - }, - { - {ID: "DO_NOT_PICK_IGNORE_THIS_ONE_2"}, - { - ID: "tag_PiUQoI1cqt43gei", - Name: "Testing", - Color: "fffff", - CreatedAt: timestamp.Unix(1, 0), - }, - }, - }, + orgs: fixtureOrganizations, + dcs: fixtureDataCenters, + packages: successPackages, + expectedRef: core.OrganizationRef{ID: "testing"}, + diskTemplates: successDiskTemplates, + ipIDPages: successIPPages, + keysIDPages: successKeyPages, + tagIDPages: successTagPages, + inputs: [][]byte{ + // Organization selection. + keystrokes.DownArrow, keystrokes.Enter, + + // Package selection. + keystrokes.DownArrow, keystrokes.Enter, + + // Distro selection. + keystrokes.DownArrow, keystrokes.Enter, + + // IP address selection. + keystrokes.DownArrow, keystrokes.DownArrow, keystrokes.Enter, keystrokes.Escape, + + // Key selection. + keystrokes.DownArrow, keystrokes.DownArrow, keystrokes.Enter, keystrokes.Escape, + + // Tag selection. + keystrokes.DownArrow, keystrokes.DownArrow, keystrokes.Enter, keystrokes.Escape, + + // Name field. + {'n'}, + {'a'}, + {'m'}, + {'e'}, + {'t'}, + {'e'}, + {'s'}, + {'t'}, + keystrokes.DownArrow, + + // Hostname field. + {'h'}, + {'o'}, + {'s'}, + {'t'}, + {'t'}, + {'e'}, + {'s'}, + {'t'}, + keystrokes.DownArrow, + + // Description field. + {'d'}, + {'e'}, + {'s'}, + {'c'}, + {'t'}, + {'e'}, + {'s'}, + {'t'}, + keystrokes.Enter, + }, + }, + { + name: "success with package name env", + envs: map[string]string{ + "KATAPULT_PACKAGE_NAME": "Test", }, + orgs: fixtureOrganizations, + dcs: fixtureDataCenters, + packages: successPackages, + expectedRef: core.OrganizationRef{ID: "testing"}, + diskTemplates: successDiskTemplates, + ipIDPages: successIPPages, + keysIDPages: successKeyPages, + tagIDPages: successTagPages, inputs: [][]byte{ // Organization selection. keystrokes.DownArrow, keystrokes.Enter, @@ -1027,10 +1344,73 @@ func TestVMs_Create(t *testing.T) { // Data center selection. keystrokes.DownArrow, keystrokes.Enter, - // Package selection. + // Distro selection. keystrokes.DownArrow, keystrokes.Enter, - // Distro selection. + // IP address selection. + keystrokes.DownArrow, keystrokes.DownArrow, keystrokes.Enter, keystrokes.Escape, + + // Key selection. + keystrokes.DownArrow, keystrokes.DownArrow, keystrokes.Enter, keystrokes.Escape, + + // Tag selection. + keystrokes.DownArrow, keystrokes.DownArrow, keystrokes.Enter, keystrokes.Escape, + + // Name field. + {'n'}, + {'a'}, + {'m'}, + {'e'}, + {'t'}, + {'e'}, + {'s'}, + {'t'}, + keystrokes.DownArrow, + + // Hostname field. + {'h'}, + {'o'}, + {'s'}, + {'t'}, + {'t'}, + {'e'}, + {'s'}, + {'t'}, + keystrokes.DownArrow, + + // Description field. + {'d'}, + {'e'}, + {'s'}, + {'c'}, + {'t'}, + {'e'}, + {'s'}, + {'t'}, + keystrokes.Enter, + }, + }, + { + name: "success with distribution name env", + envs: map[string]string{ + "KATAPULT_DISTRIBUTION_NAME": "Ubuntu 20.04", + }, + orgs: fixtureOrganizations, + dcs: fixtureDataCenters, + packages: successPackages, + expectedRef: core.OrganizationRef{ID: "testing"}, + diskTemplates: successDiskTemplates, + ipIDPages: successIPPages, + keysIDPages: successKeyPages, + tagIDPages: successTagPages, + inputs: [][]byte{ + // Organization selection. + keystrokes.DownArrow, keystrokes.Enter, + + // Data center selection. + keystrokes.DownArrow, keystrokes.Enter, + + // Package selection. keystrokes.DownArrow, keystrokes.Enter, // IP address selection. @@ -1042,7 +1422,7 @@ func TestVMs_Create(t *testing.T) { // Tag selection. keystrokes.DownArrow, keystrokes.DownArrow, keystrokes.Enter, keystrokes.Escape, - // Name prompt. + // Name field. {'n'}, {'a'}, {'m'}, @@ -1051,9 +1431,9 @@ func TestVMs_Create(t *testing.T) { {'e'}, {'s'}, {'t'}, - {'\n'}, + keystrokes.DownArrow, - // Hostname prompt. + // Hostname field. {'h'}, {'o'}, {'s'}, @@ -1062,9 +1442,9 @@ func TestVMs_Create(t *testing.T) { {'e'}, {'s'}, {'t'}, - {'\n'}, + keystrokes.DownArrow, - // Description prompt. + // Description field. {'d'}, {'e'}, {'s'}, @@ -1073,11 +1453,74 @@ func TestVMs_Create(t *testing.T) { {'e'}, {'s'}, {'t'}, - {'\n'}, + keystrokes.Enter, + }, + }, + { + name: "success from full env", + envs: map[string]string{ + "KATAPULT_ORG_SUBDOMAIN": "loge", + "KATAPULT_DC_ID": "dc_9UVoPiUQoI1cqtRd", + "KATAPULT_PACKAGE_ID": "vmpkg_9UVoPiUQoI1cqtRd", + "KATAPULT_DISTRIBUTION_ID": "Ubuntu-20-04", + "KATAPULT_IP_ADDRESSES": "1.1.1.1,1.1.1.2,1.1.1.3", + "KATAPULT_SSH_KEY_IDS": "key_PiUQoI1cqt43Dkc,key_PiUQoI1cqt43Dkd", + "KATAPULT_SSH_KEY_NAMES": "testing,testing1", + "KATAPULT_SSH_KEY_FINGERPRINTS": "28:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c," + + "27:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c", + "KATAPULT_TAG_NAMES": "Testing 2,Testing 3", + "KATAPULT_TAG_IDS": "tag_PiUQoI1cqt43gea,tag_PiUQoI1cqt43geb", + "KATAPULT_NAME": "test", + "KATAPULT_HOSTNAME": "testing", + "KATAPULT_DESCRIPTION": "123", + }, + orgs: fixtureOrganizations, + dcs: fixtureDataCenters, + packages: successPackages, + expectedRef: core.OrganizationRef{ID: "loge"}, + diskTemplates: successDiskTemplates, + ipIDPages: successIPPages, + keysIDPages: successKeyPages, + tagIDPages: successTagPages, + }, + { + name: "success from full minus hostname env", + envs: map[string]string{ + "KATAPULT_ORG_SUBDOMAIN": "loge", + "KATAPULT_DC_ID": "dc_9UVoPiUQoI1cqtRd", + "KATAPULT_PACKAGE_ID": "vmpkg_9UVoPiUQoI1cqtRd", + "KATAPULT_DISTRIBUTION_ID": "Ubuntu-20-04", + "KATAPULT_IP_ADDRESSES": "1.1.1.1,1.1.1.2,1.1.1.3", + "KATAPULT_SSH_KEY_IDS": "key_PiUQoI1cqt43Dkc,key_PiUQoI1cqt43Dkd", + "KATAPULT_SSH_KEY_NAMES": "testing,testing1", + "KATAPULT_SSH_KEY_FINGERPRINTS": "28:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c," + + "27:57:25:0d:8a:ad:00:d0:91:a2:23:7d:7b:70:39:0c", + "KATAPULT_TAG_NAMES": "Testing 2,Testing 3", + "KATAPULT_TAG_IDS": "tag_PiUQoI1cqt43gea,tag_PiUQoI1cqt43geb", + "KATAPULT_NAME": "test", + "KATAPULT_DESCRIPTION": "123", + }, + orgs: fixtureOrganizations, + dcs: fixtureDataCenters, + packages: successPackages, + expectedRef: core.OrganizationRef{ID: "loge"}, + diskTemplates: successDiskTemplates, + ipIDPages: successIPPages, + keysIDPages: successKeyPages, + tagIDPages: successTagPages, + inputs: [][]byte{ + {'t'}, + {'e'}, + {'s'}, + {'t'}, + {'i'}, + {'n'}, + {'g'}, + keystrokes.Enter, }, }, - // Error throwing + // Client error throwing { name: "orgs throws error", @@ -1159,16 +1602,103 @@ func TestVMs_Create(t *testing.T) { keystrokes.Enter, keystrokes.Enter, keystrokes.Enter, keystrokes.Enter, {'a'}, - {'\n'}, - {'\n'}, - {'\n'}, + keystrokes.Enter, + }, + }, + + // Env validation handling + + { + name: "org name error", + orgs: []*core.Organization{}, + envs: map[string]string{"KATAPULT_ORG_NAME": "test"}, + wantErr: "the org name/subdomain in your org env variable not attached to your user", + }, + { + name: "org subdomain error", + orgs: []*core.Organization{}, + envs: map[string]string{"KATAPULT_ORG_SUBDOMAIN": "test"}, + wantErr: "the org name/subdomain in your org env variable not attached to your user", + }, + { + name: "dc id error", + orgs: []*core.Organization{{Name: "test", SubDomain: "testing", ID: "org"}}, + dcs: []*core.DataCenter{}, + envs: map[string]string{ + "KATAPULT_ORG_NAME": "test", + "KATAPULT_DC_ID": "dc", + }, + wantErr: "the dc name/id in your dc env variable not attached to your user", + }, + { + name: "dc name error", + orgs: []*core.Organization{{Name: "test", SubDomain: "testing", ID: "org"}}, + dcs: []*core.DataCenter{}, + envs: map[string]string{ + "KATAPULT_ORG_NAME": "test", + "KATAPULT_DC_NAME": "dc", + }, + wantErr: "the dc name/id in your dc env variable not attached to your user", + }, + { + name: "package id error", + orgs: []*core.Organization{{Name: "test", SubDomain: "testing", ID: "org"}}, + dcs: []*core.DataCenter{{Name: "dc", ID: "dc"}}, + packages: []*core.VirtualMachinePackage{}, + envs: map[string]string{ + "KATAPULT_ORG_NAME": "test", + "KATAPULT_DC_NAME": "dc", + "KATAPULT_PACKAGE_ID": "package", + }, + wantErr: "the package name/slug in your package env variable not attached to your user", + }, + { + name: "package name error", + orgs: []*core.Organization{{Name: "test", SubDomain: "testing", ID: "org"}}, + dcs: []*core.DataCenter{{Name: "dc", ID: "dc"}}, + packages: []*core.VirtualMachinePackage{}, + envs: map[string]string{ + "KATAPULT_ORG_NAME": "test", + "KATAPULT_DC_NAME": "dc", + "KATAPULT_PACKAGE_NAME": "package", + }, + wantErr: "the package name/slug in your package env variable not attached to your user", + }, + { + name: "distro name error", + orgs: []*core.Organization{{Name: "test", SubDomain: "testing", ID: "org"}}, + dcs: []*core.DataCenter{{Name: "dc", ID: "dc"}}, + packages: []*core.VirtualMachinePackage{{ID: "package"}}, + diskTemplates: []*core.DiskTemplate{}, + expectedRef: core.OrganizationRef{ID: "org"}, + envs: map[string]string{ + "KATAPULT_ORG_NAME": "test", + "KATAPULT_DC_NAME": "dc", + "KATAPULT_PACKAGE_ID": "package", + "KATAPULT_DISTRIBUTION_ID": "testing", + }, + wantErr: "the distribution name/slug in your distribution env variables not attached to your user", + }, + { + name: "distro id error", + orgs: []*core.Organization{{Name: "test", SubDomain: "testing", ID: "org"}}, + dcs: []*core.DataCenter{{Name: "dc", ID: "dc"}}, + packages: []*core.VirtualMachinePackage{{ID: "package"}}, + diskTemplates: []*core.DiskTemplate{}, + expectedRef: core.OrganizationRef{ID: "org"}, + envs: map[string]string{ + "KATAPULT_ORG_NAME": "test", + "KATAPULT_DC_NAME": "dc", + "KATAPULT_PACKAGE_ID": "package", + "KATAPULT_DISTRIBUTION_NAME": "testing", }, + wantErr: "the distribution name/slug in your distribution env variables not attached to your user", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Defines stdin. - stdin := &console.StdinDripFeeder{Inputs: tt.inputs} + stdin := &console.StdinDripFeeder{T: t, Inputs: tt.inputs} // Defines the mock terminal. mockTerminal := &console.MockTerminal{} @@ -1210,7 +1740,8 @@ func TestVMs_Create(t *testing.T) { // Create the command. cmd := virtualMachinesCmd( nil, orgsClient, dcsClient, vmPackagesClient, diskTemplatesClient, - ipAddressesClient, sshKeysClient, tags, vmBuilderClient, mockTerminal) + ipAddressesClient, sshKeysClient, tags, vmBuilderClient, mockTerminal, + mapGetter{m: tt.envs}) cmd.SetIn(stdin) cmd.SetArgs([]string{"create"}) stdout := assertCobraCommandReturnStdout(t, cmd, tt.wantErr, tt.stderr) @@ -1228,7 +1759,6 @@ func TestVMs_Create(t *testing.T) { } if golden.Update() { golden.Set(t, buf.Bytes()) - return } assert.Equal(t, string(golden.Get(t)), buf.String()) }) diff --git a/internal/keystrokes/keystrokes.go b/internal/keystrokes/keystrokes.go index 07ef53c..1bf5d92 100644 --- a/internal/keystrokes/keystrokes.go +++ b/internal/keystrokes/keystrokes.go @@ -10,6 +10,12 @@ var ( // UpArrow is used to define the up arrow action. UpArrow = []byte{27, 91, 65} + // LeftArrow is used to define the left arrow action. + LeftArrow = []byte{27, 91, 68} + + // RightArrow is used to define the right arrow action. + RightArrow = []byte{27, 91, 67} + // Enter is used to define an enter action. Enter = []byte{13}