ISOs need version AND schema

This commit is contained in:
2025-11-08 22:23:26 +00:00
parent b330b2aea7
commit c623843d53
16 changed files with 170 additions and 174 deletions

View File

@@ -1,5 +1,11 @@
# Building the Wild Cloud Central API
These are instructions for working with the Wild Cloud Central API (Wild API). Wild API is a web service that runs on Wild Central. Users can interact with the API directly, through the Wild CLI, or through the Wild Web App. The CLI and Web App depend on the API extensively.
Whenever changes are made to the API, it is important that the CLI and API are updated appropriately.
Use tests on the API extensively to keep the API functioning well for all clients, but don't duplicate test layers. If something is tested in one place, it doesn't need to be tested again in another place. Prefer unit tests. Tests should be run with `make test` after all API changes. If a bug was found by any means other than tests, it is a signal that a test should have been present to catch it earlier, so make sure a new test catches that bug before fixing it.
## Dev Environment Requirements
- Go 1.21+

6
go.mod
View File

@@ -7,3 +7,9 @@ require (
github.com/rs/cors v1.11.1
gopkg.in/yaml.v3 v3.0.1
)
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/stretchr/testify v1.11.1 // indirect
)

6
go.sum
View File

@@ -1,7 +1,13 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA=
github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

View File

