1# Agnhost 2 3## Overview 4 5There are significant differences between Linux and Windows, especially in the way 6something can be obtained or tested. For example, the DNS suffix list can be found in 7`/etc/resolv.conf` on Linux, but on Windows, such file does not exist, the same 8information could retrieved through other means. To combat those differences, 9`agnhost` was created. 10 11`agnhost` is an extendable CLI that behaves and outputs the same expected content, 12no matter the underlying OS. The name itself reflects this idea, being a portmanteau 13word of the words agnostic and host. 14 15The image was created for testing purposes, reducing the need for having different test 16cases for the same tested behaviour. 17 18 19## Developer notes 20 21We've introduced versioning into the `agnhost` binary for debugging purposes (e.g.: if the 22image and binary versions do not match, see [here](https://github.com/kubernetes/kubernetes/pull/79667#discussion_r304198370)). 23 24Whenever the image `VERSION` is bumped, the `Version` in `agnhost.go` will also have to be bumped. 25 26 27## Usage 28 29The `agnhost` binary has several subcommands which are can be used to test different 30Kubernetes features; their behaviour and output is not affected by the underlying OS. 31 32For example, let's consider the following `pod.yaml` file: 33 34```yaml 35 apiVersion: v1 36 kind: Pod 37 metadata: 38 name: test-agnhost 39 spec: 40 containers: 41 - args: 42 - dns-suffix 43 image: k8s.gcr.io/e2e-test-images/agnhost:2.14 44 name: agnhost 45 dnsConfig: 46 nameservers: 47 - 1.1.1.1 48 searches: 49 - resolv.conf.local 50 dnsPolicy: None 51``` 52 53After we've used it to create a pod: 54 55```console 56 kubectl create -f pod.yaml 57``` 58 59We can then check the container's output to see what is DNS suffix list the Pod was 60configured with: 61 62```console 63 kubectl logs pod/test-agnhost 64``` 65 66The output will be `resolv.conf.local`, as expected. Alternatively, the Pod could be 67created with the `pause` argument instead, allowing us execute multiple commands: 68 69```console 70 kubectl exec test-agnhost -- /agnhost dns-suffix 71 kubectl exec test-agnhost -- /agnhost dns-server-list 72``` 73 74The `agnhost` binary is a CLI with the following subcommands: 75 76 77### audit-proxy 78 79The audit proxy is used to test dynamic auditing. It listens on port 8080 for incoming audit 80events and writes them in a uniform manner to stdout. 81 82Usage: 83 84```console 85 kubectl exec test-agnhost -- /agnhost audit-proxy 86``` 87 88 89### connect 90 91Tries to open a TCP or SCTP connection to the given host and port. On error it 92prints an error message prefixed with a specific fixed string that 93test cases can check for: 94 95* `UNKNOWN` - Generic/unknown (non-network) error (eg, bad arguments) 96* `TIMEOUT` - The connection attempt timed out 97* `DNS` - An error in DNS resolution 98* `REFUSED` - Connection refused 99* `OTHER` - Other networking error (eg, "no route to host") 100 101(Theoretically it would be nicer for it to distinguish these by exit 102code, but it's much easier for test programs to compare strings in the 103output than to check the exit code.) 104 105Usage: 106 107```console 108 kubectl exec test-agnhost -- /agnhost connect [--timeout=<duration>] [--protocol=<protocol>] <host>:<port> 109``` 110 111The optional `--protocol` parameter can be set to `sctp` to test SCTP 112connections. The default value is `tcp`. 113 114### crd-conversion-webhook 115 116The subcommand tests `CustomResourceConversionWebhook`. After deploying it to Kubernetes cluster, 117the administrator needs to create a `CustomResourceConversion.Webhook` in Kubernetes cluster 118to use remote webhook for conversions. 119 120The subcommand starts a HTTP server, listening on port 443, and creating the `/crdconvert` 121endpoint. 122 123Usage 124 125```console 126 kubectl exec test-agnhost -- /agnhost crd-conversion-webhook \ 127 [--tls-cert-file <tls-cert-file>] [--tls-private-key-file <tls-private-key-file>] 128``` 129 130 131### dns-server-list 132 133It will output the host's configured DNS servers, separated by commas. 134 135Usage: 136 137```console 138 kubectl exec test-agnhost -- /agnhost dns-server-list 139``` 140 141 142### dns-suffix 143 144It will output the host's configured DNS suffix list, separated by commas. 145 146Usage: 147 148```console 149 kubectl exec test-agnhost -- /agnhost dns-suffix 150``` 151 152 153### entrypoint-tester 154 155This subcommand will print the arguments it's passed and exists. 156 157Usage: 158 159```console 160 kubectl exec test-agnhost -- /agnhost entrypoint-tester foo lish args 161``` 162 163 164### etc-hosts 165 166It will output the contents of host's `hosts` file. This file's location is `/etc/hosts` 167on Linux, while on Windows it is `C:/Windows/System32/drivers/etc/hosts`. 168 169Usage: 170 171```console 172 kubectl exec test-agnhost -- /agnhost etc-hosts 173``` 174 175 176### fake-gitserver 177 178Fakes a git server. When doing `git clone http://localhost:8000`, you will clone an empty git 179repo named `localhost` on local. You can also use `git clone http://localhost:8000 my-repo-name` to 180rename that repo. Access to the service with the backing pod will show you below information. 181 182```console 183curl -w "\n" http://localhost:8000 184I am a fake git server 185``` 186 187Usage: 188 189```console 190 kubectl exec test-agnhost -- /agnhost fake-gitserver 191``` 192 193 194### guestbook 195 196Starts a HTTP server on the given `--http-port` (default: 80), serving various endpoints representing a 197guestbook app. The endpoints and their purpose are: 198 199- `/register`: A guestbook replica will subscribe to a primary, to its given `--replicaof` endpoint. The primary 200 will then push any updates it receives to its registered replicas through the `--backend-port` (default: 6379). 201- `/get`: Returns `{"data": value}`, where the `value` is the stored value for the given `key` if non-empty, 202 or the entire store. 203- `/set`: Will set the given key-value pair in its own store and propagate it to its replicas, if any. 204 Will return `{"data": "Updated"}` to the caller on success. 205- `/guestbook`: Will proxy the request to `agnhost-primary` if the given `cmd` is `set`, or `agnhost-replica` 206 if the given `cmd` is `get`. 207 208Usage: 209 210```console 211guestbook="test/e2e/testing-manifests/guestbook" 212sed_expr="s|{{.AgnhostImage}}|k8s.gcr.io/e2e-test-images/agnhost:2.14|" 213 214# create the services. 215kubectl create -f ${guestbook}/frontend-service.yaml 216kubectl create -f ${guestbook}/agnhost-primary-service.yaml 217kubectl create -f ${guestbook}/agnhost-replica-service.yaml 218 219# create the deployments. 220cat ${guestbook}/frontend-deployment.yaml.in | sed ${sed_expr} | kubectl create -f - 221cat ${guestbook}/agnhost-primary-deployment.yaml.in | sed ${sed_expr} | kubectl create -f - 222cat ${guestbook}/agnhost-replica-deployment.yaml.in | sed ${sed_expr} | kubectl create -f - 223``` 224 225 226### help 227 228Prints the binary's help menu. Additionally, it can be followed by another subcommand 229in order to get more information about that subcommand, including its possible arguments. 230 231Usage: 232 233```console 234 kubectl exec test-agnhost -- /agnhost help 235``` 236 237 238### inclusterclient 239 240The subcommand will periodically poll the Kubernetes `/healthz` endpoint using the in-cluster 241config. Because of this, the subcommand is meant to be run inside of a Kubernetes pod. It can 242also be used to validate token rotation. 243 244The given `--poll-interval` flag (default is 30 seconds) represents the poll interval in 245seconds of the call to `/healhz`. 246 247Usage: 248 249```console 250 kubectl exec test-agnhost -- /agnhost inclusterclient [--poll-interval <poll-interval>] 251``` 252 253 254### liveness 255 256Starts a simple server that is alive for 10 seconds, then reports unhealthy for the rest 257of its (hopefully) short existence. 258 259Usage: 260 261```console 262 kubectl exec test-agnhost -- /agnhost liveness 263``` 264 265 266### logs-generator 267 268The `logs-generator` subcommand is a tool to create predictable load on the logs delivery system. 269It generates random lines with predictable format and predictable average length. 270Each line can be later uniquely identified to ensure logs delivery. 271 272Tool is parametrized with the total number of number that should be generated and the duration of 273the generation process. For example, if you want to create a throughput of 100 lines per second 274for a minute, you set total number of lines to 6000 and duration to 1 minute. 275 276Parameters are passed through CLI flags. There are no defaults, you should always pass the flags 277to the container. Total number of line is parametrized through the flag `--log-lines-total` 278and duration in go format is parametrized through the flag `--run-duration`. 279 280Inside the container all log lines are written to the stdout. 281 282Each line is on average 100 bytes long and follows this pattern: 283 284``` 2852000-12-31T12:59:59Z <id> <method> /api/v1/namespaces/<namespace>/endpoints/<random_string> <random_number> 286``` 287 288Where `<id>` refers to the number from 0 to `total_lines - 1`, which is unique for each 289line in a given run of the container. 290 291Examples: 292 293```console 294docker run -i \ 295 k8s.gcr.io/e2e-test-images/agnhost:2.29 \ 296 logs-generator --log-lines-total 10 --run-duration 1s 297``` 298 299```console 300kubectl run logs-generator \ 301 --generator=run-pod/v1 \ 302 --image=k8s.gcr.io/e2e-test-images/agnhost:2.29 \ 303 --restart=Never \ 304 -- logs-generator -t 10 -d 1s 305``` 306 307### mounttest 308 309The `mounttest` subcommand can be used to create files with various permissions, read files, 310and output file system type, mode, owner, and permissions for any given file. 311 312The subcommand can accept the following flags: 313 314- `fs_type`: Path to print the FS type for. 315- `file_mode`: Path to print the mode bits of. 316- `file_perm`: Path to print the perms of. 317- `file_owner`: Path to print the owning UID and GID of. 318- `new_file_0644`: Path to write to and read from with perm 0644. 319- `new_file_0666`: Path to write to and read from with perm 0666. 320- `new_file_0660`: Path to write to and read from with perm 0660. 321- `new_file_0777`: Path to write to and read from with perm 0777. 322- `file_content`: Path to read the file content from. 323- `file_content_in_loop`: Path to read the file content in loop from. 324- `retry_time` (default: 180): Retry time during the loop. 325- `break_on_expected_content` (default: true): Break out of loop on expected content (use with `--file_content_in_loop` flag only). 326 327Usage: 328 329```console 330 kubectl exec test-agnhost -- /agnhost mounttest \ 331 [--fs_type <path>] [--file_mode <path>] [--file_perm <path>] [--file_owner <path>] \ 332 [--new_file_0644 <path>] [--new_file_0666 <path>] [--new_file_0660 <path>] [--new_file_0777 <path>] \ 333 [--file_content <path>] [--file_content_in_loop <path>] \ 334 [--retry_time <seconds>] [--break_on_expected_content <true_or_false>] 335``` 336 337 338### net 339 340The goal of this Go project is to consolidate all low-level 341network testing "daemons" into one place. In network testing we 342frequently have need of simple daemons (common/Runner) that perform 343some "trivial" set of actions on a socket. 344 345Usage: 346 347* A package for each general area that is being tested, for example 348 `nat/` will contain Runners that test various NAT features. 349* Every runner should be registered via `main.go:makeRunnerMap()`. 350* Runners receive a JSON options structure as to their configuration. `Run()` 351 should return the disposition of the test. 352 353Runners can be executed into two different ways, either through the 354command-line or via an HTTP request: 355 356Command-line: 357 358```console 359 kubectl exec test-agnhost -- /agnhost net --runner <runner> --options <json> 360 kubectl exec test-agnhost -- /agnhost net \ 361 --runner nat-closewait-client \ 362 --options '{"RemoteAddr":"127.0.0.1:9999"}' 363``` 364 365HTTP server: 366 367```console 368 kubectl exec test-agnhost -- /agnhost net --serve :8889 369 kubectl exec test-agnhost -- curl -v -X POST localhost:8889/run/nat-closewait-server \ 370 -d '{"LocalAddr":"127.0.0.1:9999"}' 371``` 372 373### netexec 374 375Starts a HTTP(S) server on given port with the following endpoints: 376 377- `/`: Returns the request's timestamp. 378- `/clientip`: Returns the request's IP address. 379- `/dial`: Creates a given number of requests to the given host and port using the given protocol, 380 and returns a JSON with the fields `responses` (successful request responses) and `errors` ( 381 failed request responses). Returns `200 OK` status code if the last request succeeded, 382 `417 Expectation Failed` if it did not, or `400 Bad Request` if any of the endpoint's parameters 383 is invalid. The endpoint's parameters are: 384 - `host`: The host that will be dialed. 385 - `port`: The port that will be dialed. 386 - `request`: The HTTP endpoint or data to be sent through UDP. If not specified, it will result 387 in a `400 Bad Request` status code being returned. 388 - `protocol`: The protocol which will be used when making the request. Default value: `http`. 389 Acceptable values: `http`, `udp`, `sctp`. 390 - `tries`: The number of times the request will be performed. Default value: `1`. 391- `/echo`: Returns the given `msg` (`/echo?msg=echoed_msg`), with the optional status `code`. 392- `/exit`: Closes the server with the given code and graceful shutdown. The endpoint's parameters 393 are: 394 - `code`: The exit code for the process. Default value: 0. Allows an integer [0-127]. 395 - `timeout`: The amount of time to wait for connections to close before shutting down. 396 Acceptable values are golang durations. If 0 the process will exit immediately without 397 shutdown. 398 - `wait`: The amount of time to wait before starting shutdown. Acceptable values are 399 golang durations. If 0 the process will start shutdown immediately. 400- `/healthz`: Returns `200 OK` if the server is ready, `412 Status Precondition Failed` 401 otherwise. The server is considered not ready if the UDP server did not start yet or 402 it exited. 403- `/hostname`: Returns the server's hostname. 404- `/hostName`: Returns the server's hostname. 405- `/redirect`: Returns a redirect response to the given `location`, with the optional status `code` 406 (`/redirect?location=/echo%3Fmsg=foobar&code=307`). 407- `/shell`: Executes the given `shellCommand` or `cmd` (`/shell?cmd=some-command`) and 408 returns a JSON containing the fields `output` (command's output) and `error` (command's 409 error message). Returns `200 OK` if the command succeeded, `417 Expectation Failed` if not. 410- `/shutdown`: Closes the server with the exit code 0. 411- `/upload`: Accepts a file to be uploaded, writing it in the `/uploads` folder on the host. 412 Returns a JSON with the fields `output` (containing the file's name on the server) and 413 `error` containing any potential server side errors. 414 415If `--tls-cert-file` is added (ideally in conjunction with `--tls-private-key-file`, the HTTP server 416will be upgraded to HTTPS. The image has default, `localhost`-based cert/privkey files at 417`/localhost.crt` and `/localhost.key` (see: [`porter` subcommand](#porter)) 418 419If `--http-override` is set, the HTTP(S) server will always serve the override path & options, 420ignoring the request URL. 421 422It will also start a UDP server on the indicated UDP port that responds to the following commands: 423 424- `hostname`: Returns the server's hostname 425- `echo <msg>`: Returns the given `<msg>` 426- `clientip`: Returns the request's IP address 427 428Additionally, if (and only if) `--sctp-port` is passed, it will start an SCTP server on that port, 429responding to the same commands as the UDP server. 430 431Usage: 432 433```console 434 kubectl exec test-agnhost -- /agnhost netexec [--http-port <http-port>] [--udp-port <udp-port>] [--sctp-port <sctp-port>] [--tls-cert-file <cert-file>] [--tls-private-key-file <privkey-file>] 435``` 436 437### nettest 438 439A tiny web server for checking networking connectivity. 440 441Will dial out to, and expect to hear from, every pod that is a member of the service 442passed in the flag `--service`. 443 444Will serve a webserver on given `--port`, and will create the following endpoints: 445 446- `/read`: to see the current state, or `/quit` to shut down. 447 448- `/status`: to see `pass/running/fail` determination. (literally, it will return 449one of those words.) 450 451- `/write`: is used by other network test pods to register connectivity. 452 453Usage: 454 455```console 456 kubectl exec test-agnhost -- /agnhost nettest [--port <port>] [--peers <peers>] [--service <service>] [--namespace <namespace>] [--delay-shutdown <delay>] 457``` 458 459 460### no-snat-test 461 462The subcommand requires the following environment variables to be set, and they should be 463valid IP addresses: 464 465- `POD_IP` 466- `NODE_IP` 467 468Serves the following endpoints on the given port (defaults to `8080`). 469 470- `/whoami` - returns the request's IP address. 471- `/checknosnat` - queries `ip/whoami` for each provided IP (`/checknosnat?ips=ip1,ip2`), 472 and if all the response bodies match the `POD_IP`, it will return a 200 response, 500 otherwise. 473 474Usage: 475 476```console 477 kubectl run test-agnhost \ 478 --generator=run-pod/v1 \ 479 --image=k8s.gcr.io/e2e-test-images/agnhost:2.14 \ 480 --restart=Never \ 481 --env "POD_IP=<POD_IP>" \ 482 --env "NODE_IP=<NODE_IP>" \ 483 -- no-snat-test [--port <port>] 484``` 485 486 487### no-snat-test-proxy 488 489Serves the `/checknosnat` endpoint on the given port (defaults to `31235`). The endpoint 490proxies the request to the given `target` (`/checknosnat?target=target_ip&ips=ip1,ip2` 491-> `target_ip/checknosnat?ips=ip1,ip2`) and will return the same status as the status 492as the proxied request, or 500 on error. 493 494 495Usage: 496 497```console 498 kubectl exec test-agnhost -- /agnhost no-snat-test-proxy [--port <port>] 499``` 500 501 502### pause 503 504It will pause the execution of the binary. This can be used for containers 505which have to be kept in a `Running` state for various purposes, including 506executing other `agnhost` commands. 507 508Usage: 509 510```console 511 kubectl exec test-agnhost -- /agnhost pause 512``` 513 514 515### port-forward-tester 516 517Listens for TCP connections on a given address and port, optionally checks the data received, 518and sends a configurable number of data chunks, with a configurable interval between chunks. 519 520The subcommand is using the following environment variables: 521 522- `BIND_ADDRESS` (optional): The address on which it will start listening for TCP connections (default value: `localhost`) 523- `BIND_PORT`: The port on which it will start listening for TCP connections. 524- `EXPECTED_CLIENT_DATA` (optional): If set, it will check that the request sends the same exact data. 525- `CHUNKS`: How many chunks of data to write in the response. 526- `CHUNK_SIZE`: The expected size of each written chunk of data. If it does not match the actual size of the written data, it will exit with the exit code `4`. 527- `CHUNK_INTERVAL`: The amount of time to wait in between chunks. 528 529Usage: 530 531```console 532 kubectl run test-agnhost \ 533 --generator=run-pod/v1 \ 534 --image=k8s.gcr.io/e2e-test-images/agnhost:2.21 \ 535 --restart=Never \ 536 --env "BIND_ADDRESS=localhost" \ 537 --env "BIND_PORT=8080" \ 538 --env "EXPECTED_CLIENT_DATA='Hello there!'" \ 539 --env "CHUNKS=1" \ 540 --env "CHUNK_SIZE=10" \ 541 --env "CHUNK_INTERVAL=1" \ 542 -- port-forward-tester 543``` 544 545 546### porter 547 548Serves requested data on ports specified in environment variables of the form `SERVE_{PORT,TLS_PORT,SCTP_PORT}_[NNNN]`. eg: 549 - `SERVE_PORT_9001` - serve TCP connections on port 9001 550 - `SERVE_TLS_PORT_9002` - serve TLS-encrypted TCP connections on port 9002 551 - `SERVE_SCTP_PORT_9003` - serve SCTP connections on port 9003 552 553The included `localhost.crt` is a PEM-encoded TLS cert with SAN IPs `127.0.0.1` and `[::1]`, 554expiring in January 2084, generated from `src/crypto/tls`: 555 556```console 557 go run generate_cert.go --rsa-bits 2048 --host 127.0.0.1,::1,example.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h 558``` 559 560To use a different cert/key, mount them into the pod and set the `CERT_FILE` and `KEY_FILE` 561environment variables to the desired paths. 562 563Usage: 564 565```console 566 kubectl exec test-agnhost -- /agnhost porter 567``` 568 569### resource-consumer-controller 570 571This subcommand starts an HTTP server that spreads requests around resource consumers. The HTTP server has the same endpoints and usage as the one spawned by the ``resource-consumer`` subcommand. 572 573The subcommand can accept the following flags: 574 575- `port` (default: 8080): The port number to listen to. 576- `consumer-port` (default: 8080): Port number of consumers. 577- `consumer-service-name` (default: `resource-consumer`): Name of service containing resource consumers. 578- `consumer-service-namespace` (default: `default`): Namespace of service containing resource consumers. 579 580Usage: 581 582```console 583 kubectl exec test-agnhost -- /agnhost resource-consumer-controller \ 584 [--port <port>] [--consumer-port <port>] [--consumer-service-name <service-name>] [--consumer-service-namespace <namespace>] 585``` 586 587 588### serve-hostname 589 590This is a small util app to serve your hostname on TCP and/or UDP. Useful for testing. 591 592The subcommand can accept the following flags: 593 594- `tcp` (default: `false`): Serve raw over TCP. 595- `udp` (default: `false`): Serve raw over UDP. 596- `http` (default: `true`): Serve HTTP. 597- `close` (default: `false`): Close connection per each HTTP request. 598- `port` (default: `9376`): The port number to listen to. 599 600Keep in mind that `--http` cannot be given at the same time as `--tcp` or `--udp`. 601 602Usage: 603 604```console 605 kubectl exec test-agnhost -- /agnhost serve-hostname [--tcp] [--udp] [--http] [--close] [--port <port>] 606``` 607 608### test-webserver 609 610Starts a simple HTTP fileserver which serves any file specified in the URL path, if it exists. 611 612The subcommand can accept the following flags: 613 614- `port` (default: `80`): The port number to listen to. 615 616Usage: 617 618```console 619 kubectl exec test-agnhost -- /agnhost test-webserver [--port <port>] 620``` 621 622 623### webhook (Kubernetes External Admission Webhook) 624 625The subcommand tests MutatingAdmissionWebhook and ValidatingAdmissionWebhook. After deploying 626it to kubernetes cluster, administrator needs to create a MutatingWebhookConfiguration or 627ValidatingWebhookConfiguration in kubernetes cluster to register remote webhook admission controllers. 628 629More details on the configuration can be found from here [Dynamic Admission Control](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/). 630 631Check the [MutatingAdmissionWebhook](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.14/#mutatingwebhookconfiguration-v1beta1-admissionregistration-k8s-io) and [ValidatingAdmissionWebhook](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.14/#validatingwebhookconfiguration-v1beta1-admissionregistration-k8s-io) documentations for more information about them. 632 633Usage: 634 635```console 636 kubectl exec test-agnhost -- /agnhost webhook [--tls-cert-file <key-file>] [--tls-private-key-file <cert-file>] 637``` 638 639 640## Other tools 641 642The image contains `iperf`, `curl`, `dns-tools` (including `dig`), CoreDNS, for both Windows and Linux. 643 644For Windows, the image is based on `busybox`, meaning that most of the Linux common tools are also 645available on it, making it possible to run most Linux commands in the `agnhost` Windows container 646as well. Keep in mind that there might still be some differences though (e.g.: `wget` does not 647have the `-T` argument on Windows). 648 649The Windows `agnhost` image includes a `nc` binary that is 100% compliant with its Linux equivalent. 650 651 652## Image 653 654The image can be found at `k8s.gcr.io/e2e-test-images/agnhost:2.21` for both Linux and 655Windows containers (based on `mcr.microsoft.com/windows/servercore:ltsc2019`, 656`mcr.microsoft.com/windows/servercore:1903`, and `mcr.microsoft.com/windows/servercore:1909`). 657