blob: 1e513328fe1622b08db6163b627a21ea1b987093 [file] [log] [blame]
Lorenz Brunfc5dbc62020-05-28 12:18:07 +02001// Copyright 2020 The Monogon Project Authors.
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
17package e2e
18
19import (
20 "context"
Serge Bazanski54e212a2023-06-14 13:45:11 +020021 "crypto/tls"
22 "crypto/x509"
Lorenz Brunfc5dbc62020-05-28 12:18:07 +020023 "errors"
24 "fmt"
Serge Bazanski2cfafc92023-03-21 16:42:47 +010025 "io"
Leopold Schabele28e6d72020-06-03 11:39:25 +020026 "net"
Lorenz Brunfc5dbc62020-05-28 12:18:07 +020027 "net/http"
28 _ "net/http"
29 _ "net/http/pprof"
Serge Bazanski54e212a2023-06-14 13:45:11 +020030 "net/url"
Lorenz Brun3ff5af32020-06-24 16:34:11 +020031 "os"
Lorenz Brun5e4fc2d2020-09-22 18:35:15 +020032 "strings"
Lorenz Brunfc5dbc62020-05-28 12:18:07 +020033 "testing"
34 "time"
35
Serge Bazanskibe742842022-04-04 13:18:50 +020036 "google.golang.org/grpc"
Lorenz Brunfc5dbc62020-05-28 12:18:07 +020037 corev1 "k8s.io/api/core/v1"
Lorenz Brun30167f52021-03-17 17:49:01 +010038 "k8s.io/apimachinery/pkg/api/resource"
Lorenz Brunfc5dbc62020-05-28 12:18:07 +020039 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
40 podv1 "k8s.io/kubernetes/pkg/api/v1/pod"
41
Serge Bazanski31370b02021-01-07 16:31:14 +010042 common "source.monogon.dev/metropolis/node"
Serge Bazanski6dff6d62022-01-28 18:15:14 +010043 "source.monogon.dev/metropolis/node/core/identity"
Serge Bazanskibe742842022-04-04 13:18:50 +020044 "source.monogon.dev/metropolis/node/core/rpc"
Serge Bazanski31370b02021-01-07 16:31:14 +010045 apb "source.monogon.dev/metropolis/proto/api"
Serge Bazanski05f813b2023-03-16 17:58:39 +010046 "source.monogon.dev/metropolis/test/launch"
Serge Bazanski66e58952021-10-05 17:06:56 +020047 "source.monogon.dev/metropolis/test/launch/cluster"
Mateusz Zalegaddf19b42022-06-22 12:27:37 +020048 "source.monogon.dev/metropolis/test/util"
Lorenz Brunfc5dbc62020-05-28 12:18:07 +020049)
50
Leopold Schabeld603f842020-06-09 17:48:09 +020051const (
52 // Timeout for the global test context.
53 //
Serge Bazanski216fe7b2021-05-21 18:36:16 +020054 // Bazel would eventually time out the test after 900s ("large") if, for
55 // some reason, the context cancellation fails to abort it.
Leopold Schabeld603f842020-06-09 17:48:09 +020056 globalTestTimeout = 600 * time.Second
57
58 // Timeouts for individual end-to-end tests of different sizes.
Serge Bazanski1ebd1e12020-07-13 19:17:16 +020059 smallTestTimeout = 60 * time.Second
Leopold Schabeld603f842020-06-09 17:48:09 +020060 largeTestTimeout = 120 * time.Second
61)
62
Serge Bazanski216fe7b2021-05-21 18:36:16 +020063// TestE2E is the main E2E test entrypoint for single-node freshly-bootstrapped
64// E2E tests. It starts a full Metropolis node in bootstrap mode and then runs
65// tests against it. The actual tests it performs are located in the RunGroup
66// subtest.
Lorenz Brunfc5dbc62020-05-28 12:18:07 +020067func TestE2E(t *testing.T) {
Leopold Schabele28e6d72020-06-03 11:39:25 +020068 // Run pprof server for debugging
Serge Bazanski66e58952021-10-05 17:06:56 +020069 addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
70 if err != nil {
71 panic(err)
72 }
73
74 pprofListen, err := net.ListenTCP("tcp", addr)
75 if err != nil {
Serge Bazanski05f813b2023-03-16 17:58:39 +010076 launch.Fatal("Failed to listen on pprof port: %s", pprofListen.Addr())
Serge Bazanski66e58952021-10-05 17:06:56 +020077 }
78
Serge Bazanski05f813b2023-03-16 17:58:39 +010079 launch.Log("E2E: pprof server listening on %s", pprofListen.Addr())
Lorenz Brunfc5dbc62020-05-28 12:18:07 +020080 go func() {
Serge Bazanski05f813b2023-03-16 17:58:39 +010081 launch.Log("E2E: pprof server returned an error: %v", http.Serve(pprofListen, nil))
Serge Bazanski66e58952021-10-05 17:06:56 +020082 pprofListen.Close()
Lorenz Brunfc5dbc62020-05-28 12:18:07 +020083 }()
Leopold Schabele28e6d72020-06-03 11:39:25 +020084
Lorenz Brunfc5dbc62020-05-28 12:18:07 +020085 // Set a global timeout to make sure this terminates
Leopold Schabeld603f842020-06-09 17:48:09 +020086 ctx, cancel := context.WithTimeout(context.Background(), globalTestTimeout)
Serge Bazanski1f9a03b2021-08-17 13:40:53 +020087 defer cancel()
Serge Bazanski66e58952021-10-05 17:06:56 +020088
89 // Launch cluster.
Serge Bazanskie78a0892021-10-07 17:03:49 +020090 clusterOptions := cluster.ClusterOptions{
91 NumNodes: 2,
92 }
93 cluster, err := cluster.LaunchCluster(ctx, clusterOptions)
Lorenz Brunfc5dbc62020-05-28 12:18:07 +020094 if err != nil {
Serge Bazanski66e58952021-10-05 17:06:56 +020095 t.Fatalf("LaunchCluster failed: %v", err)
Lorenz Brunfc5dbc62020-05-28 12:18:07 +020096 }
Serge Bazanski66e58952021-10-05 17:06:56 +020097 defer func() {
98 err := cluster.Close()
99 if err != nil {
100 t.Fatalf("cluster Close failed: %v", err)
Lorenz Brunfc5dbc62020-05-28 12:18:07 +0200101 }
102 }()
Serge Bazanski1f9a03b2021-08-17 13:40:53 +0200103
Serge Bazanski05f813b2023-03-16 17:58:39 +0100104 launch.Log("E2E: Cluster running, starting tests...")
Lorenz Brunfc5dbc62020-05-28 12:18:07 +0200105
Serge Bazanskibe742842022-04-04 13:18:50 +0200106 // Dial first node's curator.
Serge Bazanski8535cb52023-03-29 14:15:08 +0200107 creds := rpc.NewAuthenticatedCredentials(cluster.Owner, rpc.WantInsecure())
Serge Bazanskibe742842022-04-04 13:18:50 +0200108 remote := net.JoinHostPort(cluster.NodeIDs[0], common.CuratorServicePort.PortString())
109 cl, err := grpc.Dial(remote, grpc.WithContextDialer(cluster.DialNode), grpc.WithTransportCredentials(creds))
110 if err != nil {
111 t.Fatalf("failed to dial first node's curator: %v", err)
112 }
113 defer cl.Close()
114 mgmt := apb.NewManagementClient(cl)
115
Serge Bazanski216fe7b2021-05-21 18:36:16 +0200116 // This exists to keep the parent around while all the children race.
117 // It currently tests both a set of OS-level conditions and Kubernetes
118 // Deployments and StatefulSets
Lorenz Brunfc5dbc62020-05-28 12:18:07 +0200119 t.Run("RunGroup", func(t *testing.T) {
Serge Bazanski6dff6d62022-01-28 18:15:14 +0100120 t.Run("Cluster", func(t *testing.T) {
Mateusz Zalegaddf19b42022-06-22 12:27:37 +0200121 util.TestEventual(t, "Retrieving cluster directory sucessful", ctx, 60*time.Second, func(ctx context.Context) error {
Serge Bazanskibe742842022-04-04 13:18:50 +0200122 res, err := mgmt.GetClusterInfo(ctx, &apb.GetClusterInfoRequest{})
Serge Bazanskibf68fa92021-10-05 17:53:58 +0200123 if err != nil {
124 return fmt.Errorf("GetClusterInfo: %w", err)
125 }
126
Serge Bazanskie78a0892021-10-07 17:03:49 +0200127 // Ensure that the expected node count is present.
Serge Bazanskibf68fa92021-10-05 17:53:58 +0200128 nodes := res.ClusterDirectory.Nodes
Serge Bazanskie78a0892021-10-07 17:03:49 +0200129 if want, got := clusterOptions.NumNodes, len(nodes); want != got {
Serge Bazanskibf68fa92021-10-05 17:53:58 +0200130 return fmt.Errorf("wanted %d nodes in cluster directory, got %d", want, got)
131 }
Serge Bazanski6dff6d62022-01-28 18:15:14 +0100132
133 // Ensure the nodes have the expected addresses.
134 addresses := make(map[string]bool)
135 for _, n := range nodes {
136 if len(n.Addresses) != 1 {
137 return fmt.Errorf("node %s has no addresss", identity.NodeID(n.PublicKey))
138 }
139 address := n.Addresses[0].Host
140 addresses[address] = true
141 }
142
143 for _, address := range []string{"10.1.0.2", "10.1.0.3"} {
144 if !addresses[address] {
145 return fmt.Errorf("address %q not found in directory", address)
146 }
147 }
Serge Bazanski1f9a03b2021-08-17 13:40:53 +0200148 return nil
149 })
Serge Bazanski630fb5c2023-04-06 10:50:24 +0200150 util.TestEventual(t, "Heartbeat test successful", ctx, 20*time.Second, cluster.AllNodesHealthy)
Mateusz Zalegaddf19b42022-06-22 12:27:37 +0200151 util.TestEventual(t, "Node rejoin successful", ctx, 60*time.Second, func(ctx context.Context) error {
Mateusz Zalega0246f5e2022-04-22 17:29:04 +0200152 // Ensure nodes rejoin the cluster after a reboot by reboting the 1st node.
153 if err := cluster.RebootNode(ctx, 1); err != nil {
154 return fmt.Errorf("while rebooting a node: %w", err)
155 }
156 return nil
157 })
Serge Bazanski630fb5c2023-04-06 10:50:24 +0200158 util.TestEventual(t, "Heartbeat test successful", ctx, 20*time.Second, cluster.AllNodesHealthy)
Serge Bazanski1f9a03b2021-08-17 13:40:53 +0200159 })
Serge Bazanski6dff6d62022-01-28 18:15:14 +0100160 t.Run("Kubernetes", func(t *testing.T) {
Lorenz Brunfc5dbc62020-05-28 12:18:07 +0200161 t.Parallel()
Serge Bazanskibe742842022-04-04 13:18:50 +0200162 // TODO(q3k): use SOCKS proxy.
163 clientSet, err := GetKubeClientSet(cluster, cluster.Ports[uint16(common.KubernetesAPIWrappedPort)])
Lorenz Brunfc5dbc62020-05-28 12:18:07 +0200164 if err != nil {
165 t.Fatal(err)
166 }
Serge Bazanski2cfafc92023-03-21 16:42:47 +0100167 util.TestEventual(t, "Add KubernetesWorker roles", ctx, smallTestTimeout, func(ctx context.Context) error {
168 // Find all nodes that are non-controllers.
169 var ids []string
170 srvN, err := mgmt.GetNodes(ctx, &apb.GetNodesRequest{})
171 if err != nil {
172 return fmt.Errorf("GetNodes: %w", err)
173 }
174 defer srvN.CloseSend()
175 for {
176 node, err := srvN.Recv()
177 if err == io.EOF {
178 break
179 }
180 if err != nil {
181 return fmt.Errorf("GetNodes.Recv: %w", err)
182 }
183 if node.Roles.KubernetesController != nil {
184 continue
185 }
186 if node.Roles.ConsensusMember != nil {
187 continue
188 }
189 ids = append(ids, identity.NodeID(node.Pubkey))
190 }
191
192 if len(ids) < 1 {
193 return fmt.Errorf("no appropriate nodes found")
194 }
195
196 // Make all these nodes as KubernetesWorker.
197 for _, id := range ids {
198 tr := true
199 _, err := mgmt.UpdateNodeRoles(ctx, &apb.UpdateNodeRolesRequest{
200 Node: &apb.UpdateNodeRolesRequest_Id{
201 Id: id,
202 },
203 KubernetesWorker: &tr,
204 })
205 if err != nil {
206 return fmt.Errorf("could not make node %q into kubernetes worker: %w", id, err)
207 }
208 }
209 return nil
210 })
211 util.TestEventual(t, "Node is registered and ready", ctx, largeTestTimeout, func(ctx context.Context) error {
Lorenz Brunfc5dbc62020-05-28 12:18:07 +0200212 nodes, err := clientSet.CoreV1().Nodes().List(ctx, metav1.ListOptions{})
213 if err != nil {
214 return err
215 }
Serge Bazanski2cfafc92023-03-21 16:42:47 +0100216 if len(nodes.Items) < 1 {
217 return errors.New("node not yet registered")
Lorenz Brunfc5dbc62020-05-28 12:18:07 +0200218 }
219 node := nodes.Items[0]
220 for _, cond := range node.Status.Conditions {
221 if cond.Type != corev1.NodeReady {
222 continue
223 }
224 if cond.Status != corev1.ConditionTrue {
225 return fmt.Errorf("node not ready: %v", cond.Message)
226 }
227 }
228 return nil
229 })
Mateusz Zalegaddf19b42022-06-22 12:27:37 +0200230 util.TestEventual(t, "Simple deployment", ctx, largeTestTimeout, func(ctx context.Context) error {
Lorenz Brunfc5dbc62020-05-28 12:18:07 +0200231 _, err := clientSet.AppsV1().Deployments("default").Create(ctx, makeTestDeploymentSpec("test-deploy-1"), metav1.CreateOptions{})
232 return err
233 })
Mateusz Zalegaddf19b42022-06-22 12:27:37 +0200234 util.TestEventual(t, "Simple deployment is running", ctx, largeTestTimeout, func(ctx context.Context) error {
Lorenz Brunfc5dbc62020-05-28 12:18:07 +0200235 res, err := clientSet.CoreV1().Pods("default").List(ctx, metav1.ListOptions{LabelSelector: "name=test-deploy-1"})
236 if err != nil {
237 return err
238 }
239 if len(res.Items) == 0 {
240 return errors.New("pod didn't get created")
241 }
242 pod := res.Items[0]
243 if podv1.IsPodAvailable(&pod, 1, metav1.NewTime(time.Now())) {
244 return nil
245 }
246 events, err := clientSet.CoreV1().Events("default").List(ctx, metav1.ListOptions{FieldSelector: fmt.Sprintf("involvedObject.name=%s,involvedObject.namespace=default", pod.Name)})
247 if err != nil || len(events.Items) == 0 {
248 return fmt.Errorf("pod is not ready: %v", pod.Status.Phase)
249 } else {
250 return fmt.Errorf("pod is not ready: %v", events.Items[0].Message)
251 }
252 })
Mateusz Zalegaddf19b42022-06-22 12:27:37 +0200253 util.TestEventual(t, "Simple deployment with runc", ctx, largeTestTimeout, func(ctx context.Context) error {
Lorenz Brun5e4fc2d2020-09-22 18:35:15 +0200254 deployment := makeTestDeploymentSpec("test-deploy-2")
255 var runcStr = "runc"
256 deployment.Spec.Template.Spec.RuntimeClassName = &runcStr
257 _, err := clientSet.AppsV1().Deployments("default").Create(ctx, deployment, metav1.CreateOptions{})
258 return err
259 })
Mateusz Zalegaddf19b42022-06-22 12:27:37 +0200260 util.TestEventual(t, "Simple deployment is running on runc", ctx, largeTestTimeout, func(ctx context.Context) error {
Lorenz Brun5e4fc2d2020-09-22 18:35:15 +0200261 res, err := clientSet.CoreV1().Pods("default").List(ctx, metav1.ListOptions{LabelSelector: "name=test-deploy-2"})
262 if err != nil {
263 return err
264 }
265 if len(res.Items) == 0 {
266 return errors.New("pod didn't get created")
267 }
268 pod := res.Items[0]
269 if podv1.IsPodAvailable(&pod, 1, metav1.NewTime(time.Now())) {
270 return nil
271 }
272 events, err := clientSet.CoreV1().Events("default").List(ctx, metav1.ListOptions{FieldSelector: fmt.Sprintf("involvedObject.name=%s,involvedObject.namespace=default", pod.Name)})
273 if err != nil || len(events.Items) == 0 {
274 return fmt.Errorf("pod is not ready: %v", pod.Status.Phase)
275 } else {
276 var errorMsg strings.Builder
277 for _, msg := range events.Items {
278 errorMsg.WriteString(" | ")
279 errorMsg.WriteString(msg.Message)
280 }
281 return fmt.Errorf("pod is not ready: %v", errorMsg.String())
282 }
283 })
Mateusz Zalegaddf19b42022-06-22 12:27:37 +0200284 util.TestEventual(t, "Simple StatefulSet with PVC", ctx, largeTestTimeout, func(ctx context.Context) error {
Lorenz Brun37050122021-03-30 14:00:27 +0200285 _, err := clientSet.AppsV1().StatefulSets("default").Create(ctx, makeTestStatefulSet("test-statefulset-1", corev1.PersistentVolumeFilesystem), metav1.CreateOptions{})
Lorenz Brunfc5dbc62020-05-28 12:18:07 +0200286 return err
287 })
Mateusz Zalegaddf19b42022-06-22 12:27:37 +0200288 util.TestEventual(t, "Simple StatefulSet with PVC is running", ctx, largeTestTimeout, func(ctx context.Context) error {
Lorenz Brunfc5dbc62020-05-28 12:18:07 +0200289 res, err := clientSet.CoreV1().Pods("default").List(ctx, metav1.ListOptions{LabelSelector: "name=test-statefulset-1"})
290 if err != nil {
291 return err
292 }
293 if len(res.Items) == 0 {
294 return errors.New("pod didn't get created")
295 }
296 pod := res.Items[0]
297 if podv1.IsPodAvailable(&pod, 1, metav1.NewTime(time.Now())) {
298 return nil
299 }
300 events, err := clientSet.CoreV1().Events("default").List(ctx, metav1.ListOptions{FieldSelector: fmt.Sprintf("involvedObject.name=%s,involvedObject.namespace=default", pod.Name)})
301 if err != nil || len(events.Items) == 0 {
302 return fmt.Errorf("pod is not ready: %v", pod.Status.Phase)
303 } else {
304 return fmt.Errorf("pod is not ready: %v", events.Items[0].Message)
305 }
306 })
Mateusz Zalegaddf19b42022-06-22 12:27:37 +0200307 util.TestEventual(t, "Simple StatefulSet with Block PVC", ctx, largeTestTimeout, func(ctx context.Context) error {
Lorenz Brun37050122021-03-30 14:00:27 +0200308 _, err := clientSet.AppsV1().StatefulSets("default").Create(ctx, makeTestStatefulSet("test-statefulset-2", corev1.PersistentVolumeBlock), metav1.CreateOptions{})
309 return err
310 })
Mateusz Zalegaddf19b42022-06-22 12:27:37 +0200311 util.TestEventual(t, "Simple StatefulSet with Block PVC is running", ctx, largeTestTimeout, func(ctx context.Context) error {
Lorenz Brun37050122021-03-30 14:00:27 +0200312 res, err := clientSet.CoreV1().Pods("default").List(ctx, metav1.ListOptions{LabelSelector: "name=test-statefulset-2"})
313 if err != nil {
314 return err
315 }
316 if len(res.Items) == 0 {
317 return errors.New("pod didn't get created")
318 }
319 pod := res.Items[0]
320 if podv1.IsPodAvailable(&pod, 1, metav1.NewTime(time.Now())) {
321 return nil
322 }
323 events, err := clientSet.CoreV1().Events("default").List(ctx, metav1.ListOptions{FieldSelector: fmt.Sprintf("involvedObject.name=%s,involvedObject.namespace=default", pod.Name)})
324 if err != nil || len(events.Items) == 0 {
325 return fmt.Errorf("pod is not ready: %v", pod.Status.Phase)
326 } else {
327 return fmt.Errorf("pod is not ready: %v", events.Items[0].Message)
328 }
329 })
Serge Bazanski9104e382023-04-04 20:08:21 +0200330 util.TestEventual(t, "In-cluster self-test job", ctx, smallTestTimeout, func(ctx context.Context) error {
331 _, err := clientSet.BatchV1().Jobs("default").Create(ctx, makeSelftestSpec("selftest"), metav1.CreateOptions{})
332 return err
333 })
334 util.TestEventual(t, "In-cluster self-test job passed", ctx, smallTestTimeout, func(ctx context.Context) error {
335 res, err := clientSet.BatchV1().Jobs("default").Get(ctx, "selftest", metav1.GetOptions{})
336 if err != nil {
337 return err
338 }
339 if res.Status.Failed > 0 {
340 pods, err := clientSet.CoreV1().Pods("default").List(ctx, metav1.ListOptions{
341 LabelSelector: "job-name=selftest",
342 })
343 if err != nil {
344 return util.Permanent(fmt.Errorf("job failed but failed to find pod: %w", err))
345 }
346 if len(pods.Items) < 1 {
347 return fmt.Errorf("job failed but pod does not exist")
348 }
349 lines, err := getPodLogLines(ctx, clientSet, pods.Items[0].Name, 1)
350 if err != nil {
351 return fmt.Errorf("job failed but could not get logs: %w", err)
352 }
353 if len(lines) > 0 {
354 return util.Permanent(fmt.Errorf("job failed, last log line: %s", lines[0]))
355 }
356 return util.Permanent(fmt.Errorf("job failed, empty log"))
357 }
358 if res.Status.Succeeded > 0 {
359 return nil
360 }
361 return fmt.Errorf("job still running")
362 })
Serge Bazanski54e212a2023-06-14 13:45:11 +0200363 util.TestEventual(t, "Prometheus node metrics retrieved", ctx, smallTestTimeout, func(ctx context.Context) error {
364 pool := x509.NewCertPool()
365 pool.AddCert(cluster.CACertificate)
366 cl := http.Client{
367 Transport: &http.Transport{
368 TLSClientConfig: &tls.Config{
369 Certificates: []tls.Certificate{cluster.Owner},
370 RootCAs: pool,
371 },
372 DialContext: func(ctx context.Context, _, addr string) (net.Conn, error) {
373 return cluster.DialNode(ctx, addr)
374 },
375 },
376 }
377 u := url.URL{
378 Scheme: "https",
379 Host: net.JoinHostPort(cluster.NodeIDs[0], common.MetricsPort.PortString()),
380 Path: "/metrics/node",
381 }
382 res, err := cl.Get(u.String())
383 if err != nil {
384 return err
385 }
386 defer res.Body.Close()
387 if res.StatusCode != 200 {
388 return fmt.Errorf("status code %d", res.StatusCode)
389 }
390
391 body, err := io.ReadAll(res.Body)
392 if err != nil {
393 return err
394 }
395 needle := "node_uname_info"
396 if !strings.Contains(string(body), needle) {
397 return util.Permanent(fmt.Errorf("could not find %q in returned response", needle))
398 }
399 return nil
400 })
Lorenz Brun30167f52021-03-17 17:49:01 +0100401 if os.Getenv("HAVE_NESTED_KVM") != "" {
Mateusz Zalegaddf19b42022-06-22 12:27:37 +0200402 util.TestEventual(t, "Pod for KVM/QEMU smoke test", ctx, smallTestTimeout, func(ctx context.Context) error {
Lorenz Brun30167f52021-03-17 17:49:01 +0100403 runcRuntimeClass := "runc"
404 _, err := clientSet.CoreV1().Pods("default").Create(ctx, &corev1.Pod{
405 ObjectMeta: metav1.ObjectMeta{
406 Name: "vm-smoketest",
407 },
408 Spec: corev1.PodSpec{
409 Containers: []corev1.Container{{
410 Name: "vm-smoketest",
411 ImagePullPolicy: corev1.PullNever,
412 Image: "bazel/metropolis/vm/smoketest:smoketest_container",
413 Resources: corev1.ResourceRequirements{
414 Limits: corev1.ResourceList{
415 "devices.monogon.dev/kvm": *resource.NewQuantity(1, ""),
416 },
417 },
418 }},
419 RuntimeClassName: &runcRuntimeClass,
420 RestartPolicy: corev1.RestartPolicyNever,
421 },
422 }, metav1.CreateOptions{})
423 return err
424 })
Mateusz Zalegaddf19b42022-06-22 12:27:37 +0200425 util.TestEventual(t, "KVM/QEMU smoke test completion", ctx, smallTestTimeout, func(ctx context.Context) error {
Lorenz Brun30167f52021-03-17 17:49:01 +0100426 pod, err := clientSet.CoreV1().Pods("default").Get(ctx, "vm-smoketest", metav1.GetOptions{})
427 if err != nil {
428 return fmt.Errorf("failed to get pod: %w", err)
429 }
430 if pod.Status.Phase == corev1.PodSucceeded {
431 return nil
432 }
433 events, err := clientSet.CoreV1().Events("default").List(ctx, metav1.ListOptions{FieldSelector: fmt.Sprintf("involvedObject.name=%s,involvedObject.namespace=default", pod.Name)})
434 if err != nil || len(events.Items) == 0 {
435 return fmt.Errorf("pod is not ready: %v", pod.Status.Phase)
436 } else {
437 return fmt.Errorf("pod is not ready: %v", events.Items[len(events.Items)-1].Message)
438 }
439 })
440 }
Lorenz Brunfc5dbc62020-05-28 12:18:07 +0200441 })
442 })
Lorenz Brunfc5dbc62020-05-28 12:18:07 +0200443}