Merge pull request #122594 from aravindhp/nlq-windows-e2e

e2e: add NodeLogQuery tests for Windows nodes
This commit is contained in:
Kubernetes Prow Robot 2024-02-05 21:41:27 -08:00 committed by GitHub
commit 2ec3ef62d6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -23,6 +23,7 @@ import (
"io"
"os/exec"
"path/filepath"
"regexp"
"strings"
"time"
@ -457,21 +458,32 @@ var _ = SIGDescribe("kubelet", func() {
})
// 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 windowsNodeName string
ginkgo.BeforeEach(func(ctx context.Context) {
nodes, err := e2enode.GetReadyNodesIncludingTainted(ctx, c)
allNodes, err := e2enode.GetReadyNodesIncludingTainted(ctx, c)
framework.ExpectNoError(err)
if len(nodes.Items) == 0 {
if len(allNodes.Items) == 0 {
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)
if len(linuxNodes.Items) == 0 {
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
*/
@ -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
*/
@ -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
*/
@ -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
*/
@ -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>"
returns the kubelet since the current date and time. This can be "-- No entries --" which is correct.
Test if kubectl get --raw "/api/v1/nodes/<insert-linux-node-name-here>/proxy/logs/?query=kubelet&sinceTime=<now>"
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() {
@ -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")
}
})
/*
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 {
e2enode.Filter(nodes, func(node v1.Node) bool {
return isLinuxNode(&node)
filteredNodes := nodes
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 {
return false
}
if os, found := node.Labels[v1.LabelOSStable]; found {
return (os == "linux")
if foundOS, found := node.Labels[v1.LabelOSStable]; found {
return (os == foundOS)
}
return false
}
@ -631,10 +714,26 @@ func assertContains(expectedString string, result string) {
framework.Failf("Failed to find \"%s\"", expectedString)
}
func journalctlCommandOnNode(nodeName string, args string) string {
result, err := e2essh.NodeExec(context.Background(), nodeName,
"journalctl --utc --no-pager --output=short-precise "+args, framework.TestContext.Provider)
func commandOnNode(nodeName string, cmd string) string {
result, err := e2essh.NodeExec(context.Background(), nodeName, cmd, framework.TestContext.Provider)
framework.ExpectNoError(err)
e2essh.LogResult(result)
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, " ", "")
}