mirror of
				https://github.com/siderolabs/talos.git
				synced 2025-11-04 10:21:13 +01:00 
			
		
		
		
	If the dashboard is run without the "Config URL" screen, do not initialize it, and do not probe the kernel args for the code parameter. Refactor the dashboard to do not construct the unused screens at all. Closes siderolabs/talos#7300. Signed-off-by: Utku Ozdemir <utku.ozdemir@siderolabs.com>
		
			
				
	
	
		
			212 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			212 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// This Source Code Form is subject to the terms of the Mozilla Public
 | 
						|
// License, v. 2.0. If a copy of the MPL was not distributed with this
 | 
						|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
 | 
						|
 | 
						|
package dashboard
 | 
						|
 | 
						|
import (
 | 
						|
	"context"
 | 
						|
	"fmt"
 | 
						|
	"strings"
 | 
						|
 | 
						|
	"github.com/rivo/tview"
 | 
						|
	"github.com/siderolabs/go-procfs/procfs"
 | 
						|
	"google.golang.org/grpc/codes"
 | 
						|
	"google.golang.org/grpc/status"
 | 
						|
 | 
						|
	"github.com/siderolabs/talos/internal/pkg/dashboard/resourcedata"
 | 
						|
	"github.com/siderolabs/talos/internal/pkg/meta"
 | 
						|
	"github.com/siderolabs/talos/pkg/machinery/constants"
 | 
						|
	runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime"
 | 
						|
)
 | 
						|
 | 
						|
const unset = "[gray](unset)[-]"
 | 
						|
 | 
						|
type configURLData struct {
 | 
						|
	existingCode string
 | 
						|
}
 | 
						|
 | 
						|
// ConfigURLGrid represents the config URL grid.
 | 
						|
type ConfigURLGrid struct {
 | 
						|
	tview.Grid
 | 
						|
 | 
						|
	dashboard *Dashboard
 | 
						|
 | 
						|
	form *tview.Form
 | 
						|
 | 
						|
	template     *tview.TextView
 | 
						|
	existingCode *tview.TextView
 | 
						|
	newCodeField *tview.InputField
 | 
						|
	infoView     *tview.TextView
 | 
						|
 | 
						|
	selectedNode string
 | 
						|
	nodeMap      map[string]*configURLData
 | 
						|
}
 | 
						|
 | 
						|
// NewConfigURLGrid returns a new config URL grid.
 | 
						|
func NewConfigURLGrid(ctx context.Context, dashboard *Dashboard) *ConfigURLGrid {
 | 
						|
	grid := &ConfigURLGrid{
 | 
						|
		Grid:         *tview.NewGrid(),
 | 
						|
		dashboard:    dashboard,
 | 
						|
		form:         tview.NewForm(),
 | 
						|
		template:     tview.NewTextView().SetDynamicColors(true).SetLabel("Template").SetScrollable(false),
 | 
						|
		existingCode: tview.NewTextView().SetDynamicColors(true).SetLabel("Existing Code").SetText(unset).SetSize(1, 0).SetScrollable(false),
 | 
						|
		newCodeField: tview.NewInputField().SetLabel("New Code"),
 | 
						|
		infoView:     tview.NewTextView().SetDynamicColors(true).SetSize(2, 0).SetScrollable(false),
 | 
						|
 | 
						|
		nodeMap: make(map[string]*configURLData),
 | 
						|
	}
 | 
						|
 | 
						|
	grid.template.SetText(grid.readTemplateFromKernelArgs())
 | 
						|
 | 
						|
	grid.SetRows(-1, 18, -1).SetColumns(-1, 72, -1)
 | 
						|
 | 
						|
	grid.form.SetBorder(true)
 | 
						|
 | 
						|
	grid.form.AddFormItem(grid.template)
 | 
						|
	grid.form.AddFormItem(grid.existingCode)
 | 
						|
	grid.form.AddFormItem(grid.newCodeField)
 | 
						|
	grid.form.AddButton("Save", func() {
 | 
						|
		ctx = nodeContext(ctx, grid.selectedNode)
 | 
						|
 | 
						|
		value := grid.newCodeField.GetText()
 | 
						|
 | 
						|
		if value == "" {
 | 
						|
			grid.infoView.SetText("[red]Error: No code entered[-]")
 | 
						|
 | 
						|
			return
 | 
						|
		}
 | 
						|
 | 
						|
		err := dashboard.cli.MetaWrite(ctx, meta.DownloadURLCode, []byte(value))
 | 
						|
		if err != nil {
 | 
						|
			grid.infoView.SetText(fmt.Sprintf("[red]Error: %v[-]", err))
 | 
						|
 | 
						|
			return
 | 
						|
		}
 | 
						|
 | 
						|
		grid.clearForm()
 | 
						|
		grid.dashboard.selectScreen(ScreenSummary)
 | 
						|
	})
 | 
						|
	grid.form.AddButton("Delete", func() {
 | 
						|
		ctx = nodeContext(ctx, grid.selectedNode)
 | 
						|
 | 
						|
		err := dashboard.cli.MetaDelete(ctx, meta.DownloadURLCode)
 | 
						|
		if err != nil {
 | 
						|
			if status.Code(err) == codes.NotFound {
 | 
						|
				grid.clearForm()
 | 
						|
				grid.infoView.SetText("[green]Already deleted[-]")
 | 
						|
 | 
						|
				return
 | 
						|
			}
 | 
						|
 | 
						|
			grid.infoView.SetText(fmt.Sprintf("[red]Error: %v[-]", err))
 | 
						|
 | 
						|
			return
 | 
						|
		}
 | 
						|
 | 
						|
		grid.clearForm()
 | 
						|
		grid.infoView.SetText("[green]Deleted successfully[-]")
 | 
						|
	})
 | 
						|
 | 
						|
	grid.form.AddFormItem(grid.infoView)
 | 
						|
 | 
						|
	grid.AddItem(tview.NewBox(), 0, 0, 1, 3, 0, 0, false)
 | 
						|
	grid.AddItem(tview.NewBox(), 1, 0, 1, 1, 0, 0, false)
 | 
						|
	grid.AddItem(grid.form, 1, 1, 1, 1, 0, 0, false)
 | 
						|
	grid.AddItem(tview.NewBox(), 1, 2, 1, 1, 0, 0, false)
 | 
						|
	grid.AddItem(tview.NewBox(), 2, 0, 1, 3, 0, 0, false)
 | 
						|
 | 
						|
	return grid
 | 
						|
}
 | 
						|
 | 
						|
