e2e: add NodeLogQuery tests for Windows nodes
This commit is contained in:
		@@ -23,6 +23,7 @@ import (
 | 
				
			|||||||
	"io"
 | 
						"io"
 | 
				
			||||||
	"os/exec"
 | 
						"os/exec"
 | 
				
			||||||
	"path/filepath"
 | 
						"path/filepath"
 | 
				
			||||||
 | 
						"regexp"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -457,21 +458,32 @@ var _ = SIGDescribe("kubelet", func() {
 | 
				
			|||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Tests for NodeLogQuery feature
 | 
						// Tests for NodeLogQuery feature
 | 
				
			||||||
	f.Describe("kubectl get --raw \"/api/v1/nodes/<insert-node-name-here>/proxy/logs/?query=/<insert-log-file-name-here>", feature.NodeLogQuery, "[LinuxOnly]", func() {
 | 
						f.Describe("kubectl get --raw \"/api/v1/nodes/<insert-node-name-here>/proxy/logs/?query=/<insert-log-file-name-here>", feature.NodeLogQuery, func() {
 | 
				
			||||||
		var linuxNodeName string
 | 
							var linuxNodeName string
 | 
				
			||||||
 | 
							var windowsNodeName string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ginkgo.BeforeEach(func(ctx context.Context) {
 | 
							ginkgo.BeforeEach(func(ctx context.Context) {
 | 
				
			||||||
			nodes, err := e2enode.GetReadyNodesIncludingTainted(ctx, c)
 | 
								allNodes, err := e2enode.GetReadyNodesIncludingTainted(ctx, c)
 | 
				
			||||||
			framework.ExpectNoError(err)
 | 
								framework.ExpectNoError(err)
 | 
				
			||||||
			if len(nodes.Items) == 0 {
 | 
								if len(allNodes.Items) == 0 {
 | 
				
			||||||
				framework.Fail("Expected at least one node to be present")
 | 
									framework.Fail("Expected at least one node to be present")
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
								// Make a copy of the node list as getLinuxNodes will filter out the Windows nodes
 | 
				
			||||||
 | 
								nodes := allNodes.DeepCopy()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			linuxNodes := getLinuxNodes(nodes)
 | 
								linuxNodes := getLinuxNodes(nodes)
 | 
				
			||||||
			if len(linuxNodes.Items) == 0 {
 | 
								if len(linuxNodes.Items) == 0 {
 | 
				
			||||||
				framework.Fail("Expected at least one Linux node to be present")
 | 
									framework.Fail("Expected at least one Linux node to be present")
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
								linuxNodeName = linuxNodes.Items[0].Name
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								windowsNodes := getWindowsNodes(allNodes)
 | 
				
			||||||
 | 
								if len(windowsNodes.Items) == 0 {
 | 
				
			||||||
 | 
									framework.Logf("No Windows node found")
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									windowsNodeName = windowsNodes.Items[0].Name
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			linuxNodeName = nodes.Items[0].Name
 | 
					 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/*
 | 
							/*
 | 
				
			||||||
@@ -494,7 +506,7 @@ var _ = SIGDescribe("kubelet", func() {
 | 
				
			|||||||
		})
 | 
							})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/*
 | 
							/*
 | 
				
			||||||
			Test if kubectl get --raw "/api/v1/nodes/<insert-node-name-here>/proxy/logs/?query=kubelet"
 | 
								Test if kubectl get --raw "/api/v1/nodes/<insert-linux-node-name-here>/proxy/logs/?query=kubelet"
 | 
				
			||||||
			returns the kubelet logs
 | 
								returns the kubelet logs
 | 
				
			||||||
		*/
 | 
							*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -509,7 +521,7 @@ var _ = SIGDescribe("kubelet", func() {
 | 
				
			|||||||
		})
 | 
							})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/*
 | 
							/*
 | 
				
			||||||
			Test if kubectl get --raw "/api/v1/nodes/<insert-node-name-here>/proxy/logs/?query=kubelet&boot=0"
 | 
								Test if kubectl get --raw "/api/v1/nodes/<insert-linux-node-name-here>/proxy/logs/?query=kubelet&boot=0"
 | 
				
			||||||
			returns kubelet logs from the current boot
 | 
								returns kubelet logs from the current boot
 | 
				
			||||||
		*/
 | 
							*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -524,7 +536,7 @@ var _ = SIGDescribe("kubelet", func() {
 | 
				
			|||||||
		})
 | 
							})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/*
 | 
							/*
 | 
				
			||||||
			Test if kubectl get --raw "/api/v1/nodes/<insert-node-name-here>/proxy/logs/?query=kubelet&tailLines=3"
 | 
								Test if kubectl get --raw "/api/v1/nodes/<insert-linux-node-name-here>/proxy/logs/?query=kubelet&tailLines=3"
 | 
				
			||||||
			returns the last three lines of the kubelet log
 | 
								returns the last three lines of the kubelet log
 | 
				
			||||||
		*/
 | 
							*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -543,7 +555,7 @@ var _ = SIGDescribe("kubelet", func() {
 | 
				
			|||||||
		})
 | 
							})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/*
 | 
							/*
 | 
				
			||||||
			Test if kubectl get --raw "/api/v1/nodes/<insert-node-name-here>/proxy/logs/?query=kubelet&pattern=kubelet"
 | 
								Test if kubectl get --raw "/api/v1/nodes/<insert-linux-node-name-here>/proxy/logs/?query=kubelet&pattern=container"
 | 
				
			||||||
			returns kubelet logs for the current boot with the pattern container
 | 
								returns kubelet logs for the current boot with the pattern container
 | 
				
			||||||
		*/
 | 
							*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -562,8 +574,8 @@ var _ = SIGDescribe("kubelet", func() {
 | 
				
			|||||||
		})
 | 
							})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/*
 | 
							/*
 | 
				
			||||||
			Test if kubectl get --raw "/api/v1/nodes/<insert-node-name-here>/proxy/logs/?query=kubelet&sinceTime=<now>"
 | 
								Test if kubectl get --raw "/api/v1/nodes/<insert-linux-node-name-here>/proxy/logs/?query=kubelet&sinceTime=<now>"
 | 
				
			||||||
			returns the kubelet since the current date and time. This can be "-- No entries --" which is correct.
 | 
								returns the kubelet logs since the current date and time. This can be "-- No entries --" which is correct.
 | 
				
			||||||
		*/
 | 
							*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ginkgo.It("should return the kubelet logs since the current date and time", func() {
 | 
							ginkgo.It("should return the kubelet logs since the current date and time", func() {
 | 
				
			||||||
@@ -581,22 +593,93 @@ var _ = SIGDescribe("kubelet", func() {
 | 
				
			|||||||
				framework.Failf("Failed to receive the correct kubelet logs or the correct amount of lines of logs")
 | 
									framework.Failf("Failed to receive the correct kubelet logs or the correct amount of lines of logs")
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
								Test if kubectl get --raw "/api/v1/nodes/<insert-windows-node-name-here>/proxy/logs/?query="Microsoft-Windows-Security-SPP"
 | 
				
			||||||
 | 
								returns the Microsoft-Windows-Security-SPP log
 | 
				
			||||||
 | 
							*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ginkgo.It("should return the Microsoft-Windows-Security-SPP logs", func(ctx context.Context) {
 | 
				
			||||||
 | 
								if len(windowsNodeName) == 0 {
 | 
				
			||||||
 | 
									ginkgo.Skip("No Windows node found")
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								ginkgo.By("Starting the command")
 | 
				
			||||||
 | 
								tk := e2ekubectl.NewTestKubeconfig(framework.TestContext.CertDir, framework.TestContext.Host, framework.TestContext.KubeConfig, framework.TestContext.KubeContext, framework.TestContext.KubectlPath, ns)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								queryCommand := fmt.Sprintf("/api/v1/nodes/%s/proxy/logs/?query=Microsoft-Windows-Security-SPP", windowsNodeName)
 | 
				
			||||||
 | 
								cmd := tk.KubectlCmd("get", "--raw", queryCommand)
 | 
				
			||||||
 | 
								result := runKubectlCommand(cmd)
 | 
				
			||||||
 | 
								assertContains("ProviderName: Microsoft-Windows-Security-SPP", result)
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
								Test if kubectl get --raw "/api/v1/nodes/<insert-windows-node-name-here>/proxy/logs/?query=Microsoft-Windows-Security-SPP&tailLines=3"
 | 
				
			||||||
 | 
								returns the last three lines of the Microsoft-Windows-Security-SPP log
 | 
				
			||||||
 | 
							*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ginkgo.It("should return the last three lines of the Microsoft-Windows-Security-SPP logs", func(ctx context.Context) {
 | 
				
			||||||
 | 
								e2eskipper.SkipUnlessProviderIs(framework.ProvidersWithSSH...)
 | 
				
			||||||
 | 
								if len(windowsNodeName) == 0 {
 | 
				
			||||||
 | 
									ginkgo.Skip("No Windows node found")
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								ginkgo.By("Starting the command")
 | 
				
			||||||
 | 
								tk := e2ekubectl.NewTestKubeconfig(framework.TestContext.CertDir, framework.TestContext.Host, framework.TestContext.KubeConfig, framework.TestContext.KubeContext, framework.TestContext.KubectlPath, ns)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								queryCommand := fmt.Sprintf("/api/v1/nodes/%s/proxy/logs/?query=Microsoft-Windows-Security-SPP&tailLines=3", windowsNodeName)
 | 
				
			||||||
 | 
								cmd := tk.KubectlCmd("get", "--raw", queryCommand)
 | 
				
			||||||
 | 
								result := runKubectlCommand(cmd)
 | 
				
			||||||
 | 
								logs := getWinEventCommandOnNode(windowsNodeName, "Microsoft-Windows-Security-SPP", " -MaxEvents 3")
 | 
				
			||||||
 | 
								if trimSpaceNewlineInString(result) != trimSpaceNewlineInString(logs) {
 | 
				
			||||||
 | 
									framework.Failf("Failed to receive the correct Microsoft-Windows-Security-SPP logs or the correct amount of lines of logs")
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
								Test if kubectl get --raw "/api/v1/nodes/<insert-windows-node-name-here>/proxy/logs/?query=Microsoft-Windows-Security-SPP&pattern=Health"
 | 
				
			||||||
 | 
								returns the lines of the Microsoft-Windows-Security-SPP log with the pattern Health
 | 
				
			||||||
 | 
							*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ginkgo.It("should return the Microsoft-Windows-Security-SPP logs with the pattern Health", func(ctx context.Context) {
 | 
				
			||||||
 | 
								e2eskipper.SkipUnlessProviderIs(framework.ProvidersWithSSH...)
 | 
				
			||||||
 | 
								if len(windowsNodeName) == 0 {
 | 
				
			||||||
 | 
									ginkgo.Skip("No Windows node found")
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								ginkgo.By("Starting the command")
 | 
				
			||||||
 | 
								tk := e2ekubectl.NewTestKubeconfig(framework.TestContext.CertDir, framework.TestContext.Host, framework.TestContext.KubeConfig, framework.TestContext.KubeContext, framework.TestContext.KubectlPath, ns)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								queryCommand := fmt.Sprintf("/api/v1/nodes/%s/proxy/logs/?query=Microsoft-Windows-Security-SPP&pattern=Health", windowsNodeName)
 | 
				
			||||||
 | 
								cmd := tk.KubectlCmd("get", "--raw", queryCommand)
 | 
				
			||||||
 | 
								result := runKubectlCommand(cmd)
 | 
				
			||||||
 | 
								logs := getWinEventCommandOnNode(windowsNodeName, "Microsoft-Windows-Security-SPP", "  | Where-Object -Property Message -Match Health")
 | 
				
			||||||
 | 
								if trimSpaceNewlineInString(result) != trimSpaceNewlineInString(logs) {
 | 
				
			||||||
 | 
									framework.Failf("Failed to receive the correct Microsoft-Windows-Security-SPP logs or the correct amount of lines of logs")
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func getLinuxNodes(nodes *v1.NodeList) *v1.NodeList {
 | 
					func getLinuxNodes(nodes *v1.NodeList) *v1.NodeList {
 | 
				
			||||||
	e2enode.Filter(nodes, func(node v1.Node) bool {
 | 
						filteredNodes := nodes
 | 
				
			||||||
		return isLinuxNode(&node)
 | 
						e2enode.Filter(filteredNodes, func(node v1.Node) bool {
 | 
				
			||||||
 | 
							return isNode(&node, "linux")
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
	return nodes
 | 
						return filteredNodes
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func isLinuxNode(node *v1.Node) bool {
 | 
					func getWindowsNodes(nodes *v1.NodeList) *v1.NodeList {
 | 
				
			||||||
 | 
						filteredNodes := nodes
 | 
				
			||||||
 | 
						e2enode.Filter(filteredNodes, func(node v1.Node) bool {
 | 
				
			||||||
 | 
							return isNode(&node, "windows")
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						return filteredNodes
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func isNode(node *v1.Node, os string) bool {
 | 
				
			||||||
	if node == nil {
 | 
						if node == nil {
 | 
				
			||||||
		return false
 | 
							return false
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if os, found := node.Labels[v1.LabelOSStable]; found {
 | 
						if foundOS, found := node.Labels[v1.LabelOSStable]; found {
 | 
				
			||||||
		return (os == "linux")
 | 
							return (os == foundOS)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return false
 | 
						return false
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -631,10 +714,26 @@ func assertContains(expectedString string, result string) {
 | 
				
			|||||||
	framework.Failf("Failed to find \"%s\"", expectedString)
 | 
						framework.Failf("Failed to find \"%s\"", expectedString)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func journalctlCommandOnNode(nodeName string, args string) string {
 | 
					func commandOnNode(nodeName string, cmd string) string {
 | 
				
			||||||
	result, err := e2essh.NodeExec(context.Background(), nodeName,
 | 
						result, err := e2essh.NodeExec(context.Background(), nodeName, cmd, framework.TestContext.Provider)
 | 
				
			||||||
		"journalctl --utc --no-pager --output=short-precise "+args, framework.TestContext.Provider)
 | 
					 | 
				
			||||||
	framework.ExpectNoError(err)
 | 
						framework.ExpectNoError(err)
 | 
				
			||||||
	e2essh.LogResult(result)
 | 
						e2essh.LogResult(result)
 | 
				
			||||||
	return result.Stdout
 | 
						return result.Stdout
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func journalctlCommandOnNode(nodeName string, args string) string {
 | 
				
			||||||
 | 
						return commandOnNode(nodeName, "journalctl --utc --no-pager --output=short-precise "+args)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getWinEventCommandOnNode(nodeName string, providerName, args string) string {
 | 
				
			||||||
 | 
						output := commandOnNode(nodeName, "Get-WinEvent -FilterHashtable @{LogName='Application'; ProviderName='"+providerName+"'}"+args+" | Sort-Object TimeCreated | Format-Table -AutoSize -Wrap")
 | 
				
			||||||
 | 
						return output
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func trimSpaceNewlineInString(s string) string {
 | 
				
			||||||
 | 
						// Remove Windows newlines
 | 
				
			||||||
 | 
						re := regexp.MustCompile(` +\r?\n +`)
 | 
				
			||||||
 | 
						s = re.ReplaceAllString(s, "")
 | 
				
			||||||
 | 
						// Replace spaces to account for cases like "\r\n " that could lead to false negatives
 | 
				
			||||||
 | 
						return strings.ReplaceAll(s, " ", "")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user