tests: Check FQDN for external services on Windows

Windows does not support partially qualified domain names, which is why the test can fail.

Additionally, because nslookup may return 0 on Windows, even if the given DNS name was not
found, this issue was not observed until recently. We're now checking stderr as well.
This commit is contained in:
Claudiu Belu 2020-04-30 19:25:36 -07:00
parent 4ccfeb18d9
commit 2a07efe925
2 changed files with 29 additions and 7 deletions

View File

@ -901,10 +901,14 @@ func (j *TestJig) checkNodePortServiceReachability(svc *v1.Service, pod *v1.Pod)
// checkExternalServiceReachability ensures service of type externalName resolves to IP address and no fake externalName is set // checkExternalServiceReachability ensures service of type externalName resolves to IP address and no fake externalName is set
// FQDN of kubernetes is used as externalName(for air tight platforms). // FQDN of kubernetes is used as externalName(for air tight platforms).
func (j *TestJig) checkExternalServiceReachability(svc *v1.Service, pod *v1.Pod) error { func (j *TestJig) checkExternalServiceReachability(svc *v1.Service, pod *v1.Pod) error {
// NOTE(claudiub): Windows does not support PQDN.
svcName := fmt.Sprintf("%s.%s.svc.%s", svc.Name, svc.Namespace, framework.TestContext.ClusterDNSDomain)
// Service must resolve to IP // Service must resolve to IP
cmd := fmt.Sprintf("nslookup %s", svc.Name) cmd := fmt.Sprintf("nslookup %s", svcName)
_, err := framework.RunHostCmd(pod.Namespace, pod.Name, cmd) _, stderr, err := framework.RunHostCmdWithFullOutput(pod.Namespace, pod.Name, cmd)
if err != nil { // NOTE(claudiub): nslookup may return 0 on Windows, even though the DNS name was not found. In this case,
// we can check stderr for the error.
if err != nil || (framework.NodeOSDistroIs("windows") && strings.Contains(stderr, fmt.Sprintf("can't find %s", svcName))) {
return fmt.Errorf("ExternalName service %q must resolve to IP", pod.Namespace+"/"+pod.Name) return fmt.Errorf("ExternalName service %q must resolve to IP", pod.Namespace+"/"+pod.Name)
} }
return nil return nil

View File

@ -588,13 +588,19 @@ func isTimeout(err error) bool {
// Exec runs the kubectl executable. // Exec runs the kubectl executable.
func (b KubectlBuilder) Exec() (string, error) { func (b KubectlBuilder) Exec() (string, error) {
stdout, _, err := b.ExecWithFullOutput()
return stdout, err
}
// ExecWithFullOutput runs the kubectl executable, and returns the stdout and stderr.
func (b KubectlBuilder) ExecWithFullOutput() (string, string, error) {
var stdout, stderr bytes.Buffer var stdout, stderr bytes.Buffer
cmd := b.cmd cmd := b.cmd
cmd.Stdout, cmd.Stderr = &stdout, &stderr cmd.Stdout, cmd.Stderr = &stdout, &stderr
Logf("Running '%s %s'", cmd.Path, strings.Join(cmd.Args[1:], " ")) // skip arg[0] as it is printed separately Logf("Running '%s %s'", cmd.Path, strings.Join(cmd.Args[1:], " ")) // skip arg[0] as it is printed separately
if err := cmd.Start(); err != nil { if err := cmd.Start(); err != nil {
return "", fmt.Errorf("error starting %v:\nCommand stdout:\n%v\nstderr:\n%v\nerror:\n%v", cmd, cmd.Stdout, cmd.Stderr, err) return "", "", fmt.Errorf("error starting %v:\nCommand stdout:\n%v\nstderr:\n%v\nerror:\n%v", cmd, cmd.Stdout, cmd.Stderr, err)
} }
errCh := make(chan error, 1) errCh := make(chan error, 1)
go func() { go func() {
@ -608,18 +614,18 @@ func (b KubectlBuilder) Exec() (string, error) {
rc = int(ee.Sys().(syscall.WaitStatus).ExitStatus()) rc = int(ee.Sys().(syscall.WaitStatus).ExitStatus())
Logf("rc: %d", rc) Logf("rc: %d", rc)
} }
return stdout.String(), uexec.CodeExitError{ return stdout.String(), stderr.String(), uexec.CodeExitError{
Err: fmt.Errorf("error running %v:\nCommand stdout:\n%v\nstderr:\n%v\nerror:\n%v", cmd, cmd.Stdout, cmd.Stderr, err), Err: fmt.Errorf("error running %v:\nCommand stdout:\n%v\nstderr:\n%v\nerror:\n%v", cmd, cmd.Stdout, cmd.Stderr, err),
Code: rc, Code: rc,
} }
} }
case <-b.timeout: case <-b.timeout:
b.cmd.Process.Kill() b.cmd.Process.Kill()
return "", fmt.Errorf("timed out waiting for command %v:\nCommand stdout:\n%v\nstderr:\n%v", cmd, cmd.Stdout, cmd.Stderr) return "", "", fmt.Errorf("timed out waiting for command %v:\nCommand stdout:\n%v\nstderr:\n%v", cmd, cmd.Stdout, cmd.Stderr)
} }
Logf("stderr: %q", stderr.String()) Logf("stderr: %q", stderr.String())
Logf("stdout: %q", stdout.String()) Logf("stdout: %q", stdout.String())
return stdout.String(), nil return stdout.String(), stderr.String(), nil
} }
// RunKubectlOrDie is a convenience wrapper over kubectlBuilder // RunKubectlOrDie is a convenience wrapper over kubectlBuilder
@ -632,6 +638,12 @@ func RunKubectl(namespace string, args ...string) (string, error) {
return NewKubectlCommand(namespace, args...).Exec() return NewKubectlCommand(namespace, args...).Exec()
} }
// RunKubectlWithFullOutput is a convenience wrapper over kubectlBuilder
// It will also return the command's stderr.
func RunKubectlWithFullOutput(namespace string, args ...string) (string, string, error) {
return NewKubectlCommand(namespace, args...).ExecWithFullOutput()
}
// RunKubectlOrDieInput is a convenience wrapper over kubectlBuilder that takes input to stdin // RunKubectlOrDieInput is a convenience wrapper over kubectlBuilder that takes input to stdin
func RunKubectlOrDieInput(namespace string, data string, args ...string) string { func RunKubectlOrDieInput(namespace string, data string, args ...string) string {
return NewKubectlCommand(namespace, args...).WithStdinData(data).ExecOrDie(namespace) return NewKubectlCommand(namespace, args...).WithStdinData(data).ExecOrDie(namespace)
@ -1043,6 +1055,12 @@ func RunHostCmd(ns, name, cmd string) (string, error) {
return RunKubectl(ns, "exec", fmt.Sprintf("--namespace=%v", ns), name, "--", "/bin/sh", "-x", "-c", cmd) return RunKubectl(ns, "exec", fmt.Sprintf("--namespace=%v", ns), name, "--", "/bin/sh", "-x", "-c", cmd)
} }
// RunHostCmdWithFullOutput runs the given cmd in the context of the given pod using `kubectl exec`
// inside of a shell. It will also return the command's stderr.
func RunHostCmdWithFullOutput(ns, name, cmd string) (string, string, error) {
return RunKubectlWithFullOutput(ns, "exec", fmt.Sprintf("--namespace=%v", ns), name, "--", "/bin/sh", "-x", "-c", cmd)
}
// RunHostCmdOrDie calls RunHostCmd and dies on error. // RunHostCmdOrDie calls RunHostCmd and dies on error.
func RunHostCmdOrDie(ns, name, cmd string) string { func RunHostCmdOrDie(ns, name, cmd string) string {
stdout, err := RunHostCmd(ns, name, cmd) stdout, err := RunHostCmd(ns, name, cmd)