func (widget *ConfigURLGrid) readTemplateFromKernelArgs() (val string) {
 | 
						|
	defer func() { // catch potential panic from procfs.ProcCmdline()
 | 
						|
		if r := recover(); r != nil {
 | 
						|
			val = "error reading kernel args"
 | 
						|
		}
 | 
						|
	}()
 | 
						|
 | 
						|
	option := procfs.ProcCmdline().Get(constants.KernelParamConfig).First()
 | 
						|
	if option == nil {
 | 
						|
		return unset
 | 
						|
	}
 | 
						|
 | 
						|
	codeVar := fmt.Sprintf("${%s}", constants.CodeKey)
 | 
						|
 | 
						|
	return strings.ReplaceAll(tview.Escape(*option), codeVar, fmt.Sprintf("[green]%s[-]", codeVar))
 | 
						|
}
 | 
						|
 | 
						|
// OnScreenSelect implements the screenSelectListener interface.
 | 
						|
func (widget *ConfigURLGrid) onScreenSelect(active bool) {
 | 
						|
	if active {
 | 
						|
		widget.dashboard.app.SetFocus(widget.form)
 | 
						|
	} else {
 | 
						|
		widget.clearForm()
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// OnNodeSelect implements the NodeSelectListener interface.
 | 
						|
func (widget *ConfigURLGrid) OnNodeSelect(node string) {
 | 
						|
	if node != widget.selectedNode {
 | 
						|
		widget.selectedNode = node
 | 
						|
 | 
						|
		widget.clearForm()
 | 
						|
		widget.redraw()
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// OnResourceDataChange implements the ResourceDataListener interface.
 | 
						|
func (widget *ConfigURLGrid) OnResourceDataChange(data resourcedata.Data) {
 | 
						|
	widget.updateNodeData(data)
 | 
						|
 | 
						|
	if data.Node == widget.selectedNode {
 | 
						|
		widget.redraw()
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (widget *ConfigURLGrid) updateNodeData(data resourcedata.Data) {
 | 
						|
	nodeData := widget.getOrCreateNodeData(data.Node)
 | 
						|
 | 
						|
	//nolint:gocritic
 | 
						|
	switch res := data.Resource.(type) {
 | 
						|
	case *runtimeres.MetaKey:
 | 
						|
		if res.Metadata().ID() == runtimeres.MetaKeyTagToID(meta.DownloadURLCode) {
 | 
						|
			if data.Deleted {
 | 
						|
				nodeData.existingCode = unset
 | 
						|
			} else {
 | 
						|
				val := res.TypedSpec().Value
 | 
						|
				if val == "" {
 | 
						|
					val = "(empty)"
 | 
						|
				}
 | 
						|
 | 
						|
				nodeData.existingCode = fmt.Sprintf("[blue]%s[-]", val)
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (widget *ConfigURLGrid) redraw() {
 | 
						|
	data := widget.getOrCreateNodeData(widget.selectedNode)
 | 
						|
 | 
						|
	widget.existingCode.SetText(data.existingCode)
 | 
						|
}
 | 
						|
 | 
						|
func (widget *ConfigURLGrid) getOrCreateNodeData(node string) *configURLData {
 | 
						|
	nodeData, ok := widget.nodeMap[node]
 | 
						|
	if !ok {
 | 
						|
		nodeData = &configURLData{
 | 
						|
			existingCode: unset,
 | 
						|
		}
 | 
						|
 | 
						|
		widget.nodeMap[node] = nodeData
 | 
						|
	}
 | 
						|
 | 
						|
	return nodeData
 | 
						|
}
 | 
						|
 | 
						|
func (widget *ConfigURLGrid) clearForm() {
 | 
						|
	widget.form.SetFocus(0)
 | 
						|
	widget.infoView.SetText("")
 | 
						|
	widget.newCodeField.SetText("")
 | 
						|
}
 |