blob: 85f8909bb09bbf9c68dd32fb414c84340dcadfa9 [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 },
198 },
199 Template: corev1.PodTemplateSpec{
200 ObjectMeta: metav1.ObjectMeta{
201 Labels: map[string]string{
202 "name": name,
203 },
204 },
205 Spec: corev1.PodSpec{
206 Containers: []corev1.Container{
207 {
Serge Bazanski591d8082023-03-16 00:26:59 +0100208 Name: "test",
Jan Schär652c2ad2024-11-19 17:40:50 +0100209 ImagePullPolicy: corev1.PullIfNotPresent,
210 Image: "test.monogon.internal/metropolis/test/e2e/persistentvolume/persistentvolume_image",
211 VolumeMounts: []corev1.VolumeMount{
212 corev1.VolumeMount{
213 Name: "vol-default",
214 MountPath: "/vol/default",
215 },
216 corev1.VolumeMount{
217 Name: "vol-local-strict",
218 MountPath: "/vol/local-strict",
219 },
220 corev1.VolumeMount{
221 Name: "vol-readonly",
222 ReadOnly: true,
223 MountPath: "/vol/readonly",
Lorenz Brunfc5dbc62020-05-28 12:18:07 +0200224 },
225 },
226 },
227 },
228 },
229 },
230 },
231 }
232}
Serge Bazanski9104e382023-04-04 20:08:21 +0200233
234func getPodLogLines(ctx context.Context, cs kubernetes.Interface, podName string, nlines int64) ([]string, error) {
235 logsR := cs.CoreV1().Pods("default").GetLogs(podName, &corev1.PodLogOptions{
236 TailLines: &nlines,
237 })
238 logs, err := logsR.Stream(ctx)
239 if err != nil {
240 return nil, fmt.Errorf("stream failed: %w", err)
241 }
242 var buf bytes.Buffer
243 _, err = io.Copy(&buf, logs)
244 if err != nil {
245 return nil, fmt.Errorf("copy failed: %w", err)
246 }
247 lineStr := strings.Trim(buf.String(), "\n")
248 lines := strings.Split(lineStr, "\n")
Jan Schär652c2ad2024-11-19 17:40:50 +0100249 if len(lines) > int(nlines) {
250 lines = lines[len(lines)-int(nlines):]
251 }
Serge Bazanski9104e382023-04-04 20:08:21 +0200252 return lines, nil
253}