diff --git a/test/e2e/framework/service/jig.go b/test/e2e/framework/service/jig.go index 72c2c5dd7c2..419745ad4be 100644 --- a/test/e2e/framework/service/jig.go +++ b/test/e2e/framework/service/jig.go @@ -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 // FQDN of kubernetes is used as externalName(for air tight platforms). 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 - cmd := fmt.Sprintf("nslookup %s", svc.Name) - _, err := framework.RunHostCmd(pod.Namespace, pod.Name, cmd) - if err != nil { + cmd := fmt.Sprintf("nslookup %s", svcName) + _, stderr, err := framework.RunHostCmdWithFullOutput(pod.Namespace, pod.Name, cmd) + // 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 nil diff --git a/test/e2e/framework/util.go b/test/e2e/framework/util.go index b4e2d33a847..3ed9060ce5d 100644 --- a/test/e2e/framework/util.go +++ b/test/e2e/framework/util.go @@ -588,13 +588,19 @@ func isTimeout(err error) bool { // Exec runs the kubectl executable. 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 cmd := b.cmd 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 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) go func() { @@ -608,18 +614,18 @@ func (b KubectlBuilder) Exec() (string, error) { rc = int(ee.Sys().(syscall.WaitStatus).ExitStatus()) 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), Code: rc, } } case <-b.timeout: 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("stdout: %q", stdout.String()) - return stdout.String(), nil + return stdout.String(), stderr.String(), nil } // RunKubectlOrDie is a convenience wrapper over kubectlBuilder @@ -632,6 +638,12 @@ func RunKubectl(namespace string, args ...string) (string, error) { 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 func RunKubectlOrDieInput(namespace string, data string, args ...string) string { 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) } +// 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. func RunHostCmdOrDie(ns, name, cmd string) string { stdout, err := RunHostCmd(ns, name, cmd)