Merge pull request #76507 from bclau/tests/agnhost-updates

tests: agnhost image updates
k3s-v1.15.3
Kubernetes Prow Robot 2019-04-30 14:10:58 -07:00 committed by GitHub
commit afde45abfa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 202 additions and 26 deletions

View File

@ -0,0 +1,79 @@
# Agnhost
## Overview
There are significant differences between Linux and Windows, especially in the way
something can be obtained or tested. For example, the DNS suffix list can be found in
`/etc/resolv.conf` on Linux, but on Windows, such file does not exist, the same
information could retrieved through other means. To combat those differences,
`agnhost` was created.
`agnhost` is an extendable CLI that behaves and outputs the same expected content,
no matter the underlying OS. The name itself reflects this idea, being a portmanteau
word of the words agnost and host.
The image was created for testing purposes, reducing the need for having different test
cases for the same tested behaviour.
## Usage
The `agnhost` binary is a CLI with the following subcommands:
- `dns-suffix`: It will output the host's configured DNS suffix list, separated by commas.
- `dns-server-list`: It will output the host's configured DNS servers, separated by commas.
- `etc-hosts`: It will output the contents of host's `hosts` file. This file's location
is `/etc/hosts` on Linux, while on Windows it is `C:/Windows/System32/drivers/etc/hosts`.
- `pause`: It will pause the execution of the binary. This can be used for containers
which have to be kept in a `Running` state for various purposes, including executing
other `agnhost` commands.
- `help`: Prints the binary's help menu. Additionally, it can be followed by another
subcommand in order to get more information about that subcommand, including its
possible arguments.
For example, let's consider the following `pod.yaml` file:
```yaml
apiVersion: v1
kind: Pod
metadata:
name: test-agnhost
spec:
containers:
- args:
- dns-suffix
image: gcr.io/kubernetes-e2e-test-images/agnhost:1.0
name: agnhost
dnsConfig:
nameservers:
- 1.1.1.1
searches:
- resolv.conf.local
dnsPolicy: None
```
After we've used it to create a pod:
```console
kubectl create -f pod.yaml
```
We can then check the container's output to see what is DNS suffix list the Pod was
configured with:
```console
kubectl logs pod/test-agnhost
```
The output will be `resolv.conf.local`, as expected. Alternatively, the Pod could be
created with the `pause` argument instead, allowing us execute multiple commands:
```console
kubectl exec test-agnhost -- /agnhost dns-suffix
kubectl exec test-agnhost -- /agnhost dns-server-list
```
## Image
The image can be found at `gcr.io/kubernetes-e2e-test-images/agnhost:1.0` for Linux
containers, and `e2eteam/agnhost:1.0` for Windows containers. In the future, the same
repository can be used for both OSes.

View File

