mirror of
https://github.com/kairos-io/kairos-agent.git
synced 2025-08-20 09:14:22 +00:00
208 lines
5.3 KiB
Go
208 lines
5.3 KiB
Go
package agent
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
|
|
tea "github.com/charmbracelet/bubbletea"
|
|
"github.com/charmbracelet/lipgloss"
|
|
"github.com/kairos-io/kairos-agent/v2/internal/bus"
|
|
sdk "github.com/kairos-io/kairos-sdk/bus"
|
|
"github.com/mudler/go-pluggable"
|
|
)
|
|
|
|
// Customization Page
|
|
|
|
type YAMLPrompt struct {
|
|
YAMLSection string
|
|
Bool bool
|
|
Prompt string
|
|
Default string
|
|
AskFirst bool
|
|
AskPrompt string
|
|
IfEmpty string
|
|
PlaceHolder string
|
|
}
|
|
|
|
type EventPayload struct {
|
|
Config string `json:"config"`
|
|
}
|
|
|
|
// Discover and run plugins for customization
|
|
func runCustomizationPlugins() ([]YAMLPrompt, error) {
|
|
bus.Manager.Initialize()
|
|
var r []YAMLPrompt
|
|
|
|
bus.Manager.Response(sdk.EventInteractiveInstall, func(p *pluggable.Plugin, resp *pluggable.EventResponse) {
|
|
err := json.Unmarshal([]byte(resp.Data), &r)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
}
|
|
})
|
|
|
|
_, err := bus.Manager.Publish(sdk.EventInteractiveInstall, EventPayload{})
|
|
if err != nil {
|
|
return r, err
|
|
}
|
|
|
|
return r, nil
|
|
|
|
}
|
|
|
|
func newCustomizationPage() *customizationPage {
|
|
return &customizationPage{
|
|
options: []string{
|
|
"User & Password",
|
|
"SSH Keys",
|
|
},
|
|
|
|
cursor: 0,
|
|
cursorWithIds: map[int]string{
|
|
0: "user_password",
|
|
1: "ssh_keys",
|
|
},
|
|
}
|
|
}
|
|
|
|
func checkPageExists(pageID string, options map[int]string) bool {
|
|
for _, opt := range options {
|
|
if strings.Contains(opt, pageID) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
type customizationPage struct {
|
|
cursor int
|
|
options []string
|
|
cursorWithIds map[int]string
|
|
}
|
|
|
|
func (p *customizationPage) Title() string {
|
|
return "Customization"
|
|
}
|
|
|
|
func (p *customizationPage) Help() string {
|
|
return genericNavigationHelp
|
|
}
|
|
|
|
func (p *customizationPage) Init() tea.Cmd {
|
|
mainModel.log.Debugf("Running customization plugins...")
|
|
yaML, err := runCustomizationPlugins()
|
|
if err != nil {
|
|
mainModel.log.Debugf("Error running customization plugins: %v", err)
|
|
return nil
|
|
}
|
|
if len(yaML) > 0 {
|
|
startIdx := len(p.options)
|
|
for i, prompt := range yaML {
|
|
// Check if its already added to the options!
|
|
if checkPageExists(idFromSection(prompt), p.cursorWithIds) {
|
|
mainModel.log.Debugf("Customization page for %s already exists, skipping", prompt.YAMLSection)
|
|
continue
|
|
}
|
|
optIdx := startIdx + i
|
|
if prompt.Bool == false {
|
|
mainModel.log.Debugf("Adding customization option for %s", prompt.YAMLSection)
|
|
p.options = append(p.options, fmt.Sprintf("Configure %s", prompt.YAMLSection))
|
|
pageID := idFromSection(prompt)
|
|
p.cursorWithIds[optIdx] = pageID
|
|
newPage := newGenericQuestionPage(prompt)
|
|
mainModel.pages = append(mainModel.pages, newPage)
|
|
} else {
|
|
mainModel.log.Debugf("Adding customization option(bool) for %s", prompt.YAMLSection)
|
|
p.options = append(p.options, fmt.Sprintf("Configure %s", prompt.YAMLSection))
|
|
pageID := idFromSection(prompt)
|
|
p.cursorWithIds[optIdx] = pageID
|
|
newPage := newGenericBoolPage(prompt)
|
|
mainModel.pages = append(mainModel.pages, newPage)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Now add the finish and install options to the bottom of the list
|
|
if !checkPageExists("summary", p.cursorWithIds) {
|
|
p.options = append(p.options, "Finish Customization and start Installation")
|
|
p.cursorWithIds[len(p.cursorWithIds)] = "summary"
|
|
}
|
|
|
|
mainModel.log.Debugf("Customization options loaded: %v", p.cursorWithIds)
|
|
return nil
|
|
}
|
|
|
|
func (p *customizationPage) Update(msg tea.Msg) (Page, tea.Cmd) {
|
|
switch msg := msg.(type) {
|
|
case tea.KeyMsg:
|
|
switch msg.String() {
|
|
case "up", "k":
|
|
if p.cursor > 0 {
|
|
p.cursor--
|
|
}
|
|
case "down", "j":
|
|
if p.cursor < len(p.options)-1 {
|
|
p.cursor++
|
|
}
|
|
case "enter":
|
|
if pageID, ok := p.cursorWithIds[p.cursor]; ok {
|
|
return p, func() tea.Msg { return GoToPageMsg{PageID: pageID} }
|
|
}
|
|
}
|
|
}
|
|
return p, nil
|
|
}
|
|
|
|
func (p *customizationPage) View() string {
|
|
s := "Customization Options\n\n"
|
|
s += "Configure additional settings:\n\n"
|
|
|
|
for i, option := range p.options {
|
|
cursor := " "
|
|
if p.cursor == i {
|
|
cursor = lipgloss.NewStyle().Foreground(kairosAccent).Render(">")
|
|
}
|
|
tick := ""
|
|
pageID, ok := p.cursorWithIds[i]
|
|
if ok && p.isConfigured(pageID) {
|
|
tick = lipgloss.NewStyle().Foreground(kairosAccent).Render(checkMark)
|
|
}
|
|
s += fmt.Sprintf("%s %s %s\n", cursor, option, tick)
|
|
}
|
|
|
|
return s
|
|
}
|
|
|
|
// Helper methods to check configuration
|
|
func (p *customizationPage) isUserConfigured() bool {
|
|
return mainModel.username != "" && mainModel.password != ""
|
|
}
|
|
|
|
func (p *customizationPage) isSSHConfigured() bool {
|
|
return len(mainModel.sshKeys) > 0
|
|
}
|
|
|
|
func (p *customizationPage) ID() string { return "customization" }
|
|
|
|
// isConfigured checks if a given pageID is configured, supporting both static and dynamic fields
|
|
func (p *customizationPage) isConfigured(pageID string) bool {
|
|
// Hardcoded checks for static fields
|
|
if pageID == "user_password" {
|
|
return p.isUserConfigured()
|
|
}
|
|
if pageID == "ssh_keys" {
|
|
return p.isSSHConfigured()
|
|
}
|
|
// Try to find a page with this ID and call Configured() if available
|
|
for _, page := range mainModel.pages {
|
|
if idProvider, ok := page.(interface{ ID() string }); ok && idProvider.ID() == pageID {
|
|
// We found the page with the given ID, check if it has a Configured method
|
|
if configuredProvider, ok := page.(interface{ Configured() bool }); ok {
|
|
// Call the Configured method to check if it's configured
|
|
return configuredProvider.Configured()
|
|
}
|
|
}
|
|
}
|
|
return false
|
|
}
|