blob: c8e04bf236ff7e6cbb32a30e7629a5ab7bbd68f6 [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 {
Lorenz Brun276a7462023-07-12 21:28:54 +020076 return &appsv1.Deployment{
77 ObjectMeta: metav1.ObjectMeta{Name: name},
78 Spec: appsv1.DeploymentSpec{
79 Selector: &metav1.LabelSelector{MatchLabels: map[string]string{
80 "name": name,
81 }},
Jan Schärd1a8b642024-12-03 17:40:41 +010082 Replicas: ptr.To(int32(1)),
Lorenz Brun276a7462023-07-12 21:28:54 +020083 Template: corev1.PodTemplateSpec{
84 ObjectMeta: metav1.ObjectMeta{
85 Labels: map[string]string{
86 "name": name,
87 },
88 },
89 Spec: corev1.PodSpec{
90 Containers: []corev1.Container{
91 {
92 Name: "test",
93 ImagePullPolicy: corev1.PullIfNotPresent,
94 Image: "test.monogon.internal/metropolis/test/e2e/httpserver/httpserver_image",
95 LivenessProbe: &corev1.Probe{
96 ProbeHandler: corev1.ProbeHandler{
97 HTTPGet: &corev1.HTTPGetAction{Port: intstr.FromInt(8080)},
98 },
99 },
100 },
101 },
102 },
103 },
104 },
105 }
106}
107
108// makeHTTPServerNodePortService generates the NodePort service spec
109// for testing the NodePort functionality.
110func makeHTTPServerNodePortService(name string) *corev1.Service {
111 return &corev1.Service{
112 ObjectMeta: metav1.ObjectMeta{Name: name},
113 Spec: corev1.ServiceSpec{
114 Type: corev1.ServiceTypeNodePort,
115 Selector: map[string]string{
116 "name": name,
117 },
118 Ports: []corev1.ServicePort{{
119 Name: name,
120 Protocol: corev1.ProtocolTCP,
121 Port: 80,
122 NodePort: 80,
123 TargetPort: intstr.FromInt(8080),
124 }},
125 },
126 }
127}
128
Serge Bazanski9104e382023-04-04 20:08:21 +0200129// makeSelftestSpec generates a Job spec for the E2E self-test image.
130func makeSelftestSpec(name string) *batchv1.Job {
Serge Bazanski9104e382023-04-04 20:08:21 +0200131 return &batchv1.Job{
132 ObjectMeta: metav1.ObjectMeta{Name: name},
133 Spec: batchv1.JobSpec{
Jan Schärd1a8b642024-12-03 17:40:41 +0100134 BackoffLimit: ptr.To(int32(1)),
Serge Bazanski9104e382023-04-04 20:08:21 +0200135 Template: corev1.PodTemplateSpec{
136 ObjectMeta: metav1.ObjectMeta{
137 Labels: map[string]string{
138 "job-name": name,
139 },
140 },
141 Spec: corev1.PodSpec{
142 Containers: []corev1.Container{
143 {
144 Name: "test",
Lorenz Brun150f24a2023-07-13 20:11:06 +0200145 ImagePullPolicy: corev1.PullIfNotPresent,
146 Image: "test.monogon.internal/metropolis/test/e2e/selftest/selftest_image",
Serge Bazanski9104e382023-04-04 20:08:21 +0200147 },
148 },
149 RestartPolicy: corev1.RestartPolicyOnFailure,
150 },
151 },
152 },
153 }
154}
155
Lorenz Brunfc5dbc62020-05-28 12:18:07 +0200156// makeTestStatefulSet generates a StatefulSet spec
Jan Schär73beb692024-11-27 17:47:09 +0100157func makeTestStatefulSet(name string, runtimeClass string) *appsv1.StatefulSet {
Lorenz Brunfc5dbc62020-05-28 12:18:07 +0200158 return &appsv1.StatefulSet{
159 ObjectMeta: metav1.ObjectMeta{Name: name},
160 Spec: appsv1.StatefulSetSpec{
161 Selector: &metav1.LabelSelector{MatchLabels: map[string]string{
162 "name": name,
163 }},
164 VolumeClaimTemplates: []corev1.PersistentVolumeClaim{
165 {
Jan Schär652c2ad2024-11-19 17:40:50 +0100166 ObjectMeta: metav1.ObjectMeta{Name: "vol-default"},
Lorenz Brunfc5dbc62020-05-28 12:18:07 +0200167 Spec: corev1.PersistentVolumeClaimSpec{
168 AccessModes: []corev1.PersistentVolumeAccessMode{corev1.ReadWriteOnce},
Tim Windelschmidtddc5e6a2024-04-23 23:44:34 +0200169 Resources: corev1.VolumeResourceRequirements{
Jan Schär652c2ad2024-11-19 17:40:50 +0100170 Requests: map[corev1.ResourceName]resource.Quantity{corev1.ResourceStorage: resource.MustParse("1Mi")},
Lorenz Brunfc5dbc62020-05-28 12:18:07 +0200171 },
Jan Schär652c2ad2024-11-19 17:40:50 +0100172 VolumeMode: ptr.To(corev1.PersistentVolumeFilesystem),
173 },
174 },
175 {
Jan Schär652c2ad2024-11-19 17:40:50 +0100176 ObjectMeta: metav1.ObjectMeta{Name: "vol-readonly"},
177 Spec: corev1.PersistentVolumeClaimSpec{
178 AccessModes: []corev1.PersistentVolumeAccessMode{corev1.ReadWriteOnce},
179 Resources: corev1.VolumeResourceRequirements{
180 Requests: map[corev1.ResourceName]resource.Quantity{corev1.ResourceStorage: resource.MustParse("1Mi")},
181 },
182 VolumeMode: ptr.To(corev1.PersistentVolumeFilesystem),
Lorenz Brunfc5dbc62020-05-28 12:18:07 +0200183 },
184 },
Jan Schärbe70c922024-11-21 11:16:03 +0100185 {
186 ObjectMeta: metav1.ObjectMeta{Name: "vol-block"},
187 Spec: corev1.PersistentVolumeClaimSpec{
188 AccessModes: []corev1.PersistentVolumeAccessMode{corev1.ReadWriteOnce},
189 Resources: corev1.VolumeResourceRequirements{
190 Requests: map[corev1.ResourceName]resource.Quantity{corev1.ResourceStorage: resource.MustParse("1Mi")},
191 },
192 VolumeMode: ptr.To(corev1.PersistentVolumeBlock),
193 },
194 },
Lorenz Brunfc5dbc62020-05-28 12:18:07 +0200195 },
196 Template: corev1.PodTemplateSpec{
197 ObjectMeta: metav1.ObjectMeta{
198 Labels: map[string]string{
199 "name": name,
200 },
201 },
202 Spec: corev1.PodSpec{
203 Containers: []corev1.Container{
204 {
Serge Bazanski591d8082023-03-16 00:26:59 +0100205 Name: "test",
Jan Schär652c2ad2024-11-19 17:40:50 +0100206 ImagePullPolicy: corev1.PullIfNotPresent,
207 Image: "test.monogon.internal/metropolis/test/e2e/persistentvolume/persistentvolume_image",
Jan Schär73beb692024-11-27 17:47:09 +0100208 Args: []string{"-runtimeclass", runtimeClass},
Jan Schär652c2ad2024-11-19 17:40:50 +0100209 VolumeMounts: []corev1.VolumeMount{
Jan Schärbe70c922024-11-21 11:16:03 +0100210 {
Jan Schär652c2ad2024-11-19 17:40:50 +0100211 Name: "vol-default",
212 MountPath: "/vol/default",
213 },
Jan Schärbe70c922024-11-21 11:16:03 +0100214 {
Jan Schär652c2ad2024-11-19 17:40:50 +0100215 Name: "vol-readonly",
216 ReadOnly: true,
217 MountPath: "/vol/readonly",
Lorenz Brunfc5dbc62020-05-28 12:18:07 +0200218 },
219 },
Jan Schärbe70c922024-11-21 11:16:03 +0100220 VolumeDevices: []corev1.VolumeDevice{
221 {
222 Name: "vol-block",
223 DevicePath: "/vol/block",
224 },
225 },
Lorenz Brunfc5dbc62020-05-28 12:18:07 +0200226 },
227 },
Jan Schär73beb692024-11-27 17:47:09 +0100228 RuntimeClassName: ptr.To(runtimeClass),
Lorenz Brunfc5dbc62020-05-28 12:18:07 +0200229 },
230 },
231 },
232 }
233}
Serge Bazanski9104e382023-04-04 20:08:21 +0200234
235func getPodLogLines(ctx context.Context, cs kubernetes.Interface, podName string, nlines int64) ([]string, error) {
236 logsR := cs.CoreV1().Pods("default").GetLogs(podName, &corev1.PodLogOptions{
237 TailLines: &nlines,
238 })
239 logs, err := logsR.Stream(ctx)
240 if err != nil {
241 return nil, fmt.Errorf("stream failed: %w", err)
242 }
243 var buf bytes.Buffer
244 _, err = io.Copy(&buf, logs)
245 if err != nil {
246 return nil, fmt.Errorf("copy failed: %w", err)
247 }
248 lineStr := strings.Trim(buf.String(), "\n")
249 lines := strings.Split(lineStr, "\n")
Jan Schär652c2ad2024-11-19 17:40:50 +0100250 if len(lines) > int(nlines) {
251 lines = lines[len(lines)-int(nlines):]
252 }
Serge Bazanski9104e382023-04-04 20:08:21 +0200253 return lines, nil
254}