From 0effea884f86aeef0edac3a94c38524a3f2a4d53 Mon Sep 17 00:00:00 2001 From: Tiago Silva Date: Mon, 4 Nov 2024 18:58:42 +0000 Subject: [PATCH] [kube] use local image for kubernetes integration tests (#48321) (#48386) * [kube] use local image for kubernetes integration tests This PR replaces the use of the `nginx:latest` Docker image in Kubernetes integration tests with a custom-built image based on `alpine:3.20.3`. This custom image includes a shell for `kubectl exec` integration tests and a compiled binary to act as an HTTP server. This change addresses recent issues where test workflows failed due to problems downloading the `nginx` image. * add information about the image * source files from alpine cdn * copy files from alpine * Update fixtures/alpine/README.md * Update fixtures/alpine/README.md * source files from alpine cdn (again) --------- Co-authored-by: Alan Parra --- .../kube-integration-tests-non-root.yaml | 33 ++++++++++++ .gitignore | 2 + fixtures/alpine/Dockerfile | 9 ++++ fixtures/alpine/README.md | 50 ++++++++++++++++++ .../alpine-ncopa.at.alpinelinux.org.asc | 52 +++++++++++++++++++ fixtures/alpine/webserver.go | 26 ++++++++++ integration/kube_integration_test.go | 13 +++-- 7 files changed, 182 insertions(+), 3 deletions(-) create mode 100644 fixtures/alpine/Dockerfile create mode 100644 fixtures/alpine/README.md create mode 100644 fixtures/alpine/alpine-ncopa.at.alpinelinux.org.asc create mode 100644 fixtures/alpine/webserver.go diff --git a/.github/workflows/kube-integration-tests-non-root.yaml b/.github/workflows/kube-integration-tests-non-root.yaml index ac2892e75cbcd..af1c4613b4630 100644 --- a/.github/workflows/kube-integration-tests-non-root.yaml +++ b/.github/workflows/kube-integration-tests-non-root.yaml @@ -28,6 +28,7 @@ on: env: TEST_KUBE: true KUBECONFIG: /home/.kube/config + ALPINE_VERSION: 3.20.3 jobs: test: @@ -81,6 +82,38 @@ jobs: cp -r $HOME/.kube /home/ chown -R ci:ci /home/.kube + - name: Build Alpine image with webserver + run: | + + export SHORT_VERSION=${ALPINE_VERSION%.*} + + # download the alpine image + # store the files in the fixtures/alpine directory + # to avoid passing all the repository files to the docker build context. + cd ./fixtures/alpine + + # download alpine minirootfs and signature + curl -fSsLO https://dl-cdn.alpinelinux.org/alpine/v$SHORT_VERSION/releases/x86_64/alpine-minirootfs-$ALPINE_VERSION-x86_64.tar.gz + curl -fSsLO https://dl-cdn.alpinelinux.org/alpine/v$SHORT_VERSION/releases/x86_64/alpine-minirootfs-$ALPINE_VERSION-x86_64.tar.gz.asc + curl -fSsLO https://dl-cdn.alpinelinux.org/alpine/v$SHORT_VERSION/releases/x86_64/alpine-minirootfs-$ALPINE_VERSION-x86_64.tar.gz.sha256 + + # verify the checksum + sha256sum -c alpine-minirootfs-$ALPINE_VERSION-x86_64.tar.gz.sha256 + + # verify the signature + gpg --import ./alpine-ncopa.at.alpinelinux.org.asc + gpg --verify ./alpine-minirootfs-$ALPINE_VERSION-x86_64.tar.gz.asc ./alpine-minirootfs-$ALPINE_VERSION-x86_64.tar.gz + + # build the webserver + CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ./webserver ./webserver.go + + docker build -t alpine-webserver:v1 --build-arg=ALPINE_VERSION=$ALPINE_VERSION -f ./Dockerfile . + + # load the image into the kind cluster + kind load docker-image alpine-webserver:v1 + + cd - + - name: Run tests timeout-minutes: 40 run: | diff --git a/.gitignore b/.gitignore index 6adc620d19d35..c87cd2d47bda5 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,8 @@ default.etcd # usually release tarballs get in the way *.gz +# ignore all tarballs except the alpine one +!fixtures/alpine/alpine-minirootfs-*.tar.gz *.zip # editors diff --git a/fixtures/alpine/Dockerfile b/fixtures/alpine/Dockerfile new file mode 100644 index 0000000000000..4b66c356c5466 --- /dev/null +++ b/fixtures/alpine/Dockerfile @@ -0,0 +1,9 @@ +FROM scratch + +ARG ALPINE_VERSION + +ADD alpine-minirootfs-$ALPINE_VERSION-x86_64.tar.gz / + +COPY webserver /webserver + +CMD [ "/webserver" ] diff --git a/fixtures/alpine/README.md b/fixtures/alpine/README.md new file mode 100644 index 0000000000000..a6506854275fd --- /dev/null +++ b/fixtures/alpine/README.md @@ -0,0 +1,50 @@ +# `alpine-webserver:v1` Build Process + +## Source + +The `alpine-webserver:v1` image is based on `alpine` `minirootfs`, but instead of relying on Docker Hub's official Alpine image, we source the original files directly from Alpine's CDN. This approach mitigates issues with Docker Hub and GitHub Action network failures, which have been a common cause of integration test failures. + +The build process is specified in the `.github/workflows/kube-integration-tests-non-root.yaml` file. + +## Download + +To download the new `alpine-minirootfs` image, follow the instructions: + +```bash +$ export ALPINE_VERSION=3.20.3 +$ export SHORT_VERSION=${ALPINE_VERSION%.*} + +# download alpine minirootfs and signature +$ curl -fSsLO https://dl-cdn.alpinelinux.org/alpine/v$SHORT_VERSION/releases/x86_64/alpine-minirootfs-$ALPINE_VERSION-x86_64.tar.gz +$ curl -fSsLO https://dl-cdn.alpinelinux.org/alpine/v$SHORT_VERSION/releases/x86_64/alpine-minirootfs-$ALPINE_VERSION-x86_64.tar.gz.asc +$ curl -fSsLO https://dl-cdn.alpinelinux.org/alpine/v$SHORT_VERSION/releases/x86_64/alpine-minirootfs-$ALPINE_VERSION-x86_64.tar.gz.sha256 + +``` + +## Source Validation + +The build process in `.github/workflows/kube-integration-tests-non-root.yaml` validates both the SHA-256 checksum and the GPG signature. The signature verification uses `alpine-ncopa.at.alpinelinux.org.asc`, which is the official public key used by Alpine Linux to sign its assets. This public key is available on the [Alpine Linux Downloads page](https://www.alpinelinux.org/downloads/). + +## Image Build Process + +The image is constructed from a scratch filesystem and incorporates only the necessary components to run the web server. Here’s the basic Dockerfile configuration: + +```Dockerfile +FROM scratch + +ARG ALPINE_VERSION + +ADD alpine-minirootfs-$ALPINE_VERSION-x86_64.tar.gz / + +COPY webserver /webserver + +CMD [ "/webserver" ] +``` + +This minimalist configuration ensures the image remains lightweight, secure, and tailored to only the required functionalities. + +## Image distribution + +After sucessfull build, the image is loaded into our `kind` cluster with the tag `alpine-webserver:v1`. + +Note: `:latest` can't be used otherwise Kubernetes will try loading the image from dockerhub and fail. \ No newline at end of file diff --git a/fixtures/alpine/alpine-ncopa.at.alpinelinux.org.asc b/fixtures/alpine/alpine-ncopa.at.alpinelinux.org.asc new file mode 100644 index 0000000000000..b4b34a5b3da96 --- /dev/null +++ b/fixtures/alpine/alpine-ncopa.at.alpinelinux.org.asc @@ -0,0 +1,52 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v2 + +mQINBFSIEDwBEADbib88gv1dBgeEez1TIh6A5lAzRl02JrdtYkDoPr5lQGYv0qKP +lWpd3jgGe8n90krGmT9W2nooRdyZjZ6UPbhYSJ+tub6VuKcrtwROXP2gNNqJA5j3 +vkXQ40725CVig7I3YCpzjsKRStwegZAelB8ZyC4zb15J7YvTVkd6qa/uuh8H21X2 +h/7IZJz50CMxyz8vkdyP2niIGZ4fPi0cVtsg8l4phbNJ5PwFOLMYl0b5geKMviyR +MxxQ33iNa9X+RcWeR751IQfax6xNcbOrxNRzfzm77fY4KzBezcnqJFnrl/p8qgBq +GHKmrrcjv2MF7dCWHGAPm1/vdPPjUpOcEOH4uGvX7P4w2qQ0WLBTDDO47/BiuY9A +DIwEF1afNXiJke4fmjDYMKA+HrnhocvI48VIX5C5+C5aJOKwN2EOpdXSvmsysTSt +gIc4ffcaYugfAIEn7ZdgcYmTlbIphHmOmOgt89J+6Kf9X6mVRmumI3cZWetf2FEV +fS9v24C2c8NRw3LESoDT0iiWsCHcsixCYqqvjzJBJ0TSEIVCZepOOBp8lfMl4YEZ +BVMzOx558LzbF2eR/XEsr3AX7Ga1jDu2N5WzIOa0YvJl1xcQxc0RZumaMlZ81dV/ +uu8G2+HTrJMZK933ov3pbxaZ38/CbCA90SBk5xqVqtTNAHpIkdGj90v2lwARAQAB +tCVOYXRhbmFlbCBDb3BhIDxuY29wYUBhbHBpbmVsaW51eC5vcmc+iQI2BBMBCAAg +BQJUiBA8AhsDBQsJCAcCBhUICQoLAgMWAgECHgECF4AACgkQKTrNCQfZSVrcNxAA +mEzX9PQaczzlPAlDe3m1AN0lP6E/1pYWLBGs6qGh18cWxdjyOWsO47nA1P+cTGSS +AYe4kIOIx9kp2SxObdKeZTuZCBdWfQu/cuRE12ugQQFERlpwVRNd6NYuT3WyZ7v8 +ZXRw4f33FIt4CSrW1/AyM/vrA+tWNo7bbwr/CFaIcL8kINPccdFOpWh14erONd/P +Eb3gO81yXIA6c1Vl4mce2JS0hd6EFohxS5yMQJMRIS/Zg8ufT3yHJXIaSnG+KRP7 +WWLR0ZaLraCykYi/EW9mmQ49LxQqvKOgjpRW9aNgDA+arKl1umjplkAFI1GZ0/qA +sgKm4agdvLGZiCZqDXcRWNolG5PeOUUpim1f59pGnupZ3Rbz4BF84U+1uL+yd0OR +5Y98AxWFyq0dqKz/zFYwQkMVnl9yW0pkJmP7r6PKj0bhWksQX+RjYPosj3wxPZ7i +SKMX7xZaqon/CHpH9/Xm8CabGcDITrS6h+h8x0FFT/MV/LKgc3q8E4mlXelew1Rt +xK4hzXFpXKl0WcQg54fj1Wqy47FlkArG50di0utCBGlmVZQA8nqE5oYkFLppiFXz +1SXCXojff/XZdNF2WdgV8aDKOYTK1WDPUSLmqY+ofOkQL49YqZ9M5FR8hMAbvL6e +4CbxVXCkWJ6Q9Lg79AzS3pvOXCJ/CUDQs7B30v026Ba5Ag0EVIgQPAEQAMHuPAv/ +B0KP9SEA1PsX5+37k46lTP7lv7VFd7VaD1rAUM/ZyD2fWgrJprcCPEpdMfuszfOH +jGVQ708VQ+vlD3vFoOZE+KgeKnzDG9FzYXXPmxkWzEEqI168ameF/LQhN12VF1mq +5LbukiAKx2ytb1I8onvCvNJDvH1D/3BxSj7ThV9bP/bFufcOHFBMFwtyBmUaR5Wx +96Bq+7DEbTrxhshoQgUqILEudUyhZa05/TrpUvC4f8qc0deaqJFO1zD6guZxRWZd +SWJdcFzTadyg36P4eyFMxa1Ft7BlDKdKLAFlCGgR0jfOnKRmdRKGRNFTLQ68aBld +N4wxBuMwe0tmRw9zYwWwD43Aq9E26YtuxVR1wb3zUmi+47QH4ANAzMioimE9Mj5S +qYrgzQJ0IGwIjBt+HNzHvYX+kyMuVFK41k2Vo6oUOVHuQMu3UgLvSPMsyw69d+Iw +K/rrsQwuutrvJ8Qcda3rea1HvWBVcY/uyoRsOsCS7itS6MK6KKTKaW8iskmEb2/h +Q1ZB1QaWm2sQ8Xcmb3QZgtyBfZKuC95T/mAXPT0uET6bTpP5DdEi3wFs+qw/c9FZ +SNDZ4hfNuS24d2u3Rh8LWt/U83ieAutNntOLGhvuZm1jLYt2KvzXE8cLt3V75/ZF +O+xEV7rLuOtrHKWlzgJQzsDp1gM4Tz9ULeY7ABEBAAGJAh8EGAEIAAkFAlSIEDwC +GwwACgkQKTrNCQfZSVrIgBAArhCdo3ItpuEKWcxx22oMwDm+0dmXmzqcPnB8y9Tf +NcocToIXP47H1+XEenZdTYZJOrdqzrK6Y1PplwQv6hqFToypgbQTeknrZ8SCDyEK +cU4id2r73THTzgNSiC4QAE214i5kKd6PMQn7XYVjsxvin3ZalS2x4m8UFal2C9nj +o8HqoTsDOSRy0mzoqAqXmeAe3X9pYme/CUwA6R8hHEgX7jUhm/ArVW5wZboAinw5 +BmKBjWiIwT1vxfvwgbC0EA1O24G4zQqEJ2ILmcM3RvWwtFFWasQqV7qnKdpD8EIb +oPa8Ocl7joDc5seK8BzsI7tXN4Yjw0aHCOlZ15fWHPYKgDFRQaRFffODPNbxQNiz +Yru3pbEWDLIUoQtJyKl+o2+8m4aWCYNzJ1WkEQje9RaBpHNDcyen5yC73tCEJsvT +ZuMI4Xqc4xgLt8woreKE57GRdg2fO8fO40X3R/J5YM6SqG7y2uwjVCHFBeO2Nkkr +8nOno+Rbn2b03c9MapMT4ll8jJds4xwhhpIjzPLWd2ZcX/ZGqmsnKPiroe9p1VPo +lN72Ohr9lS+OXfvOPV2N+Ar5rCObmhnYbXGgU/qyhk1qkRu+w2bBZOOQIdaCfh5A +Hbn3ZGGGQskgWZDFP4xZ3DWXFSWMPuvEjbmUn2xrh9oYsjsOGy9tyBFFySU2vyZP +Mkc= +=FcYC +-----END PGP PUBLIC KEY BLOCK----- diff --git a/fixtures/alpine/webserver.go b/fixtures/alpine/webserver.go new file mode 100644 index 0000000000000..b41a818ffc18c --- /dev/null +++ b/fixtures/alpine/webserver.go @@ -0,0 +1,26 @@ +// Teleport +// Copyright (C) 2024 Gravitational, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package main + +import "net/http" + +func main() { + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("Hello, world!")) + }) + http.ListenAndServe(":80", nil) +} diff --git a/integration/kube_integration_test.go b/integration/kube_integration_test.go index 2e74ff76ea2db..9c5140a28023c 100644 --- a/integration/kube_integration_test.go +++ b/integration/kube_integration_test.go @@ -1436,7 +1436,7 @@ func testKubeEphemeralContainers(t *testing.T, suite *KubeSuite) { sessCreatorTerm := NewTerminal(250) group := &errgroup.Group{} group.Go(func() error { - cmd := []string{"/bin/sh", "echo", "hello from an ephemeral container"} + cmd := []string{"/bin/sh", "-c", "echo hello from an ephemeral container"} debugPod, _, err := generateDebugContainer(contName, cmd, pod) if err != nil { return trace.Wrap(err) @@ -1541,7 +1541,7 @@ func generateDebugContainer(name string, cmd []string, pod *v1.Pod) (*v1.Pod, *v ec := &v1.EphemeralContainer{ EphemeralContainerCommon: v1.EphemeralContainerCommon{ Name: name, - Image: "alpine:latest", + Image: localPodImage, Command: cmd, ImagePullPolicy: v1.PullIfNotPresent, Stdin: true, @@ -1704,6 +1704,13 @@ func newNamespace(name string) *v1.Namespace { } } +// localPodImage is a container image that is used for testing +// It's a docker image that runs a simple web server +// that listens on port 80 and returns "Hello, World!" on GET / +// This image is vendored in the Teleport repository in the +// fixtures/alpine directory. Check the Dockerfile there for details. +const localPodImage = "alpine-webserver:v1" + func newPod(ns, name string) *v1.Pod { return &v1.Pod{ ObjectMeta: metav1.ObjectMeta{ @@ -1713,7 +1720,7 @@ func newPod(ns, name string) *v1.Pod { Spec: v1.PodSpec{ Containers: []v1.Container{{ Name: "nginx", - Image: "nginx:alpine", + Image: localPodImage, }}, }, }