-
Notifications
You must be signed in to change notification settings - Fork 28
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
IPV6 APIVIP Handling #589
IPV6 APIVIP Handling #589
Changes from all commits
bcafb09
26e8725
1e82f22
9a6cd09
090d385
d66ad0b
38dc6b7
cdca128
7582178
86843c2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,7 @@ package validation | |
import ( | ||
"errors" | ||
"fmt" | ||
"net/netip" | ||
"net/url" | ||
"os" | ||
"path/filepath" | ||
|
@@ -32,6 +33,7 @@ func validateKubernetes(ctx *image.Context) []FailedValidation { | |
return failures | ||
} | ||
|
||
failures = append(failures, validateNetwork(&def.Kubernetes)...) | ||
failures = append(failures, validateNodes(&def.Kubernetes)...) | ||
failures = append(failures, validateManifestURLs(&def.Kubernetes)...) | ||
failures = append(failures, validateHelm(&def.Kubernetes, combustion.HelmValuesPath(ctx), combustion.HelmCertsPath(ctx))...) | ||
|
@@ -52,12 +54,6 @@ func validateNodes(k8s *image.Kubernetes) []FailedValidation { | |
return failures | ||
} | ||
|
||
if k8s.Network.APIVIP == "" { | ||
failures = append(failures, FailedValidation{ | ||
UserMessage: "The 'apiVIP' field is required in the 'network' section when defining entries under 'nodes'.", | ||
}) | ||
} | ||
|
||
var nodeTypes []string | ||
var nodeNames []string | ||
var initialisers []*image.Node | ||
|
@@ -117,6 +113,47 @@ func validateNodes(k8s *image.Kubernetes) []FailedValidation { | |
return failures | ||
} | ||
|
||
func validateNetwork(k8s *image.Kubernetes) []FailedValidation { | ||
var failures []FailedValidation | ||
|
||
if k8s.Network.APIVIP == "" { | ||
if len(k8s.Nodes) > 1 { | ||
failures = append(failures, FailedValidation{ | ||
UserMessage: "The 'apiVIP' field is required in the 'network' section for multi node clusters.", | ||
}) | ||
} | ||
|
||
return failures | ||
} | ||
|
||
parsedIP, err := netip.ParseAddr(k8s.Network.APIVIP) | ||
if err != nil { | ||
failures = append(failures, FailedValidation{ | ||
atanasdinov marked this conversation as resolved.
Show resolved
Hide resolved
|
||
UserMessage: fmt.Sprintf("Invalid address value %q for field 'apiVIP'.", k8s.Network.APIVIP), | ||
Error: err, | ||
}) | ||
|
||
return failures | ||
} | ||
|
||
if !parsedIP.Is4() && !parsedIP.Is6() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These two checks are redundant, when the address is parsed successfully it's either IPv4 or IPv6. And would also not pass the following IsGlobalUnicast() test anyway. |
||
failures = append(failures, FailedValidation{ | ||
UserMessage: "Only IPv4 and IPv6 addresses are valid values for field 'apiVIP'.", | ||
}) | ||
|
||
return failures | ||
} | ||
|
||
if !parsedIP.IsGlobalUnicast() { | ||
msg := fmt.Sprintf("Invalid non-unicast cluster API address (%s) for field 'apiVIP'.", k8s.Network.APIVIP) | ||
failures = append(failures, FailedValidation{ | ||
UserMessage: msg, | ||
}) | ||
} | ||
|
||
return failures | ||
} | ||
|
||
func validateManifestURLs(k8s *image.Kubernetes) []FailedValidation { | ||
var failures []FailedValidation | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,7 +15,7 @@ import ( | |
|
||
var validNetwork = image.Network{ | ||
APIHost: "host.com", | ||
APIVIP: "127.0.0.1", | ||
APIVIP: "192.168.1.1", | ||
} | ||
|
||
func TestValidateKubernetes(t *testing.T) { | ||
|
@@ -78,7 +78,10 @@ func TestValidateKubernetes(t *testing.T) { | |
`failures all sections`: { | ||
K8s: image.Kubernetes{ | ||
Version: "v1.30.3", | ||
Network: validNetwork, | ||
Network: image.Network{ | ||
APIHost: "host.com", | ||
APIVIP: "127.0.0.1", | ||
}, | ||
Nodes: []image.Node{ | ||
{ | ||
Type: image.KubernetesNodeTypeServer, | ||
|
@@ -116,6 +119,7 @@ func TestValidateKubernetes(t *testing.T) { | |
"Helm chart 'name' field must be defined.", | ||
"Helm repository 'name' field for \"apache-repo\" must match the 'repositoryName' field in at least one defined Helm chart.", | ||
"Helm chart 'repositoryName' \"another-apache-repo\" for Helm chart \"\" does not match the name of any defined repository.", | ||
"Invalid non-unicast cluster API address (127.0.0.1) for field 'apiVIP'.", | ||
}, | ||
}, | ||
} | ||
|
@@ -185,24 +189,6 @@ func TestValidateNodes(t *testing.T) { | |
Nodes: []image.Node{}, | ||
}, | ||
}, | ||
`with nodes - no network config`: { | ||
K8s: image.Kubernetes{ | ||
Network: image.Network{}, | ||
Nodes: []image.Node{ | ||
{ | ||
Hostname: "host1", | ||
Type: image.KubernetesNodeTypeServer, | ||
}, | ||
{ | ||
Hostname: "host2", | ||
Type: image.KubernetesNodeTypeAgent, | ||
}, | ||
}, | ||
}, | ||
ExpectedFailedMessages: []string{ | ||
"The 'apiVIP' field is required in the 'network' section when defining entries under 'nodes'.", | ||
}, | ||
}, | ||
`no hostname`: { | ||
K8s: image.Kubernetes{ | ||
Network: validNetwork, | ||
|
@@ -1079,3 +1065,108 @@ func TestValidateAdditionalArtifacts(t *testing.T) { | |
}) | ||
} | ||
} | ||
|
||
func TestValidateNetwork(t *testing.T) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hey Danial, glad to see that most of my code has been reused :) |
||
tests := map[string]struct { | ||
K8s image.Kubernetes | ||
ExpectedFailedMessages []string | ||
}{ | ||
`no network defined, no nodes defined`: { | ||
K8s: image.Kubernetes{ | ||
Network: image.Network{}, | ||
}, | ||
}, | ||
`no network defined, nodes defined`: { | ||
K8s: image.Kubernetes{ | ||
Network: image.Network{}, | ||
Nodes: []image.Node{ | ||
{ | ||
Hostname: "node1", | ||
Type: "server", | ||
Initialiser: false, | ||
}, | ||
{ | ||
Hostname: "node2", | ||
Type: "server", | ||
Initialiser: false, | ||
}, | ||
}, | ||
}, | ||
ExpectedFailedMessages: []string{ | ||
"The 'apiVIP' field is required in the 'network' section for multi node clusters.", | ||
}, | ||
}, | ||
`valid ipv4`: { | ||
K8s: image.Kubernetes{ | ||
Network: image.Network{ | ||
APIVIP: "192.168.1.1", | ||
}, | ||
}, | ||
}, | ||
`valid ipv6`: { | ||
K8s: image.Kubernetes{ | ||
Network: image.Network{ | ||
APIVIP: "fd12:3456:789a::21", | ||
}, | ||
}, | ||
}, | ||
`invalid ipv4`: { | ||
K8s: image.Kubernetes{ | ||
Network: image.Network{ | ||
APIVIP: "500.168.1.1", | ||
}, | ||
}, | ||
ExpectedFailedMessages: []string{ | ||
"Invalid address value \"500.168.1.1\" for field 'apiVIP'.", | ||
}, | ||
}, | ||
`non-unicast ipv4`: { | ||
K8s: image.Kubernetes{ | ||
Network: image.Network{ | ||
APIVIP: "127.0.0.1", | ||
}, | ||
}, | ||
ExpectedFailedMessages: []string{ | ||
"Invalid non-unicast cluster API address (127.0.0.1) for field 'apiVIP'.", | ||
}, | ||
}, | ||
`invalid ipv6`: { | ||
K8s: image.Kubernetes{ | ||
Network: image.Network{ | ||
APIVIP: "xxxx:3456:789a::21", | ||
}, | ||
}, | ||
ExpectedFailedMessages: []string{ | ||
"Invalid address value \"xxxx:3456:789a::21\" for field 'apiVIP'.", | ||
}, | ||
}, | ||
`non-unicast ipv6`: { | ||
K8s: image.Kubernetes{ | ||
Network: image.Network{ | ||
APIVIP: "ff02::1", | ||
}, | ||
}, | ||
ExpectedFailedMessages: []string{ | ||
"Invalid non-unicast cluster API address (ff02::1) for field 'apiVIP'.", | ||
}, | ||
}, | ||
} | ||
|
||
for name, test := range tests { | ||
t.Run(name, func(t *testing.T) { | ||
k := test.K8s | ||
failures := validateNetwork(&k) | ||
assert.Len(t, failures, len(test.ExpectedFailedMessages)) | ||
|
||
var foundMessages []string | ||
for _, foundValidation := range failures { | ||
foundMessages = append(foundMessages, foundValidation.UserMessage) | ||
} | ||
|
||
for _, expectedMessage := range test.ExpectedFailedMessages { | ||
assert.Contains(t, foundMessages, expectedMessage) | ||
} | ||
|
||
}) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit-picking, but I'd align these two names from "IPV4"/"IPV6" to IPv4 too, as the rest of the code/prints.