blob: 25a785d61c3321e52d44b285c683ab98d3f20c94 [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
Serge Bazanski99b02142024-04-17 16:33:28 +020017package kubernetes
Lorenz Brunfc5dbc62020-05-28 12:18:07 +020018
19import (
Serge Bazanski9104e382023-04-04 20:08:21 +020020 "bytes"
21 "context"
Lorenz Brunfc5dbc62020-05-28 12:18:07 +020022 "fmt"
Serge Bazanski9104e382023-04-04 20:08:21 +020023 "io"
24 "strings"
Lorenz Brunfc5dbc62020-05-28 12:18:07 +020025
26 appsv1 "k8s.io/api/apps/v1"
Serge Bazanski9104e382023-04-04 20:08:21 +020027 batchv1 "k8s.io/api/batch/v1"
Lorenz Brunfc5dbc62020-05-28 12:18:07 +020028 corev1 "k8s.io/api/core/v1"
29 "k8s.io/apimachinery/pkg/api/resource"
30 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
31 "k8s.io/apimachinery/pkg/util/intstr"
32 "k8s.io/client-go/kubernetes"
Jan Schär652c2ad2024-11-19 17:40:50 +010033 "k8s.io/utils/ptr"
Lorenz Brunfc5dbc62020-05-28 12:18:07 +020034)
35
Serge Bazanski216fe7b2021-05-21 18:36:16 +020036// makeTestDeploymentSpec generates a Deployment spec for a single pod running
37// NGINX with a readiness probe. This allows verifying that the control plane
38// is capable of scheduling simple pods and that kubelet works, its runtime is
39// set up well enough to run a simple container and the network to the pod can
40// pass readiness probe traffic.
Lorenz Brunfc5dbc62020-05-28 12:18:07 +020041func makeTestDeploymentSpec(name string) *appsv1.Deployment {
42 return &appsv1.Deployment{
43 ObjectMeta: metav1.ObjectMeta{Name: name},
44 Spec: appsv1.DeploymentSpec{
45 Selector: &metav1.LabelSelector{MatchLabels: map[string]string{
46 "name": name,
47 }},
48 Template: corev1.PodTemplateSpec{
49 ObjectMeta: metav1.ObjectMeta{
50 Labels: map[string]string{
51 "name": name,
52 },
53 },
54 Spec: corev1.PodSpec{
55 Containers: []corev1.Container{
56 {
Serge Bazanski591d8082023-03-16 00:26:59 +010057 Name: "test",
58 ImagePullPolicy: corev1.PullNever,
59 Image: "bazel/metropolis/test/e2e/preseedtest:preseedtest_image",
Lorenz Brunfc5dbc62020-05-28 12:18:07 +020060 ReadinessProbe: &corev1.Probe{
Lorenz Brund13c1c62022-03-30 19:58:58 +020061 ProbeHandler: corev1.ProbeHandler{
Lorenz Brunfc5dbc62020-05-28 12:18:07 +020062 HTTPGet: &corev1.HTTPGetAction{Port: intstr.FromInt(80)},
63 },
64 },
65 },
66 },
67 },
68 },
69 },
70 }
71}
72
Lorenz Brun276a7462023-07-12 21:28:54 +020073// makeHTTPServerDeploymentSpec generates the deployment spec for the test HTTP
74// server.
75func makeHTTPServerDeploymentSpec(name string) *appsv1.Deployment {
76 oneVal := int32(1)
77 return &appsv1.Deployment{
78 ObjectMeta: metav1.ObjectMeta{Name: name},
79 Spec: appsv1.DeploymentSpec{
80 Selector: &metav1.LabelSelector{MatchLabels: map[string]string{
81 "name": name,
82 }},
83 Replicas: &oneVal,
84 Template: corev1.PodTemplateSpec{
85 ObjectMeta: metav1.ObjectMeta{
86 Labels: map[string]string{
87 "name": name,
88 },
89 },
90 Spec: corev1.PodSpec{
91 Containers: []corev1.Container{
92 {
93 Name: "test",
94 ImagePullPolicy: corev1.PullIfNotPresent,
95 Image: "test.monogon.internal/metropolis/test/e2e/httpserver/httpserver_image",
96 LivenessProbe: &corev1.Probe{
97 ProbeHandler: corev1.ProbeHandler{
98 HTTPGet: &corev1.HTTPGetAction{Port: intstr.FromInt(8080)},
99 },
100 },
101 },
102 },
103 },
104 },
105 },
106 }
107}
108
109// makeHTTPServerNodePortService generates the NodePort service spec
110// for testing the NodePort functionality.
111func makeHTTPServerNodePortService(name string) *corev1.Service {
112 return &corev1.Service{
113 ObjectMeta: metav1.ObjectMeta{Name: name},
114 Spec: corev1.ServiceSpec{
115 Type: corev1.ServiceTypeNodePort,
116 Selector: map[string]string{
117 "name": name,
118 },
119 Ports: []corev1.ServicePort{{
120 Name: name,
121 Protocol: corev1.ProtocolTCP,
122 Port: 80,
123 NodePort: 80,
124 TargetPort: intstr.FromInt(8080),
125 }},
126 },
127 }
128}
129
Serge Bazanski9104e382023-04-04 20:08:21 +0200130// makeSelftestSpec generates a Job spec for the E2E self-test image.
131func makeSelftestSpec(name string) *batchv1.Job {
132 one := int32(1)
133 return &batchv1.Job{
134 ObjectMeta: metav1.ObjectMeta{Name: name},
135 Spec: batchv1.JobSpec{
136 BackoffLimit: &one,
137 Template: corev1.PodTemplateSpec{
138 ObjectMeta: metav1.ObjectMeta{
139 Labels: map[string]string{
140 "job-name": name,
141 },
142 },
143 Spec: corev1.PodSpec{
144 Containers: []corev1.Container{
145 {
146 Name: "test",
Lorenz Brun150f24a2023-07-13 20:11:06 +0200147 ImagePullPolicy: corev1.PullIfNotPresent,
148 Image: "test.monogon.internal/metropolis/test/e2e/selftest/selftest_image",
Serge Bazanski9104e382023-04-04 20:08:21 +0200149 },
150 },
151 RestartPolicy: corev1.RestartPolicyOnFailure,
152 },
153 },
154 },
155 }
156}
157
Lorenz Brunfc5dbc62020-05-28 12:18:07 +0200158// makeTestStatefulSet generates a StatefulSet spec
Jan Schär652c2ad2024-11-19 17:40:50 +0100159func makeTestStatefulSet(name string) *appsv1.StatefulSet {
Lorenz Brunfc5dbc62020-05-28 12:18:07 +0200160 return &appsv1.StatefulSet{
161 ObjectMeta: metav1.ObjectMeta{Name: name},
162 Spec: appsv1.StatefulSetSpec{
163 Selector: &metav1.LabelSelector{MatchLabels: map[string]string{
164 "name": name,
165 }},
166 VolumeClaimTemplates: []corev1.PersistentVolumeClaim{
167 {
Jan Schär652c2ad2024-11-19 17:40:50 +0100168 ObjectMeta: metav1.ObjectMeta{Name: "vol-default"},
Lorenz Brunfc5dbc62020-05-28 12:18:07 +0200169 Spec: corev1.PersistentVolumeClaimSpec{
170 AccessModes: []corev1.PersistentVolumeAccessMode{corev1.ReadWriteOnce},
Tim Windelschmidtddc5e6a2024-04-23 23:44:34 +0200171 Resources: corev1.VolumeResourceRequirements{
Jan Schär652c2ad2024-11-19 17:40:50 +0100172 Requests: map[corev1.ResourceName]resource.Quantity{corev1.ResourceStorage: resource.MustParse("1Mi")},
Lorenz Brunfc5dbc62020-05-28 12:18:07 +0200173 },
Jan Schär652c2ad2024-11-19 17:40:50 +0100174 VolumeMode: ptr.To(corev1.PersistentVolumeFilesystem),
175 },
176 },
177 {
178 ObjectMeta: metav1.ObjectMeta{Name: "vol-local-strict"},
179 Spec: corev1.PersistentVolumeClaimSpec{
180 AccessModes: []corev1.PersistentVolumeAccessMode{corev1.ReadWriteOnce},
181 Resources: corev1.VolumeResourceRequirements{
182 Requests: map[corev1.ResourceName]resource.Quantity{corev1.ResourceStorage: resource.MustParse("5Mi")},
183 },
184 StorageClassName: ptr.To("local-strict"),
185 VolumeMode: ptr.To(corev1.PersistentVolumeFilesystem),
186 },
187 },
188 {
189 ObjectMeta: metav1.ObjectMeta{Name: "vol-readonly"},
190 Spec: corev1.PersistentVolumeClaimSpec{
191 AccessModes: []corev1.PersistentVolumeAccessMode{corev1.ReadWriteOnce},
192 Resources: corev1.VolumeResourceRequirements{
193 Requests: map[corev1.ResourceName]resource.Quantity{corev1.ResourceStorage: resource.MustParse("1Mi")},
194 },
195 VolumeMode: ptr.To(corev1.PersistentVolumeFilesystem),
Lorenz Brunfc5dbc62020-05-28 12:18:07 +0200196 },
197 },
Jan Schärbe70c922024-11-21 11:16:03 +0100198 {
199 ObjectMeta: metav1.ObjectMeta{Name: "vol-block"},
200 Spec: corev1.PersistentVolumeClaimSpec{
201 AccessModes: []corev1.PersistentVolumeAccessMode{corev1.ReadWriteOnce},
202 Resources: corev1.VolumeResourceRequirements{
203 Requests: map[corev1.ResourceName]resource.Quantity{corev1.ResourceStorage: resource.MustParse("1Mi")},
204 },
205 VolumeMode: ptr.To(corev1.PersistentVolumeBlock),
206 },
207 },
Lorenz Brunfc5dbc62020-05-28 12:18:07 +0200208 },
209 Template: corev1.PodTemplateSpec{
210 ObjectMeta: metav1.ObjectMeta{
211 Labels: map[string]string{
212 "name": name,
213 },
214 },
215 Spec: corev1.PodSpec{
216 Containers: []corev1.Container{
217 {
Serge Bazanski591d8082023-03-16 00:26:59 +0100218 Name: "test",
Jan Schär652c2ad2024-11-19 17:40:50 +0100219 ImagePullPolicy: corev1.PullIfNotPresent,
220 Image: "test.monogon.internal/metropolis/test/e2e/persistentvolume/persistentvolume_image",
221 VolumeMounts: []corev1.VolumeMount{
Jan Schärbe70c922024-11-21 11:16:03 +0100222 {
Jan Schär652c2ad2024-11-19 17:40:50 +0100223 Name: "vol-default",
224 MountPath: "/vol/default",
225 },
Jan Schärbe70c922024-11-21 11:16:03 +0100226 {
Jan Schär652c2ad2024-11-19 17:40:50 +0100227 Name: "vol-local-strict",
228 MountPath: "/vol/local-strict",
229 },
Jan Schärbe70c922024-11-21 11:16:03 +0100230 {
Jan Schär652c2ad2024-11-19 17:40:50 +0100231 Name: "vol-readonly",
232 ReadOnly: true,
233 MountPath: "/vol/readonly",
Lorenz Brunfc5dbc62020-05-28 12:18:07 +0200234 },
235 },
Jan Schärbe70c922024-11-21 11:16:03 +0100236 VolumeDevices: []corev1.VolumeDevice{
237 {
238 Name: "vol-block",
239 DevicePath: "/vol/block",
240 },
241 },
Lorenz Brunfc5dbc62020-05-28 12:18:07 +0200242 },
243 },
244 },
245 },
246 },
247 }
248}
Serge Bazanski9104e382023-04-04 20:08:21 +0200249
250func getPodLogLines(ctx context.Context, cs kubernetes.Interface, podName string, nlines int64) ([]string, error) {
251 logsR := cs.CoreV1().Pods("default").GetLogs(podName, &corev1.PodLogOptions{
252 TailLines: &nlines,
253 })
254 logs, err := logsR.Stream(ctx)
255 if err != nil {
256 return nil, fmt.Errorf("stream failed: %w", err)
257 }
258 var buf bytes.Buffer
259 _, err = io.Copy(&buf, logs)
260 if err != nil {
261 return nil, fmt.Errorf("copy failed: %w", err)
262 }
263 lineStr := strings.Trim(buf.String(), "\n")
264 lines := strings.Split(lineStr, "\n")
Jan Schär652c2ad2024-11-19 17:40:50 +0100265 if len(lines) > int(nlines) {
266 lines = lines[len(lines)-int(nlines):]
267 }
Serge Bazanski9104e382023-04-04 20:08:21 +0200268 return lines, nil
269}