blob: 1f45fab1ce98966261bf0c3b241f22b4250cd149 [file] [log] [blame]
Tim Windelschmidt6d33a432025-02-04 14:34:25 +01001// Copyright The Monogon Project Authors.
Lorenz Brunfc5dbc62020-05-28 12:18:07 +02002// SPDX-License-Identifier: Apache-2.0
Lorenz Brunfc5dbc62020-05-28 12:18:07 +02003
Serge Bazanski99b02142024-04-17 16:33:28 +02004package kubernetes
Lorenz Brunfc5dbc62020-05-28 12:18:07 +02005
6import (
Serge Bazanski9104e382023-04-04 20:08:21 +02007 "bytes"
8 "context"
Lorenz Brunfc5dbc62020-05-28 12:18:07 +02009 "fmt"
Serge Bazanski9104e382023-04-04 20:08:21 +020010 "io"
11 "strings"
Lorenz Brunfc5dbc62020-05-28 12:18:07 +020012
13 appsv1 "k8s.io/api/apps/v1"
Serge Bazanski9104e382023-04-04 20:08:21 +020014 batchv1 "k8s.io/api/batch/v1"
Lorenz Brunfc5dbc62020-05-28 12:18:07 +020015 corev1 "k8s.io/api/core/v1"
16 "k8s.io/apimachinery/pkg/api/resource"
17 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
18 "k8s.io/apimachinery/pkg/util/intstr"
19 "k8s.io/client-go/kubernetes"
Jan Schär652c2ad2024-11-19 17:40:50 +010020 "k8s.io/utils/ptr"
Lorenz Brunfc5dbc62020-05-28 12:18:07 +020021)
22
Serge Bazanski216fe7b2021-05-21 18:36:16 +020023// makeTestDeploymentSpec generates a Deployment spec for a single pod running
24// NGINX with a readiness probe. This allows verifying that the control plane
25// is capable of scheduling simple pods and that kubelet works, its runtime is
26// set up well enough to run a simple container and the network to the pod can
27// pass readiness probe traffic.
Lorenz Brunfc5dbc62020-05-28 12:18:07 +020028func makeTestDeploymentSpec(name string) *appsv1.Deployment {
29 return &appsv1.Deployment{
30 ObjectMeta: metav1.ObjectMeta{Name: name},
31 Spec: appsv1.DeploymentSpec{
32 Selector: &metav1.LabelSelector{MatchLabels: map[string]string{
33 "name": name,
34 }},
35 Template: corev1.PodTemplateSpec{
36 ObjectMeta: metav1.ObjectMeta{
37 Labels: map[string]string{
38 "name": name,
39 },
40 },
41 Spec: corev1.PodSpec{
42 Containers: []corev1.Container{
43 {
Serge Bazanski591d8082023-03-16 00:26:59 +010044 Name: "test",
45 ImagePullPolicy: corev1.PullNever,
46 Image: "bazel/metropolis/test/e2e/preseedtest:preseedtest_image",
Lorenz Brunfc5dbc62020-05-28 12:18:07 +020047 ReadinessProbe: &corev1.Probe{
Lorenz Brund13c1c62022-03-30 19:58:58 +020048 ProbeHandler: corev1.ProbeHandler{
Lorenz Brunfc5dbc62020-05-28 12:18:07 +020049 HTTPGet: &corev1.HTTPGetAction{Port: intstr.FromInt(80)},
50 },
51 },
52 },
53 },
54 },
55 },
56 },
57 }
58}
59
Lorenz Brun276a7462023-07-12 21:28:54 +020060// makeHTTPServerDeploymentSpec generates the deployment spec for the test HTTP
61// server.
62func makeHTTPServerDeploymentSpec(name string) *appsv1.Deployment {
Lorenz Brun276a7462023-07-12 21:28:54 +020063 return &appsv1.Deployment{
64 ObjectMeta: metav1.ObjectMeta{Name: name},
65 Spec: appsv1.DeploymentSpec{
66 Selector: &metav1.LabelSelector{MatchLabels: map[string]string{
67 "name": name,
68 }},
Jan Schärd1a8b642024-12-03 17:40:41 +010069 Replicas: ptr.To(int32(1)),
Lorenz Brun276a7462023-07-12 21:28:54 +020070 Template: corev1.PodTemplateSpec{
71 ObjectMeta: metav1.ObjectMeta{
72 Labels: map[string]string{
73 "name": name,
74 },
75 },
76 Spec: corev1.PodSpec{
77 Containers: []corev1.Container{
78 {
79 Name: "test",
80 ImagePullPolicy: corev1.PullIfNotPresent,
81 Image: "test.monogon.internal/metropolis/test/e2e/httpserver/httpserver_image",
82 LivenessProbe: &corev1.Probe{
83 ProbeHandler: corev1.ProbeHandler{
84 HTTPGet: &corev1.HTTPGetAction{Port: intstr.FromInt(8080)},
85 },
86 },
87 },
88 },
89 },
90 },
91 },
92 }
93}
94
95// makeHTTPServerNodePortService generates the NodePort service spec
96// for testing the NodePort functionality.
97func makeHTTPServerNodePortService(name string) *corev1.Service {
98 return &corev1.Service{
99 ObjectMeta: metav1.ObjectMeta{Name: name},
100 Spec: corev1.ServiceSpec{
101 Type: corev1.ServiceTypeNodePort,
102 Selector: map[string]string{
103 "name": name,
104 },
105 Ports: []corev1.ServicePort{{
106 Name: name,
107 Protocol: corev1.ProtocolTCP,
108 Port: 80,
109 NodePort: 80,
110 TargetPort: intstr.FromInt(8080),
111 }},
112 },
113 }
114}
115
Serge Bazanski9104e382023-04-04 20:08:21 +0200116// makeSelftestSpec generates a Job spec for the E2E self-test image.
117func makeSelftestSpec(name string) *batchv1.Job {
Serge Bazanski9104e382023-04-04 20:08:21 +0200118 return &batchv1.Job{
119 ObjectMeta: metav1.ObjectMeta{Name: name},
120 Spec: batchv1.JobSpec{
Jan Schärd1a8b642024-12-03 17:40:41 +0100121 BackoffLimit: ptr.To(int32(1)),
Serge Bazanski9104e382023-04-04 20:08:21 +0200122 Template: corev1.PodTemplateSpec{
123 ObjectMeta: metav1.ObjectMeta{
124 Labels: map[string]string{
125 "job-name": name,
126 },
127 },
128 Spec: corev1.PodSpec{
129 Containers: []corev1.Container{
130 {
131 Name: "test",
Lorenz Brun150f24a2023-07-13 20:11:06 +0200132 ImagePullPolicy: corev1.PullIfNotPresent,
133 Image: "test.monogon.internal/metropolis/test/e2e/selftest/selftest_image",
Serge Bazanski9104e382023-04-04 20:08:21 +0200134 },
135 },
136 RestartPolicy: corev1.RestartPolicyOnFailure,
137 },
138 },
139 },
140 }
141}
142
Lorenz Brunfc5dbc62020-05-28 12:18:07 +0200143// makeTestStatefulSet generates a StatefulSet spec
Jan Schär73beb692024-11-27 17:47:09 +0100144func makeTestStatefulSet(name string, runtimeClass string) *appsv1.StatefulSet {
Lorenz Brunfc5dbc62020-05-28 12:18:07 +0200145 return &appsv1.StatefulSet{
146 ObjectMeta: metav1.ObjectMeta{Name: name},
147 Spec: appsv1.StatefulSetSpec{
148 Selector: &metav1.LabelSelector{MatchLabels: map[string]string{
149 "name": name,
150 }},
151 VolumeClaimTemplates: []corev1.PersistentVolumeClaim{
152 {
Jan Schär652c2ad2024-11-19 17:40:50 +0100153 ObjectMeta: metav1.ObjectMeta{Name: "vol-default"},
Lorenz Brunfc5dbc62020-05-28 12:18:07 +0200154 Spec: corev1.PersistentVolumeClaimSpec{
155 AccessModes: []corev1.PersistentVolumeAccessMode{corev1.ReadWriteOnce},
Tim Windelschmidtddc5e6a2024-04-23 23:44:34 +0200156 Resources: corev1.VolumeResourceRequirements{
Jan Schär652c2ad2024-11-19 17:40:50 +0100157 Requests: map[corev1.ResourceName]resource.Quantity{corev1.ResourceStorage: resource.MustParse("1Mi")},
Lorenz Brunfc5dbc62020-05-28 12:18:07 +0200158 },
Jan Schär652c2ad2024-11-19 17:40:50 +0100159 VolumeMode: ptr.To(corev1.PersistentVolumeFilesystem),
160 },
161 },
162 {
Jan Schär652c2ad2024-11-19 17:40:50 +0100163 ObjectMeta: metav1.ObjectMeta{Name: "vol-readonly"},
164 Spec: corev1.PersistentVolumeClaimSpec{
165 AccessModes: []corev1.PersistentVolumeAccessMode{corev1.ReadWriteOnce},
166 Resources: corev1.VolumeResourceRequirements{
167 Requests: map[corev1.ResourceName]resource.Quantity{corev1.ResourceStorage: resource.MustParse("1Mi")},
168 },
169 VolumeMode: ptr.To(corev1.PersistentVolumeFilesystem),
Lorenz Brunfc5dbc62020-05-28 12:18:07 +0200170 },
171 },
Jan Schärbe70c922024-11-21 11:16:03 +0100172 {
173 ObjectMeta: metav1.ObjectMeta{Name: "vol-block"},
174 Spec: corev1.PersistentVolumeClaimSpec{
175 AccessModes: []corev1.PersistentVolumeAccessMode{corev1.ReadWriteOnce},
176 Resources: corev1.VolumeResourceRequirements{
177 Requests: map[corev1.ResourceName]resource.Quantity{corev1.ResourceStorage: resource.MustParse("1Mi")},
178 },
179 VolumeMode: ptr.To(corev1.PersistentVolumeBlock),
180 },
181 },
Lorenz Brunfc5dbc62020-05-28 12:18:07 +0200182 },
183 Template: corev1.PodTemplateSpec{
184 ObjectMeta: metav1.ObjectMeta{
185 Labels: map[string]string{
186 "name": name,
187 },
188 },
189 Spec: corev1.PodSpec{
190 Containers: []corev1.Container{
191 {
Serge Bazanski591d8082023-03-16 00:26:59 +0100192 Name: "test",
Jan Schär652c2ad2024-11-19 17:40:50 +0100193 ImagePullPolicy: corev1.PullIfNotPresent,
194 Image: "test.monogon.internal/metropolis/test/e2e/persistentvolume/persistentvolume_image",
Jan Schär73beb692024-11-27 17:47:09 +0100195 Args: []string{"-runtimeclass", runtimeClass},
Jan Schär652c2ad2024-11-19 17:40:50 +0100196 VolumeMounts: []corev1.VolumeMount{
Jan Schärbe70c922024-11-21 11:16:03 +0100197 {
Jan Schär652c2ad2024-11-19 17:40:50 +0100198 Name: "vol-default",
199 MountPath: "/vol/default",
200 },
Jan Schärbe70c922024-11-21 11:16:03 +0100201 {
Jan Schär652c2ad2024-11-19 17:40:50 +0100202 Name: "vol-readonly",
203 ReadOnly: true,
204 MountPath: "/vol/readonly",
Lorenz Brunfc5dbc62020-05-28 12:18:07 +0200205 },
206 },
Jan Schärbe70c922024-11-21 11:16:03 +0100207 VolumeDevices: []corev1.VolumeDevice{
208 {
209 Name: "vol-block",
210 DevicePath: "/vol/block",
211 },
212 },
Lorenz Brunfc5dbc62020-05-28 12:18:07 +0200213 },
214 },
Jan Schär73beb692024-11-27 17:47:09 +0100215 RuntimeClassName: ptr.To(runtimeClass),
Lorenz Brunfc5dbc62020-05-28 12:18:07 +0200216 },
217 },
218 },
219 }
220}
Serge Bazanski9104e382023-04-04 20:08:21 +0200221
222func getPodLogLines(ctx context.Context, cs kubernetes.Interface, podName string, nlines int64) ([]string, error) {
223 logsR := cs.CoreV1().Pods("default").GetLogs(podName, &corev1.PodLogOptions{
224 TailLines: &nlines,
225 })
226 logs, err := logsR.Stream(ctx)
227 if err != nil {
228 return nil, fmt.Errorf("stream failed: %w", err)
229 }
230 var buf bytes.Buffer
231 _, err = io.Copy(&buf, logs)
232 if err != nil {
233 return nil, fmt.Errorf("copy failed: %w", err)
234 }
235 lineStr := strings.Trim(buf.String(), "\n")
236 lines := strings.Split(lineStr, "\n")
Jan Schär652c2ad2024-11-19 17:40:50 +0100237 if len(lines) > int(nlines) {
238 lines = lines[len(lines)-int(nlines):]
239 }
Serge Bazanski9104e382023-04-04 20:08:21 +0200240 return lines, nil
241}