@ -23,13 +23,40 @@ import (
func main() {
cmdDNSSuffix := &cobra.Command{
Use: "dns-suffix",
Short: "Prints the host's DNS suffix list.",
Long: `prints the DNS suffixes of this host.`,
Short: "Prints the host's DNS suffix list",
Long: `Prints the DNS suffixes of this host.`,
Args: cobra.MaximumNArgs(0),
Run: printDNSSuffixList,
}
cmdDNSServerList := &cobra.Command{
Use: "dns-server-list",
Short: "Prints the host's DNS Server list",
Long: `Prints the DNS Server list of this host.`,
Args: cobra.MaximumNArgs(0),
Run: printDNSServerList,
}
cmdEtcHosts := &cobra.Command{
Use: "etc-hosts",
Short: "Prints the host's /etc/hosts file",
Long: `Prints the "hosts" file of this host."`,
Args: cobra.MaximumNArgs(0),
Run: printHostsFile,
}
cmdPause := &cobra.Command{
Use: "pause",
Short: "Pauses",
Long: `Pauses the execution. Useful for keeping the containers running, so other commands can be executed.`,
Args: cobra.MaximumNArgs(0),
Run: pause,
}
rootCmd := &cobra.Command{Use: "app"}
rootCmd.AddCommand(cmdDNSSuffix)
rootCmd.AddCommand(cmdDNSServerList)
rootCmd.AddCommand(cmdEtcHosts)
rootCmd.AddCommand(cmdPause)
rootCmd.Execute()
}

View File

@ -17,8 +17,14 @@ limitations under the License.
package main
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"os/exec"
"os/signal"
"strings"
"syscall"
"github.com/spf13/cobra"
)
@ -27,3 +33,58 @@ func printDNSSuffixList(cmd *cobra.Command, args []string) {
dnsSuffixList := getDNSSuffixList()
fmt.Println(strings.Join(dnsSuffixList, ","))
}
func printDNSServerList(cmd *cobra.Command, args []string) {
dnsServerList := getDNSServerList()
fmt.Println(strings.Join(dnsServerList, ","))
}
func printHostsFile(cmd *cobra.Command, args []string) {
fmt.Println(readFile(etcHostsFile))
}
func pause(cmd *cobra.Command, args []string) {
sigCh := make(chan os.Signal)
done := make(chan int, 1)
signal.Notify(sigCh, syscall.SIGINT)
signal.Notify(sigCh, syscall.SIGTERM)
signal.Notify(sigCh, syscall.SIGKILL)
go func() {
sig := <-sigCh
switch sig {
case syscall.SIGINT:
done <- 1
os.Exit(1)
case syscall.SIGTERM:
done <- 2
os.Exit(2)
case syscall.SIGKILL:
done <- 0
os.Exit(0)
}
}()
result := <-done
fmt.Printf("exiting %d\n", result)
}
func readFile(fileName string) string {
fileData, err := ioutil.ReadFile(fileName)
if err != nil {
panic(err)
}
return string(fileData)
}
func runCommand(name string, arg ...string) string {
var out bytes.Buffer
cmd := exec.Command(name, arg...)
cmd.Stdout = &out
err := cmd.Run()
if err != nil {
panic(err)
}
return strings.TrimSpace(out.String())
}

View File

@ -19,22 +19,18 @@ limitations under the License.
package main
import (
"io/ioutil"
"strings"
)
func getDNSSuffixList() []string {
const etcHostsFile = "/etc/hosts"
// A /etc/resolv.conf file managed by kubelet looks like this:
// nameserver DNS_CLUSTER_IP
// search test-dns.svc.cluster.local svc.cluster.local cluster.local q53aahaikqaehcai3ylfqdtc5b.bx.internal.cloudapp.net
// options ndots:5
fileData, err := ioutil.ReadFile("/etc/resolv.conf")
if err != nil {
panic(err)
}
lines := strings.Split(string(fileData), "\n")
func getDNSSuffixList() []string {
fileData := readFile("/etc/resolv.conf")
lines := strings.Split(fileData, "\n")
for _, line := range lines {
if strings.HasPrefix(line, "search") {
// omit the starting "search".
@ -44,3 +40,16 @@ func getDNSSuffixList() []string {
panic("Could not find DNS search list!")
}
func getDNSServerList() []string {
fileData := readFile("/etc/resolv.conf")
lines := strings.Split(fileData, "\n")
for _, line := range lines {
if strings.HasPrefix(line, "nameserver") {
// omit the starting "nameserver".
return strings.Split(line, " ")[1:]
}
}
panic("Could not find DNS search list!")
}

View File

@ -17,25 +17,25 @@ limitations under the License.
package main
import (
"bytes"
"os/exec"
"strings"
)
const etcHostsFile = "C:/Windows/System32/drivers/etc/hosts"
func getDNSSuffixList() []string {
var out bytes.Buffer
cmd := exec.Command("powershell", "-Command", "(Get-DnsClient)[0].SuffixSearchList")
cmd.Stdout = &out
err := cmd.Run()
if err != nil {
panic(err)
}
output := strings.TrimSpace(out.String())
output := runCommand("powershell", "-Command", "(Get-DnsClient)[0].SuffixSearchList")
if len(output) > 0 {
return strings.Split(output, "\r\n")
}
panic("Could not find DNS search list!")
}
func getDNSServerList() []string {
output := runCommand("powershell", "-Command", "(Get-DnsClientServerAddress).ServerAddresses")
if len(output) > 0 {
return strings.Split(output, "\r\n")
}
panic("Could not find DNS Server list!")
}