blob: 9c6711748614d477b09c2dd5ca7cace1c9f336a2 [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är73beb692024-11-27 17:47:09 +0100159func makeTestStatefulSet(name string, runtimeClass 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 {
Jan Schär652c2ad2024-11-19 17:40:50 +0100178 ObjectMeta: metav1.ObjectMeta{Name: "vol-readonly"},
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("1Mi")},
183 },
184 VolumeMode: ptr.To(corev1.PersistentVolumeFilesystem),
Lorenz Brunfc5dbc62020-05-28 12:18:07 +0200185 },
186 },
Jan Schärbe70c922024-11-21 11:16:03 +0100187 {
188 ObjectMeta: metav1.ObjectMeta{Name: "vol-block"},
189 Spec: corev1.PersistentVolumeClaimSpec{
190 AccessModes: []corev1.PersistentVolumeAccessMode{corev1.ReadWriteOnce},
191 Resources: corev1.VolumeResourceRequirements{
192 Requests: map[corev1.ResourceName]resource.Quantity{corev1.ResourceStorage: resource.MustParse("1Mi")},
193 },
194 VolumeMode: ptr.To(corev1.PersistentVolumeBlock),
195 },
196 },
Lorenz Brunfc5dbc62020-05-28 12:18:07 +0200197 },
198 Template: corev1.PodTemplateSpec{
199 ObjectMeta: metav1.ObjectMeta{
200 Labels: map[string]string{
201 "name": name,
202 },
203 },
204 Spec: corev1.PodSpec{
205 Containers: []corev1.Container{
206 {
Serge Bazanski591d8082023-03-16 00:26:59 +0100207 Name: "test",
Jan Schär652c2ad2024-11-19 17:40:50 +0100208 ImagePullPolicy: corev1.PullIfNotPresent,
209 Image: "test.monogon.internal/metropolis/test/e2e/persistentvolume/persistentvolume_image",
Jan Schär73beb692024-11-27 17:47:09 +0100210 Args: []string{"-runtimeclass", runtimeClass},
Jan Schär652c2ad2024-11-19 17:40:50 +0100211 VolumeMounts: []corev1.VolumeMount{
Jan Schärbe70c922024-11-21 11:16:03 +0100212 {
Jan Schär652c2ad2024-11-19 17:40:50 +0100213 Name: "vol-default",
214 MountPath: "/vol/default",
215 },
Jan Schärbe70c922024-11-21 11:16:03 +0100216 {
Jan Schär652c2ad2024-11-19 17:40:50 +0100217 Name: "vol-readonly",
218 ReadOnly: true,
219 MountPath: "/vol/readonly",
Lorenz Brunfc5dbc62020-05-28 12:18:07 +0200220 },
221 },
Jan Schärbe70c922024-11-21 11:16:03 +0100222 VolumeDevices: []corev1.VolumeDevice{
223 {
224 Name: "vol-block",
225 DevicePath: "/vol/block",
226 },
227 },
Lorenz Brunfc5dbc62020-05-28 12:18:07 +0200228 },
229 },
Jan Schär73beb692024-11-27 17:47:09 +0100230 RuntimeClassName: ptr.To(runtimeClass),
Lorenz Brunfc5dbc62020-05-28 12:18:07 +0200231 },
232 },
233 },
234 }
235}
Serge Bazanski9104e382023-04-04 20:08:21 +0200236
237func getPodLogLines(ctx context.Context, cs kubernetes.Interface, podName string, nlines int64) ([]string, error) {
238 logsR := cs.CoreV1().Pods("default").GetLogs(podName, &corev1.PodLogOptions{
239 TailLines: &nlines,
240 })
241 logs, err := logsR.Stream(ctx)
242 if err != nil {
243 return nil, fmt.Errorf("stream failed: %w", err)
244 }
245 var buf bytes.Buffer
246 _, err = io.Copy(&buf, logs)
247 if err != nil {
248 return nil, fmt.Errorf("copy failed: %w", err)
249 }
250 lineStr := strings.Trim(buf.String(), "\n")
251 lines := strings.Split(lineStr, "\n")
Jan Schär652c2ad2024-11-19 17:40:50 +0100252 if len(lines) > int(nlines) {
253 lines = lines[len(lines)-int(nlines):]
254 }
Serge Bazanski9104e382023-04-04 20:08:21 +0200255 return lines, nil
256}