@@ -98,13 +98,13 @@ func (api *API) RegisterRoutes(r *mux.Router) {
r.HandleFunc("/api/v1/instances/{name}/nodes/{node}/apply", api.NodeApply).Methods("POST")
r.HandleFunc("/api/v1/instances/{name}/nodes/{node}", api.NodeDelete).Methods("DELETE")
// Asset management
r.HandleFunc("/api/v1/assets", api.AssetsListSchematics).Methods("GET")
r.HandleFunc("/api/v1/assets/{schematicId}", api.AssetsGetSchematic).Methods("GET")
r.HandleFunc("/api/v1/assets/{schematicId}/download", api.AssetsDownload).Methods("POST")
r.HandleFunc("/api/v1/assets/{schematicId}/pxe/{assetType}", api.AssetsServePXE).Methods("GET")
r.HandleFunc("/api/v1/assets/{schematicId}/status", api.AssetsGetStatus).Methods("GET")
r.HandleFunc("/api/v1/assets/{schematicId}", api.AssetsDeleteSchematic).Methods("DELETE")
// PXE Asset management (schematic@version composite key)
r.HandleFunc("/api/v1/pxe/assets", api.AssetsList).Methods("GET")
r.HandleFunc("/api/v1/pxe/assets/{schematicId}/{version}", api.AssetsGet).Methods("GET")
r.HandleFunc("/api/v1/pxe/assets/{schematicId}/{version}/download", api.AssetsDownload).Methods("POST")
r.HandleFunc("/api/v1/pxe/assets/{schematicId}/{version}", api.AssetsDelete).Methods("DELETE")
r.HandleFunc("/api/v1/pxe/assets/{schematicId}/{version}/pxe/{assetType}", api.AssetsServePXE).Methods("GET")
r.HandleFunc("/api/v1/pxe/assets/{schematicId}/{version}/status", api.AssetsGetStatus).Methods("GET")
// Instance-schematic relationship
r.HandleFunc("/api/v1/instances/{name}/schematic", api.SchematicGetInstanceSchematic).Methods("GET")

View File

@@ -11,45 +11,46 @@ import (
"github.com/wild-cloud/wild-central/daemon/internal/assets"
)
// AssetsListSchematics lists all available schematics
func (api *API) AssetsListSchematics(w http.ResponseWriter, r *http.Request) {
// AssetsList lists all available assets (schematic@version combinations)
func (api *API) AssetsList(w http.ResponseWriter, r *http.Request) {
assetsMgr := assets.NewManager(api.dataDir)
schematics, err := assetsMgr.ListSchematics()
assetList, err := assetsMgr.ListAssets()
if err != nil {
respondError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to list schematics: %v", err))
respondError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to list assets: %v", err))
return
}
respondJSON(w, http.StatusOK, map[string]interface{}{
"schematics": schematics,
"assets": assetList,
})
}
// AssetsGetSchematic returns details for a specific schematic
func (api *API) AssetsGetSchematic(w http.ResponseWriter, r *http.Request) {
// AssetsGet returns details for a specific asset (schematic@version)
func (api *API) AssetsGet(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
schematicID := vars["schematicId"]
version := vars["version"]
assetsMgr := assets.NewManager(api.dataDir)
schematic, err := assetsMgr.GetSchematic(schematicID)
asset, err := assetsMgr.GetAsset(schematicID, version)
if err != nil {
respondError(w, http.StatusNotFound, fmt.Sprintf("Schematic not found: %v", err))
respondError(w, http.StatusNotFound, fmt.Sprintf("Asset not found: %v", err))
return
}
respondJSON(w, http.StatusOK, schematic)
respondJSON(w, http.StatusOK, asset)
}
// AssetsDownload downloads assets for a schematic
// AssetsDownload downloads assets for a schematic@version
func (api *API) AssetsDownload(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
schematicID := vars["schematicId"]
version := vars["version"]
// Parse request body
var req struct {
Version string `json:"version"`
Platform string `json:"platform,omitempty"`
AssetTypes []string `json:"asset_types,omitempty"`
}
@@ -59,11 +60,6 @@ func (api *API) AssetsDownload(w http.ResponseWriter, r *http.Request) {
return
}
if req.Version == "" {
respondError(w, http.StatusBadRequest, "version is required")
return
}
// Default platform to amd64 if not specified
if req.Platform == "" {
req.Platform = "amd64"
@@ -71,7 +67,7 @@ func (api *API) AssetsDownload(w http.ResponseWriter, r *http.Request) {
// Download assets
assetsMgr := assets.NewManager(api.dataDir)
if err := assetsMgr.DownloadAssets(schematicID, req.Version, req.Platform, req.AssetTypes); err != nil {
if err := assetsMgr.DownloadAssets(schematicID, version, req.Platform, req.AssetTypes); err != nil {
respondError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to download assets: %v", err))
return
}
@@ -79,7 +75,7 @@ func (api *API) AssetsDownload(w http.ResponseWriter, r *http.Request) {
respondJSON(w, http.StatusOK, map[string]interface{}{
"message": "Assets downloaded successfully",
"schematic_id": schematicID,
"version": req.Version,
"version": version,
"platform": req.Platform,
})
}
@@ -88,12 +84,13 @@ func (api *API) AssetsDownload(w http.ResponseWriter, r *http.Request) {
func (api *API) AssetsServePXE(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
schematicID := vars["schematicId"]
version := vars["version"]
assetType := vars["assetType"]
assetsMgr := assets.NewManager(api.dataDir)
// Get asset path
assetPath, err := assetsMgr.GetAssetPath(schematicID, assetType)
assetPath, err := assetsMgr.GetAssetPath(schematicID, version, assetType)
if err != nil {
respondError(w, http.StatusNotFound, fmt.Sprintf("Asset not found: %v", err))
return
@@ -137,36 +134,39 @@ func (api *API) AssetsServePXE(w http.ResponseWriter, r *http.Request) {
http.ServeContent(w, r, info.Name(), info.ModTime(), file)
}
// AssetsGetStatus returns download status for a schematic
// AssetsGetStatus returns download status for a schematic@version
func (api *API) AssetsGetStatus(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
schematicID := vars["schematicId"]
version := vars["version"]
assetsMgr := assets.NewManager(api.dataDir)
status, err := assetsMgr.GetAssetStatus(schematicID)
status, err := assetsMgr.GetAssetStatus(schematicID, version)
if err != nil {
respondError(w, http.StatusNotFound, fmt.Sprintf("Schematic not found: %v", err))
respondError(w, http.StatusNotFound, fmt.Sprintf("Asset not found: %v", err))
return
}
respondJSON(w, http.StatusOK, status)
}
// AssetsDeleteSchematic deletes a schematic and all its assets
func (api *API) AssetsDeleteSchematic(w http.ResponseWriter, r *http.Request) {
// AssetsDelete deletes an asset (schematic@version) and all its files
func (api *API) AssetsDelete(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
schematicID := vars["schematicId"]
version := vars["version"]
assetsMgr := assets.NewManager(api.dataDir)
if err := assetsMgr.DeleteSchematic(schematicID); err != nil {
respondError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to delete schematic: %v", err))
if err := assetsMgr.DeleteAsset(schematicID, version); err != nil {
respondError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to delete asset: %v", err))
return
}
respondJSON(w, http.StatusOK, map[string]string{
"message": "Schematic deleted successfully",
"message": "Asset deleted successfully",
"schematic_id": schematicID,
"version": version,
})
}

View File

@@ -177,7 +177,7 @@ func TestUpdateYAMLFile_NestedStructure(t *testing.T) {
"cloud": map[string]interface{}{
"domain": "test.com",
"dns": map[string]interface{}{
"ip": "1.2.3.4",
"ip": "1.2.3.4",
"port": 53,
},
},
@@ -488,8 +488,8 @@ func TestUpdateYAMLFile_UpdateSecrets(t *testing.T) {
// Update secrets
updateData := map[string]interface{}{
"dbPassword": "secret123",
"apiKey": "key456",
"dbPassword": "secret123",
"apiKey": "key456",
}
updateYAML, _ := yaml.Marshal(updateData)

View File

@@ -45,17 +45,9 @@ func (api *API) PXEListAssets(w http.ResponseWriter, r *http.Request) {
})
return
}
// Proxy to new asset system
assetsMgr := assets.NewManager(api.dataDir)
schematic, err := assetsMgr.GetSchematic(schematicID)
if err != nil {
respondError(w, http.StatusNotFound, fmt.Sprintf("Schematic not found: %v", err))
return
}
respondJSON(w, http.StatusOK, map[string]interface{}{
"assets": schematic.Assets,
"assets": []interface{}{},
"message": "Please use the new /api/v1/pxe/assets endpoint with both schematic ID and version",
})
}
@@ -184,20 +176,7 @@ func (api *API) PXEGetAsset(w http.ResponseWriter, r *http.Request) {
return
}
// Proxy to new asset system - serve the file directly
assetsMgr := assets.NewManager(api.dataDir)
assetPath, err := assetsMgr.GetAssetPath(schematicID, assetType)
if err != nil {
respondError(w, http.StatusNotFound, fmt.Sprintf("Asset not found: %v", err))
return
}
respondJSON(w, http.StatusOK, map[string]interface{}{
"type": assetType,
"path": assetPath,
"valid": true,
"schematic_id": schematicID,
})
respondError(w, http.StatusBadRequest, "This deprecated endpoint requires version. Please use /api/v1/pxe/assets/{schematicId}/{version}/pxe/{assetType}")
}
// PXEDeleteAsset deletes a PXE asset

View File

@@ -39,9 +39,9 @@ func (api *API) SchematicGetInstanceSchematic(w http.ResponseWriter, r *http.Req
// If schematic is configured, get asset status
var assetStatus interface{}
if schematicID != "" && schematicID != "null" {
if schematicID != "" && schematicID != "null" && version != "" && version != "null" {
assetsMgr := assets.NewManager(api.dataDir)
status, err := assetsMgr.GetAssetStatus(schematicID)
status, err := assetsMgr.GetAssetStatus(schematicID, version)
if err == nil {
assetStatus = status
}

View File

@@ -37,9 +37,9 @@ type EnhancedApp struct {
// RuntimeStatus contains runtime information from kubernetes
type RuntimeStatus struct {
Pods []PodInfo `json:"pods,omitempty"`
Replicas *ReplicaInfo `json:"replicas,omitempty"`
Resources *ResourceUsage `json:"resources,omitempty"`
Pods []PodInfo `json:"pods,omitempty"`
Replicas *ReplicaInfo `json:"replicas,omitempty"`
Resources *ResourceUsage `json:"resources,omitempty"`
RecentEvents []KubernetesEvent `json:"recentEvents,omitempty"`
}

View File

@@ -33,8 +33,8 @@ type Asset struct {
Downloaded bool `json:"downloaded"` // Whether asset exists
}
// Schematic represents a Talos schematic and its assets
type Schematic struct {
// PXEAsset represents a schematic@version combination and its assets
type PXEAsset struct {
SchematicID string `json:"schematic_id"`
Version string `json:"version"`
Path string `json:"path"`
@@ -49,9 +49,10 @@ type AssetStatus struct {
Complete bool `json:"complete"`
}
// GetAssetDir returns the asset directory for a schematic
func (m *Manager) GetAssetDir(schematicID string) string {
return filepath.Join(m.dataDir, "assets", schematicID)
// GetAssetDir returns the asset directory for a schematic@version composite key
func (m *Manager) GetAssetDir(schematicID, version string) string {
composite := fmt.Sprintf("%s@%s", schematicID, version)
return filepath.Join(m.dataDir, "assets", composite)
}
// GetAssetsRootDir returns the root assets directory
@@ -59,8 +60,8 @@ func (m *Manager) GetAssetsRootDir() string {
return filepath.Join(m.dataDir, "assets")
}
// ListSchematics returns all available schematics
func (m *Manager) ListSchematics() ([]Schematic, error) {
// ListAssets returns all available schematic@version combinations
func (m *Manager) ListAssets() ([]PXEAsset, error) {
assetsDir := m.GetAssetsRootDir()
// Ensure assets directory exists
@@ -73,52 +74,53 @@ func (m *Manager) ListSchematics() ([]Schematic, error) {
return nil, fmt.Errorf("reading assets directory: %w", err)
}
var schematics []Schematic
var assets []PXEAsset
for _, entry := range entries {
if entry.IsDir() {
schematicID := entry.Name()
schematic, err := m.GetSchematic(schematicID)
if err != nil {
// Skip invalid schematics
// Parse directory name as schematicID@version
parts := strings.SplitN(entry.Name(), "@", 2)
if len(parts) != 2 {
// Skip invalid directory names (old format or other)
continue
}
schematics = append(schematics, *schematic)
schematicID := parts[0]
version := parts[1]
asset, err := m.GetAsset(schematicID, version)
if err != nil {
// Skip invalid assets
continue
}
assets = append(assets, *asset)
}
}
return schematics, nil
return assets, nil
}
// GetSchematic returns details for a specific schematic
func (m *Manager) GetSchematic(schematicID string) (*Schematic, error) {
// GetAsset returns details for a specific schematic@version combination
func (m *Manager) GetAsset(schematicID, version string) (*PXEAsset, error) {
if schematicID == "" {
return nil, fmt.Errorf("schematic ID cannot be empty")
}
if version == "" {
return nil, fmt.Errorf("version cannot be empty")
}
assetDir := m.GetAssetDir(schematicID)
assetDir := m.GetAssetDir(schematicID, version)
// Check if schematic directory exists
// Check if asset directory exists
if !storage.FileExists(assetDir) {
return nil, fmt.Errorf("schematic %s not found", schematicID)
return nil, fmt.Errorf("asset %s@%s not found", schematicID, version)
}
// List assets for this schematic
assets, err := m.listSchematicAssets(schematicID)
// List assets for this schematic@version
assets, err := m.listAssetFiles(schematicID, version)
if err != nil {
return nil, fmt.Errorf("listing schematic assets: %w", err)
return nil, fmt.Errorf("listing assets: %w", err)
}
// Try to determine version from version file
version := ""
versionPath := filepath.Join(assetDir, "version.txt")
if storage.FileExists(versionPath) {
data, err := os.ReadFile(versionPath)
if err == nil {
version = strings.TrimSpace(string(data))
}
}
return &Schematic{
return &PXEAsset{
SchematicID: schematicID,
Version: version,
Path: assetDir,
@@ -126,9 +128,14 @@ func (m *Manager) GetSchematic(schematicID string) (*Schematic, error) {
}, nil
}
// listSchematicAssets lists all assets for a schematic
func (m *Manager) listSchematicAssets(schematicID string) ([]Asset, error) {
assetDir := m.GetAssetDir(schematicID)
// AssetExists checks if a schematic@version exists
func (m *Manager) AssetExists(schematicID, version string) bool {
return storage.FileExists(m.GetAssetDir(schematicID, version))
}
// listAssetFiles lists all asset files for a schematic@version
func (m *Manager) listAssetFiles(schematicID, version string) ([]Asset, error) {
assetDir := m.GetAssetDir(schematicID, version)
var assets []Asset
@@ -221,19 +228,13 @@ func (m *Manager) DownloadAssets(schematicID, version, platform string, assetTyp
assetTypes = []string{"kernel", "initramfs", "iso"}
}
assetDir := m.GetAssetDir(schematicID)
assetDir := m.GetAssetDir(schematicID, version)
// Ensure asset directory exists
if err := storage.EnsureDir(assetDir, 0755); err != nil {
return fmt.Errorf("creating asset directory: %w", err)
}
// Save version info
versionPath := filepath.Join(assetDir, "version.txt")
if err := os.WriteFile(versionPath, []byte(version), 0644); err != nil {
return fmt.Errorf("saving version info: %w", err)
}
// Download each requested asset
for _, assetType := range assetTypes {
if err := m.downloadAsset(schematicID, assetType, version, platform); err != nil {
@@ -246,7 +247,7 @@ func (m *Manager) DownloadAssets(schematicID, version, platform string, assetTyp
// downloadAsset downloads a single asset
func (m *Manager) downloadAsset(schematicID, assetType, version, platform string) error {
assetDir := m.GetAssetDir(schematicID)
assetDir := m.GetAssetDir(schematicID, version)
// Determine subdirectory, filename, and URL based on asset type and platform
var subdir, filename, urlPath string
@@ -261,7 +262,7 @@ func (m *Manager) downloadAsset(schematicID, assetType, version, platform string
urlPath = fmt.Sprintf("initramfs-%s.xz", platform)
case "iso":
subdir = "iso"
// Preserve version and platform in filename for clarity
// Include version in filename for clarity
filename = fmt.Sprintf("talos-%s-metal-%s.iso", version, platform)
urlPath = fmt.Sprintf("metal-%s.iso", platform)
default:
@@ -322,31 +323,24 @@ func (m *Manager) downloadAsset(schematicID, assetType, version, platform string
return nil
}
// GetAssetStatus returns the download status for a schematic
func (m *Manager) GetAssetStatus(schematicID string) (*AssetStatus, error) {
// GetAssetStatus returns the download status for a schematic@version
func (m *Manager) GetAssetStatus(schematicID, version string) (*AssetStatus, error) {
if schematicID == "" {
return nil, fmt.Errorf("schematic ID cannot be empty")
}
assetDir := m.GetAssetDir(schematicID)
// Check if schematic directory exists
if !storage.FileExists(assetDir) {
return nil, fmt.Errorf("schematic %s not found", schematicID)
if version == "" {
return nil, fmt.Errorf("version cannot be empty")
}
// Get version
version := ""
versionPath := filepath.Join(assetDir, "version.txt")
if storage.FileExists(versionPath) {
data, err := os.ReadFile(versionPath)
if err == nil {
version = strings.TrimSpace(string(data))
}
assetDir := m.GetAssetDir(schematicID, version)
// Check if asset directory exists
if !storage.FileExists(assetDir) {
return nil, fmt.Errorf("asset %s@%s not found", schematicID, version)
}
// List assets
assets, err := m.listSchematicAssets(schematicID)
assets, err := m.listAssetFiles(schematicID, version)
if err != nil {
return nil, fmt.Errorf("listing assets: %w", err)
}
@@ -370,12 +364,15 @@ func (m *Manager) GetAssetStatus(schematicID string) (*AssetStatus, error) {
}
// GetAssetPath returns the path to a specific asset file
func (m *Manager) GetAssetPath(schematicID, assetType string) (string, error) {
func (m *Manager) GetAssetPath(schematicID, version, assetType string) (string, error) {
if schematicID == "" {
return "", fmt.Errorf("schematic ID cannot be empty")
}
if version == "" {
return "", fmt.Errorf("version cannot be empty")
}
assetDir := m.GetAssetDir(schematicID)
assetDir := m.GetAssetDir(schematicID, version)
var subdir, pattern string
switch assetType {
@@ -387,7 +384,7 @@ func (m *Manager) GetAssetPath(schematicID, assetType string) (string, error) {
pattern = "initramfs-amd64.xz"
case "iso":
subdir = "iso"
pattern = "talos-*.iso" // Glob pattern for version-specific filename
pattern = "talos-*.iso" // Glob pattern for version and platform-specific filename
default:
return "", fmt.Errorf("unknown asset type: %s", assetType)
}
@@ -416,13 +413,16 @@ func (m *Manager) GetAssetPath(schematicID, assetType string) (string, error) {
return assetPath, nil
}
// DeleteSchematic removes a schematic and all its assets
func (m *Manager) DeleteSchematic(schematicID string) error {
// DeleteAsset removes a schematic@version and all its assets
func (m *Manager) DeleteAsset(schematicID, version string) error {
if schematicID == "" {
return fmt.Errorf("schematic ID cannot be empty")
}
if version == "" {
return fmt.Errorf("version cannot be empty")
}
assetDir := m.GetAssetDir(schematicID)
assetDir := m.GetAssetDir(schematicID, version)
if !storage.FileExists(assetDir) {
return nil // Already deleted, idempotent

View File

@@ -691,10 +691,10 @@ cluster:
wantErr: false,
},
{
name: "creates destination directory",
srcYAML: `baseDomain: "example.com"`,
setupDst: nil,
wantErr: false,
name: "creates destination directory",
srcYAML: `baseDomain: "example.com"`,
setupDst: nil,
wantErr: false,
},
{
name: "overwrites existing destination",

View File

@@ -28,7 +28,7 @@ func NewManager(dataDir string) *Manager {
// BootstrapProgress tracks detailed bootstrap progress
type BootstrapProgress struct {
CurrentStep int `json:"current_step"` // 0-6
CurrentStep int `json:"current_step"` // 0-6
StepName string `json:"step_name"`
Attempt int `json:"attempt"`
MaxAttempts int `json:"max_attempts"`
@@ -97,7 +97,6 @@ func (m *Manager) Start(instanceName, opType, target string) (string, error) {
return opID, nil
}
// GetByInstance returns an operation for a specific instance
func (m *Manager) GetByInstance(instanceName, opID string) (*Operation, error) {
opsDir := m.GetOperationsDir(instanceName)

View File

@@ -127,11 +127,11 @@ func TestEnsureDir(t *testing.T) {
func TestReadFile(t *testing.T) {
tests := []struct {
name string
setup func(tmpDir string) string
wantData []byte
wantErr bool
errCheck func(error) bool
name string
setup func(tmpDir string) string
wantData []byte
wantErr bool
errCheck func(error) bool
}{
{
name: "read existing file",

View File

@@ -26,15 +26,15 @@ func NewKubectl(kubeconfigPath string) *Kubectl {
// PodInfo represents pod information from kubectl
type PodInfo struct {
Name string `json:"name"`
Status string `json:"status"`
Ready string `json:"ready"`
Restarts int `json:"restarts"`
Age string `json:"age"`
Node string `json:"node,omitempty"`
IP string `json:"ip,omitempty"`
Containers []ContainerInfo `json:"containers,omitempty"`
Conditions []PodCondition `json:"conditions,omitempty"`
Name string `json:"name"`
Status string `json:"status"`
Ready string `json:"ready"`
Restarts int `json:"restarts"`
Age string `json:"age"`
Node string `json:"node,omitempty"`
IP string `json:"ip,omitempty"`
Containers []ContainerInfo `json:"containers,omitempty"`
Conditions []PodCondition `json:"conditions,omitempty"`
}
// ContainerInfo represents detailed container information
@@ -195,7 +195,7 @@ func (k *Kubectl) GetPods(namespace string, detailed bool) ([]PodInfo, error) {
Ready bool `json:"ready"`
RestartCount int `json:"restartCount"`
State struct {
Running *struct{ StartedAt time.Time } `json:"running,omitempty"`
Running *struct{ StartedAt time.Time } `json:"running,omitempty"`
Waiting *struct{ Reason, Message string } `json:"waiting,omitempty"`
Terminated *struct {
Reason string

View File

@@ -31,22 +31,22 @@ func TestNewTalosctl(t *testing.T) {
func TestTalosconfigBuildArgs(t *testing.T) {
tests := []struct {
name string
name string
talosconfigPath string
baseArgs []string
wantPrefix []string
baseArgs []string
wantPrefix []string
}{
{
name: "no talosconfig adds no prefix",
name: "no talosconfig adds no prefix",
talosconfigPath: "",
baseArgs: []string{"version", "--short"},
wantPrefix: nil,
baseArgs: []string{"version", "--short"},
wantPrefix: nil,
},
{
name: "with talosconfig adds prefix",
name: "with talosconfig adds prefix",
talosconfigPath: "/path/to/talosconfig",
baseArgs: []string{"version", "--short"},
wantPrefix: []string{"--talosconfig", "/path/to/talosconfig"},
baseArgs: []string{"version", "--short"},
wantPrefix: []string{"--talosconfig", "/path/to/talosconfig"},
},
}
@@ -137,12 +137,12 @@ func TestTalosconfigGenConfig(t *testing.T) {
func TestTalosconfigApplyConfig(t *testing.T) {
tests := []struct {
name string
nodeIP string
configFile string
insecure bool
talosconfigPath string
skipTest bool
name string
nodeIP string
configFile string
insecure bool
talosconfigPath string
skipTest bool
}{
{
name: "apply config with all params",

View File

@@ -370,9 +370,9 @@ func TestYQExec(t *testing.T) {
func TestCleanYQOutput(t *testing.T) {
tests := []struct {
name string
input string
want string
name string
input string
want string
}{
{
name: "removes trailing newline",