blob: 264793a02229e7041e8dc991b15754e1d77a202a [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
17package e2e
18
19import (
20 "context"
21 "errors"
22 "fmt"
23 "time"
24
25 appsv1 "k8s.io/api/apps/v1"
26 corev1 "k8s.io/api/core/v1"
27 "k8s.io/apimachinery/pkg/api/resource"
28 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
29 "k8s.io/apimachinery/pkg/util/intstr"
30 "k8s.io/client-go/kubernetes"
31 "k8s.io/client-go/tools/clientcmd"
32
33 apipb "git.monogon.dev/source/nexantic.git/core/generated/api"
34)
35
36// getKubeClientSet gets a Kubeconfig from the debug API and creates a K8s ClientSet using it. The identity used has
37// the system:masters group and thus has RBAC access to everything.
38func getKubeClientSet(ctx context.Context, client apipb.NodeDebugServiceClient, port uint16) (kubernetes.Interface, error) {
39 var lastErr = errors.New("context canceled before any operation completed")
40 for {
41 res, err := client.GetDebugKubeconfig(context.Background(), &apipb.GetDebugKubeconfigRequest{Id: "debug-user", Groups: []string{"system:masters"}})
42 if err == nil {
43 rawClientConfig, err := clientcmd.NewClientConfigFromBytes([]byte(res.DebugKubeconfig))
44 if err != nil {
45 return nil, err // Invalid Kubeconfigs are immediately fatal
46 }
47
48 clientConfig, err := rawClientConfig.ClientConfig()
49 clientConfig.Host = fmt.Sprintf("localhost:%v", port)
50 clientSet, err := kubernetes.NewForConfig(clientConfig)
51 if err != nil {
52 return nil, err
53 }
54 return clientSet, nil
55 }
56 if err != nil && err == ctx.Err() {
57 return nil, lastErr
58 }
59 lastErr = err
60 select {
61 case <-ctx.Done():
62 return nil, lastErr
63 case <-time.After(1 * time.Second):
64 }
65 }
66}
67
68// makeTestDeploymentSpec generates a Deployment spec for a single pod running NGINX with a readiness probe. This allows
69// verifying that the control plane is capable of scheduling simple pods and that kubelet works, its runtime is set up
70// well enough to run a simple container and the network to the pod can pass readiness probe traffic.
71func makeTestDeploymentSpec(name string) *appsv1.Deployment {
72 return &appsv1.Deployment{
73 ObjectMeta: metav1.ObjectMeta{Name: name},
74 Spec: appsv1.DeploymentSpec{
75 Selector: &metav1.LabelSelector{MatchLabels: map[string]string{
76 "name": name,
77 }},
78 Template: corev1.PodTemplateSpec{
79 ObjectMeta: metav1.ObjectMeta{
80 Labels: map[string]string{
81 "name": name,
82 },
83 },
84 Spec: corev1.PodSpec{
85 Containers: []corev1.Container{
86 {
87 Name: "test",
88 // TODO(phab/T793): Build and preseed our own container images
89 Image: "nginx:alpine",
90 ReadinessProbe: &corev1.Probe{
91 Handler: corev1.Handler{
92 HTTPGet: &corev1.HTTPGetAction{Port: intstr.FromInt(80)},
93 },
94 },
95 },
96 },
97 },
98 },
99 },
100 }
101}
102
103// makeTestStatefulSet generates a StatefulSet spec
104func makeTestStatefulSet(name string) *appsv1.StatefulSet {
105 return &appsv1.StatefulSet{
106 ObjectMeta: metav1.ObjectMeta{Name: name},
107 Spec: appsv1.StatefulSetSpec{
108 Selector: &metav1.LabelSelector{MatchLabels: map[string]string{
109 "name": name,
110 }},
111 VolumeClaimTemplates: []corev1.PersistentVolumeClaim{
112 {
113 ObjectMeta: metav1.ObjectMeta{Name: "www"},
114 Spec: corev1.PersistentVolumeClaimSpec{
115 AccessModes: []corev1.PersistentVolumeAccessMode{corev1.ReadWriteOnce},
116 Resources: corev1.ResourceRequirements{
117 Requests: map[corev1.ResourceName]resource.Quantity{corev1.ResourceStorage: resource.MustParse("50Mi")},
118 },
119 },
120 },
121 },
122 Template: corev1.PodTemplateSpec{
123 ObjectMeta: metav1.ObjectMeta{
124 Labels: map[string]string{
125 "name": name,
126 },
127 },
128 Spec: corev1.PodSpec{
129 Containers: []corev1.Container{
130 {
131 Name: "test",
132 Image: "nginx:alpine",
133 ReadinessProbe: &corev1.Probe{
134 Handler: corev1.Handler{
135 HTTPGet: &corev1.HTTPGetAction{Port: intstr.FromInt(80)},
136 },
137 },
138 },
139 },
140 },
141 },
142 },
143 }
144}