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