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