feat: add named errors to cloud config

Changes errors created by cloud config to be standardized so that any
other packages relying on the cloud config can check if the error is of
the same "type".

Signed-off-by: Daniel J. Holmes (jaitaiwan) <dan@jaitaiwan.dev>
This commit is contained in:
Daniel J. Holmes (jaitaiwan)
2025-08-01 17:32:58 +10:00
committed by Serge
parent 1ce4ade1c6
commit 144b1c74e6
2 changed files with 22 additions and 6 deletions

View File

@@ -18,6 +18,7 @@ limitations under the License.
package config package config
import ( import (
"errors"
"fmt" "fmt"
"io" "io"
"os" "os"
@@ -71,31 +72,41 @@ type ClustersConfig struct {
Clusters []*proxmoxpool.ProxmoxCluster `yaml:"clusters,omitempty"` Clusters []*proxmoxpool.ProxmoxCluster `yaml:"clusters,omitempty"`
} }
// Errors for Reading Cloud Config
var (
ErrMissingPVERegion = errors.New("missing PVE region in cloud config")
ErrMissingPVEAPIURL = errors.New("missing PVE API URL in cloud config")
ErrAuthCredentialsMissing = errors.New("user or token credentials are required")
ErrInvalidAuthCredentials = errors.New("must specify one of user or token credentials, not both")
ErrInvalidCloudConfig = errors.New("invalid cloud config")
ErrInvalidNetworkMode = fmt.Errorf("invalid network mode, valid modes are %v", ValidNetworkModes)
)
// ReadCloudConfig reads cloud config from a reader. // ReadCloudConfig reads cloud config from a reader.
func ReadCloudConfig(config io.Reader) (ClustersConfig, error) { func ReadCloudConfig(config io.Reader) (ClustersConfig, error) {
cfg := ClustersConfig{} cfg := ClustersConfig{}
if config != nil { if config != nil {
if err := yaml.NewDecoder(config).Decode(&cfg); err != nil { if err := yaml.NewDecoder(config).Decode(&cfg); err != nil {
return ClustersConfig{}, err return ClustersConfig{}, errors.Join(ErrInvalidCloudConfig, err)
} }
} }
for idx, c := range cfg.Clusters { for idx, c := range cfg.Clusters {
if c.Username != "" && c.Password != "" { if c.Username != "" && c.Password != "" {
if c.TokenID != "" || c.TokenSecret != "" { if c.TokenID != "" || c.TokenSecret != "" {
return ClustersConfig{}, fmt.Errorf("cluster #%d: token_id and token_secret are not allowed when username and password are set", idx+1) return ClustersConfig{}, fmt.Errorf("cluster #%d: %w", idx+1, ErrInvalidAuthCredentials)
} }
} else if c.TokenID == "" || c.TokenSecret == "" { } else if c.TokenID == "" || c.TokenSecret == "" {
return ClustersConfig{}, fmt.Errorf("cluster #%d: either username and password or token_id and token_secret are required", idx+1) return ClustersConfig{}, fmt.Errorf("cluster #%d: %w", idx+1, ErrAuthCredentialsMissing)
} }
if c.Region == "" { if c.Region == "" {
return ClustersConfig{}, fmt.Errorf("cluster #%d: region is required", idx+1) return ClustersConfig{}, fmt.Errorf("cluster #%d: %w", idx+1, ErrMissingPVERegion)
} }
if c.URL == "" || !strings.HasPrefix(c.URL, "http") { if c.URL == "" || !strings.HasPrefix(c.URL, "http") {
return ClustersConfig{}, fmt.Errorf("cluster #%d: url is required", idx+1) return ClustersConfig{}, fmt.Errorf("cluster #%d: %w", idx+1, ErrMissingPVEAPIURL)
} }
} }
@@ -109,7 +120,7 @@ func ReadCloudConfig(config io.Reader) (ClustersConfig, error) {
// Validate network mode is valid // Validate network mode is valid
if !slices.Contains(ValidNetworkModes, cfg.Features.Network.Mode) { if !slices.Contains(ValidNetworkModes, cfg.Features.Network.Mode) {
return ClustersConfig{}, fmt.Errorf("invalid network mode: %s, valid modes are %v", cfg.Features.Network.Mode, ValidNetworkModes) return ClustersConfig{}, ErrInvalidNetworkMode
} }
return cfg, nil return cfg, nil

View File

@@ -44,6 +44,7 @@ clusters:
`)) `))
assert.NotNil(t, err) assert.NotNil(t, err)
assert.ErrorIs(t, err, providerconfig.ErrInvalidCloudConfig)
assert.NotNil(t, cfg) assert.NotNil(t, cfg)
// Non full config // Non full config
@@ -54,6 +55,7 @@ clusters:
`)) `))
assert.NotNil(t, err) assert.NotNil(t, err)
assert.ErrorIs(t, err, providerconfig.ErrAuthCredentialsMissing)
assert.NotNil(t, cfg) assert.NotNil(t, cfg)
// Valid config with one cluster // Valid config with one cluster
@@ -142,6 +144,7 @@ clusters:
password: "secret" password: "secret"
`)) `))
assert.NotNil(t, err) assert.NotNil(t, err)
assert.ErrorIs(t, err, providerconfig.ErrMissingPVERegion)
// Errors when empty url // Errors when empty url
_, err = providerconfig.ReadCloudConfig(strings.NewReader(` _, err = providerconfig.ReadCloudConfig(strings.NewReader(`
@@ -155,6 +158,7 @@ clusters:
password: "secret" password: "secret"
`)) `))
assert.NotNil(t, err) assert.NotNil(t, err)
assert.ErrorIs(t, err, providerconfig.ErrMissingPVEAPIURL)
// Errors when invalid url protocol // Errors when invalid url protocol
_, err = providerconfig.ReadCloudConfig(strings.NewReader(` _, err = providerconfig.ReadCloudConfig(strings.NewReader(`
@@ -168,6 +172,7 @@ clusters:
password: "secret" password: "secret"
`)) `))
assert.NotNil(t, err) assert.NotNil(t, err)
assert.ErrorIs(t, err, providerconfig.ErrMissingPVEAPIURL)
} }
func TestNetworkConfig(t *testing.T) { func TestNetworkConfig(t *testing.T) {