Skip to content

Commit faa2b73

Browse files
authored
ci: Improve Validate package (Azure#2485)
* ci: improve validate package * ci: add v6 hns validate * chore: address comments Signed-off-by: John Payne <[email protected]> * ci: capture hns json object * chore: lint fix * ci: kubeproxy restart in CreateValidator * fix: validateRestartNetwork for hybrid clusters * ci: cleanup RestartKubeProxyService changes * chore: lint fix * chore: address comments --------- Signed-off-by: John Payne <[email protected]>
1 parent 8acc551 commit faa2b73

File tree

6 files changed

+186
-108
lines changed

6 files changed

+186
-108
lines changed

test/integration/datapath/datapath_windows_test.go

+9-4
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import (
1010

1111
"github.com/Azure/azure-container-networking/test/internal/datapath"
1212
"github.com/Azure/azure-container-networking/test/internal/kubernetes"
13-
"github.com/Azure/azure-container-networking/test/validate"
1413
"github.com/stretchr/testify/require"
1514
apiv1 "k8s.io/api/core/v1"
1615
)
@@ -57,9 +56,15 @@ func setupWindowsEnvironment(t *testing.T) {
5756
clientset := kubernetes.MustGetClientset()
5857

5958
if *restartKubeproxy {
60-
validator, err := validate.CreateValidator(ctx, clientset, restConfig, *podNamespace, "cniv2", false, "windows")
61-
require.NoError(t, err)
62-
err = validator.RestartKubeProxyService(ctx)
59+
privilegedDaemonSet := kubernetes.MustParseDaemonSet(kubernetes.PrivilegedDaemonSetPath)
60+
daemonsetClient := clientset.AppsV1().DaemonSets(kubernetes.PrivilegedNamespace)
61+
kubernetes.MustCreateDaemonset(ctx, daemonsetClient, privilegedDaemonSet)
62+
63+
// Ensures that pods have been replaced if test is re-run after failure
64+
if err := kubernetes.WaitForPodDaemonset(ctx, clientset, kubernetes.PrivilegedNamespace, privilegedDaemonSet.Name, kubernetes.PrivilegedLabelSelector); err != nil {
65+
require.NoError(t, err)
66+
}
67+
err := kubernetes.RestartKubeProxyService(ctx, clientset, kubernetes.PrivilegedNamespace, kubernetes.PrivilegedLabelSelector, restConfig)
6368
require.NoError(t, err)
6469
}
6570

test/internal/kubernetes/utils.go

+43-4
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,13 @@ const (
3434
SubnetNameLabel = "kubernetes.azure.com/podnetwork-subnet"
3535

3636
// RetryAttempts is the number of times to retry a test.
37-
RetryAttempts = 90
38-
RetryDelay = 10 * time.Second
39-
DeleteRetryAttempts = 12
40-
DeleteRetryDelay = 5 * time.Second
37+
RetryAttempts = 90
38+
RetryDelay = 10 * time.Second
39+
DeleteRetryAttempts = 12
40+
DeleteRetryDelay = 5 * time.Second
41+
PrivilegedDaemonSetPath = "../manifests/load/privileged-daemonset-windows.yaml"
42+
PrivilegedLabelSelector = "app=privileged-daemonset"
43+
PrivilegedNamespace = "kube-system"
4144
)
4245

4346
var Kubeconfig = flag.String("test-kubeconfig", filepath.Join(homedir.HomeDir(), ".kube", "config"), "(optional) absolute path to the kubeconfig file")
@@ -403,6 +406,9 @@ func ExecCmdOnPod(ctx context.Context, clientset *kubernetes.Clientset, namespac
403406
if err != nil {
404407
return []byte{}, errors.Wrapf(err, "error in executing command %s", cmd)
405408
}
409+
if len(stdout.Bytes()) == 0 {
410+
log.Printf("Warning: %v had 0 bytes returned from command - %v", podName, cmd)
411+
}
406412

407413
return stdout.Bytes(), nil
408414
}
@@ -465,3 +471,36 @@ func MustRestartDaemonset(ctx context.Context, clientset *kubernetes.Clientset,
465471
_, err = clientset.AppsV1().DaemonSets(namespace).Update(ctx, ds, metav1.UpdateOptions{})
466472
return errors.Wrapf(err, "failed to update ds %s", daemonsetName)
467473
}
474+
475+
// Restarts kubeproxy on windows nodes from an existing privileged daemonset
476+
func RestartKubeProxyService(ctx context.Context, clientset *kubernetes.Clientset, privilegedNamespace, privilegedLabelSelector string, config *rest.Config) error {
477+
restartKubeProxyCmd := []string{"powershell", "Restart-service", "kubeproxy"}
478+
479+
nodes, err := GetNodeList(ctx, clientset)
480+
if err != nil {
481+
return errors.Wrapf(err, "failed to get node list")
482+
}
483+
484+
for index := range nodes.Items {
485+
node := nodes.Items[index]
486+
if node.Status.NodeInfo.OperatingSystem != string(corev1.Windows) {
487+
continue
488+
}
489+
// get the privileged pod
490+
pod, err := GetPodsByNode(ctx, clientset, privilegedNamespace, privilegedLabelSelector, node.Name)
491+
if err != nil {
492+
return errors.Wrapf(err, "failed to get privileged pod on node %s", node.Name)
493+
}
494+
495+
if len(pod.Items) == 0 {
496+
return errors.Errorf("there are no privileged pods on node - %v", node.Name)
497+
}
498+
privilegedPod := pod.Items[0]
499+
// exec into the pod and restart kubeproxy
500+
_, err = ExecCmdOnPod(ctx, clientset, privilegedNamespace, privilegedPod.Name, restartKubeProxyCmd, config)
501+
if err != nil {
502+
return errors.Wrapf(err, "failed to exec into privileged pod %s on node %s", privilegedPod.Name, node.Name)
503+
}
504+
}
505+
return nil
506+
}

test/validate/linux_validate.go

+46-24
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
package validate
22

33
import (
4+
"context"
45
"encoding/json"
56

67
"github.com/Azure/azure-container-networking/cns"
78
restserver "github.com/Azure/azure-container-networking/cns/restserver"
9+
acnk8s "github.com/Azure/azure-container-networking/test/internal/kubernetes"
810
"github.com/pkg/errors"
11+
corev1 "k8s.io/api/core/v1"
912
)
1013

1114
const (
@@ -15,12 +18,12 @@ const (
1518
)
1619

1720
var (
18-
restartNetworkCmd = []string{"bash", "-c", "chroot /host /bin/bash -c systemctl restart systemd-networkd"}
19-
cnsManagedStateFileCmd = []string{"bash", "-c", "cat /var/run/azure-cns/azure-endpoints.json"}
20-
azureVnetStateFileCmd = []string{"bash", "-c", "cat /var/run/azure-vnet.json"}
21-
azureVnetIpamStateCmd = []string{"bash", "-c", "cat /var/run/azure-vnet-ipam.json"}
22-
ciliumStateFileCmd = []string{"bash", "-c", "cilium endpoint list -o json"}
23-
cnsLocalCacheCmd = []string{"curl", "localhost:10090/debug/ipaddresses", "-d", "{\"IPConfigStateFilter\":[\"Assigned\"]}"}
21+
restartNetworkCmd = []string{"bash", "-c", "chroot /host /bin/bash -c systemctl restart systemd-networkd"}
22+
cnsManagedStateFileCmd = []string{"bash", "-c", "cat /var/run/azure-cns/azure-endpoints.json"}
23+
azureVnetStateFileCmd = []string{"bash", "-c", "cat /var/run/azure-vnet.json"}
24+
azureVnetIpamStateCmd = []string{"bash", "-c", "cat /var/run/azure-vnet-ipam.json"}
25+
ciliumStateFileCmd = []string{"bash", "-c", "cilium endpoint list -o json"}
26+
cnsCachedAssignedIPStateCmd = []string{"curl", "localhost:10090/debug/ipaddresses", "-d", "{\"IPConfigStateFilter\":[\"Assigned\"]}"}
2427
)
2528

2629
type stateFileIpsFunc func([]byte) (map[string]string, error)
@@ -29,18 +32,18 @@ var linuxChecksMap = map[string][]check{
2932
"cilium": {
3033
{"cns", cnsManagedStateFileIps, cnsLabelSelector, privilegedNamespace, cnsManagedStateFileCmd}, // cns configmap "ManageEndpointState": true, | Endpoints managed in CNS State File
3134
{"cilium", ciliumStateFileIps, ciliumLabelSelector, privilegedNamespace, ciliumStateFileCmd},
32-
{"cns cache", cnsCacheStateFileIps, cnsLabelSelector, privilegedNamespace, cnsLocalCacheCmd},
35+
{"cns cache", cnsCacheStateFileIps, cnsLabelSelector, privilegedNamespace, cnsCachedAssignedIPStateCmd},
3336
},
3437
"cniv1": {
3538
{"azure-vnet", azureVnetStateIps, privilegedLabelSelector, privilegedNamespace, azureVnetStateFileCmd},
3639
{"azure-vnet-ipam", azureVnetIpamStateIps, privilegedLabelSelector, privilegedNamespace, azureVnetIpamStateCmd},
3740
},
3841
"cniv2": {
39-
{"cns cache", cnsCacheStateFileIps, cnsLabelSelector, privilegedNamespace, cnsLocalCacheCmd},
42+
{"cns cache", cnsCacheStateFileIps, cnsLabelSelector, privilegedNamespace, cnsCachedAssignedIPStateCmd},
4043
{"azure-vnet", azureVnetStateIps, privilegedLabelSelector, privilegedNamespace, azureVnetStateFileCmd}, // cns configmap "ManageEndpointState": false, | Endpoints managed in CNI State File
4144
},
4245
"dualstack": {
43-
{"cns cache", cnsCacheStateFileIps, cnsLabelSelector, privilegedNamespace, cnsLocalCacheCmd},
46+
{"cns cache", cnsCacheStateFileIps, cnsLabelSelector, privilegedNamespace, cnsCachedAssignedIPStateCmd},
4447
{"azure dualstackoverlay", azureVnetStateIps, privilegedLabelSelector, privilegedNamespace, azureVnetStateFileCmd},
4548
},
4649
}
@@ -156,21 +159,6 @@ func ciliumStateFileIps(result []byte) (map[string]string, error) {
156159
return ciliumPodIps, nil
157160
}
158161

159-
func cnsCacheStateFileIps(result []byte) (map[string]string, error) {
160-
var cnsLocalCache CNSLocalCache
161-
162-
err := json.Unmarshal(result, &cnsLocalCache)
163-
if err != nil {
164-
return nil, errors.Wrapf(err, "failed to unmarshal cns local cache")
165-
}
166-
167-
cnsPodIps := make(map[string]string)
168-
for index := range cnsLocalCache.IPConfigurationStatus {
169-
cnsPodIps[cnsLocalCache.IPConfigurationStatus[index].IPAddress] = cnsLocalCache.IPConfigurationStatus[index].PodInfo.Name()
170-
}
171-
return cnsPodIps, nil
172-
}
173-
174162
func azureVnetStateIps(result []byte) (map[string]string, error) {
175163
var azureVnetResult AzureCniState
176164
err := json.Unmarshal(result, &azureVnetResult)
@@ -212,3 +200,37 @@ func azureVnetIpamStateIps(result []byte) (map[string]string, error) {
212200
}
213201
return azureVnetIpamPodIps, nil
214202
}
203+
204+
// Linux only function
205+
func (v *Validator) validateRestartNetwork(ctx context.Context) error {
206+
nodes, err := acnk8s.GetNodeList(ctx, v.clientset)
207+
if err != nil {
208+
return errors.Wrapf(err, "failed to get node list")
209+
}
210+
211+
for index := range nodes.Items {
212+
node := nodes.Items[index]
213+
if node.Status.NodeInfo.OperatingSystem != string(corev1.Linux) {
214+
continue
215+
}
216+
// get the privileged pod
217+
pod, err := acnk8s.GetPodsByNode(ctx, v.clientset, privilegedNamespace, privilegedLabelSelector, node.Name)
218+
if err != nil {
219+
return errors.Wrapf(err, "failed to get privileged pod")
220+
}
221+
if len(pod.Items) == 0 {
222+
return errors.Errorf("there are no privileged pods on node - %v", node.Name)
223+
}
224+
privilegedPod := pod.Items[0]
225+
// exec into the pod to get the state file
226+
_, err = acnk8s.ExecCmdOnPod(ctx, v.clientset, privilegedNamespace, privilegedPod.Name, restartNetworkCmd, v.config)
227+
if err != nil {
228+
return errors.Wrapf(err, "failed to exec into privileged pod %s on node %s", privilegedPod.Name, node.Name)
229+
}
230+
err = acnk8s.WaitForPodsRunning(ctx, v.clientset, "", "")
231+
if err != nil {
232+
return errors.Wrapf(err, "failed to wait for pods running")
233+
}
234+
}
235+
return nil
236+
}

test/validate/utils.go

+5-3
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,16 @@ import (
1111
)
1212

1313
func compareIPs(expected map[string]string, actual []string) error {
14-
if len(expected) != len(actual) {
15-
return errors.Errorf("len of expected IPs != len of actual IPs, expected: %+v, actual: %+v", expected, actual)
16-
}
14+
expectedLen := len(expected)
1715

1816
for _, ip := range actual {
1917
if _, ok := expected[ip]; !ok {
2018
return errors.Errorf("actual ip %s is unexpected, expected: %+v, actual: %+v", ip, expected, actual)
2119
}
20+
delete(expected, ip)
21+
}
22+
if expectedLen != len(actual) {
23+
return errors.Errorf("len of expected IPs != len of actual IPs, expected: %+v, actual: %+v | Remaining, potentially leaked, IP(s) on state file - %v", expectedLen, len(actual), expected)
2224
}
2325

2426
return nil

test/validate/validate.go

+26-57
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package validate
22

33
import (
44
"context"
5+
"encoding/json"
56
"log"
67

78
acnk8s "github.com/Azure/azure-container-networking/test/internal/kubernetes"
@@ -72,6 +73,10 @@ func CreateValidator(ctx context.Context, clientset *kubernetes.Clientset, confi
7273
switch os {
7374
case "windows":
7475
checks = windowsChecksMap[cni]
76+
err := acnk8s.RestartKubeProxyService(ctx, clientset, privilegedNamespace, privilegedLabelSelector, config)
77+
if err != nil {
78+
return nil, errors.Wrapf(err, "failed to restart kubeproxy")
79+
}
7580
case "linux":
7681
checks = linuxChecksMap[cni]
7782
default:
@@ -99,7 +104,7 @@ func (v *Validator) Validate(ctx context.Context) error {
99104
if v.os == "linux" {
100105
// We are restarting the systmemd network and checking that the connectivity works after the restart. For more details: https://github.com/cilium/cilium/issues/18706
101106
log.Printf("Validating the restart network scenario")
102-
err = v.ValidateRestartNetwork(ctx)
107+
err = v.validateRestartNetwork(ctx)
103108
if err != nil {
104109
return errors.Wrapf(err, "failed to validate restart network scenario")
105110
}
@@ -117,33 +122,6 @@ func (v *Validator) ValidateStateFile(ctx context.Context) error {
117122
return nil
118123
}
119124

120-
func (v *Validator) ValidateRestartNetwork(ctx context.Context) error {
121-
nodes, err := acnk8s.GetNodeList(ctx, v.clientset)
122-
if err != nil {
123-
return errors.Wrapf(err, "failed to get node list")
124-
}
125-
126-
for index := range nodes.Items {
127-
// get the privileged pod
128-
pod, err := acnk8s.GetPodsByNode(ctx, v.clientset, privilegedNamespace, privilegedLabelSelector, nodes.Items[index].Name)
129-
if err != nil {
130-
return errors.Wrapf(err, "failed to get privileged pod")
131-
}
132-
133-
privelegedPod := pod.Items[0]
134-
// exec into the pod to get the state file
135-
_, err = acnk8s.ExecCmdOnPod(ctx, v.clientset, privilegedNamespace, privelegedPod.Name, restartNetworkCmd, v.config)
136-
if err != nil {
137-
return errors.Wrapf(err, "failed to exec into privileged pod - %s", privelegedPod.Name)
138-
}
139-
err = acnk8s.WaitForPodsRunning(ctx, v.clientset, "", "")
140-
if err != nil {
141-
return errors.Wrapf(err, "failed to wait for pods running")
142-
}
143-
}
144-
return nil
145-
}
146-
147125
func (v *Validator) validateIPs(ctx context.Context, stateFileIps stateFileIpsFunc, cmd []string, checkType, namespace, labelSelector string) error {
148126
log.Printf("Validating %s state file", checkType)
149127
nodes, err := acnk8s.GetNodeListByLabelSelector(ctx, v.clientset, nodeSelectorMap[v.os])
@@ -157,6 +135,9 @@ func (v *Validator) validateIPs(ctx context.Context, stateFileIps stateFileIpsFu
157135
if err != nil {
158136
return errors.Wrapf(err, "failed to get privileged pod")
159137
}
138+
if len(pod.Items) == 0 {
139+
return errors.Errorf("there are no privileged pods on node - %v", nodes.Items[index].Name)
140+
}
160141
podName := pod.Items[0].Name
161142
// exec into the pod to get the state file
162143
result, err := acnk8s.ExecCmdOnPod(ctx, v.clientset, namespace, podName, cmd, v.config)
@@ -165,7 +146,7 @@ func (v *Validator) validateIPs(ctx context.Context, stateFileIps stateFileIpsFu
165146
}
166147
filePodIps, err := stateFileIps(result)
167148
if err != nil {
168-
return errors.Wrapf(err, "failed to get pod ips from state file")
149+
return errors.Wrapf(err, "failed to get pod ips from state file on node %v", nodes.Items[index].Name)
169150
}
170151
if len(filePodIps) == 0 && v.restartCase {
171152
log.Printf("No pods found on node %s", nodes.Items[index].Name)
@@ -175,7 +156,7 @@ func (v *Validator) validateIPs(ctx context.Context, stateFileIps stateFileIpsFu
175156
podIps := getPodIPsWithoutNodeIP(ctx, v.clientset, nodes.Items[index])
176157

177158
if err := compareIPs(filePodIps, podIps); err != nil {
178-
return errors.Wrapf(errors.New("State file validation failed"), "for %s on node %s", checkType, nodes.Items[index].Name)
159+
return errors.Wrapf(err, "State file validation failed for %s on node %s", checkType, nodes.Items[index].Name)
179160
}
180161
}
181162
log.Printf("State file validation for %s passed", checkType)
@@ -257,36 +238,24 @@ func (v *Validator) ValidateDualStackControlPlane(ctx context.Context) error {
257238
return nil
258239
}
259240

260-
func (v *Validator) RestartKubeProxyService(ctx context.Context) error {
261-
nodes, err := acnk8s.GetNodeList(ctx, v.clientset)
262-
if err != nil {
263-
return errors.Wrapf(err, "failed to get node list")
264-
}
265-
266-
for index := range nodes.Items {
267-
node := nodes.Items[index]
268-
if node.Status.NodeInfo.OperatingSystem != string(corev1.Windows) {
269-
continue
270-
}
271-
// get the privileged pod
272-
pod, err := acnk8s.GetPodsByNode(ctx, v.clientset, privilegedNamespace, privilegedLabelSelector, nodes.Items[index].Name)
273-
if err != nil {
274-
return errors.Wrapf(err, "failed to get privileged pod")
275-
}
276-
277-
privelegedPod := pod.Items[0]
278-
// exec into the pod and restart kubeproxy
279-
_, err = acnk8s.ExecCmdOnPod(ctx, v.clientset, privilegedNamespace, privelegedPod.Name, restartKubeProxyCmd, v.config)
280-
if err != nil {
281-
return errors.Wrapf(err, "failed to exec into privileged pod - %s", privelegedPod.Name)
282-
}
283-
}
284-
return nil
285-
}
286-
287241
func (v *Validator) Cleanup(ctx context.Context) {
288242
// deploy privileged pod
289243
privilegedDaemonSet := acnk8s.MustParseDaemonSet(privilegedDaemonSetPathMap[v.os])
290244
daemonsetClient := v.clientset.AppsV1().DaemonSets(privilegedNamespace)
291245
acnk8s.MustDeleteDaemonset(ctx, daemonsetClient, privilegedDaemonSet)
292246
}
247+
248+
func cnsCacheStateFileIps(result []byte) (map[string]string, error) {
249+
var cnsLocalCache CNSLocalCache
250+
251+
err := json.Unmarshal(result, &cnsLocalCache)
252+
if err != nil {
253+
return nil, errors.Wrapf(err, "failed to unmarshal cns local cache")
254+
}
255+
256+
cnsPodIps := make(map[string]string)
257+
for index := range cnsLocalCache.IPConfigurationStatus {
258+
cnsPodIps[cnsLocalCache.IPConfigurationStatus[index].IPAddress] = cnsLocalCache.IPConfigurationStatus[index].PodInfo.Name()
259+
}
260+
return cnsPodIps, nil
261+
}

0 commit comments

Comments
 (0)