Skip to content

Commit 8f589f2

Browse files
committed
feat: support for kubernetes v1.21.0
1 parent 73a8c2b commit 8f589f2

File tree

10 files changed

+256
-40
lines changed

10 files changed

+256
-40
lines changed

chart/values.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ syncer:
1010
resources: {}
1111

1212
vcluster:
13-
image: rancher/k3s:v1.19.1-k3s1
13+
image: rancher/k3s:v1.21.0-k3s1
1414
command:
1515
- /bin/k3s
1616
baseArgs:

cmd/vclusterctl/cmd/create.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
)
2323

2424
var VersionMap = map[string]string{
25+
"1.21": "rancher/k3s:v1.21.0-k3s1",
2526
"1.20": "rancher/k3s:v1.20.4-k3s1",
2627
"1.19": "rancher/k3s:v1.19.8-k3s1",
2728
"1.18": "rancher/k3s:v1.18.16-k3s1",
@@ -208,10 +209,10 @@ func getReleaseValues(client kubernetes.Interface, namespace string, disableIngr
208209

209210
image, ok := VersionMap[serverVersionString]
210211
if !ok {
211-
if serverMinorInt > 20 {
212-
log.Infof("officially unsupported host server version %s, will fallback to virtual cluster version v1.20", serverVersionString)
213-
image = VersionMap["1.20"]
214-
serverVersionString = "1.20"
212+
if serverMinorInt > 21 {
213+
log.Infof("officially unsupported host server version %s, will fallback to virtual cluster version v1.21", serverVersionString)
214+
image = VersionMap["1.21"]
215+
serverVersionString = "1.21"
215216
} else {
216217
log.Infof("officially unsupported host server version %s, will fallback to virtual cluster version v1.16", serverVersionString)
217218
image = VersionMap["1.16"]

cmd/vclusterctl/cmd/list.go

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
package cmd
2+
3+
import (
4+
"context"
5+
"github.com/loft-sh/vcluster/cmd/vclusterctl/flags"
6+
"github.com/loft-sh/vcluster/cmd/vclusterctl/log"
7+
"github.com/pkg/errors"
8+
"github.com/spf13/cobra"
9+
kerrors "k8s.io/apimachinery/pkg/api/errors"
10+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
11+
"k8s.io/client-go/kubernetes"
12+
"k8s.io/client-go/tools/clientcmd"
13+
)
14+
15+
// ListCmd holds the login cmd flags
16+
type ListCmd struct {
17+
*flags.GlobalFlags
18+
19+
Namespace string
20+
log log.Logger
21+
}
22+
23+
// NewListCmd creates a new command
24+
func NewListCmd(globalFlags *flags.GlobalFlags) *cobra.Command {
25+
cmd := &ListCmd{
26+
GlobalFlags: globalFlags,
27+
log: log.GetInstance(),
28+
}
29+
30+
cobraCmd := &cobra.Command{
31+
Use: "list",
32+
Short: "Lists all virtual clusters",
33+
Long: `
34+
#######################################################
35+
#################### vcluster list ####################
36+
#######################################################
37+
Lists all virtual clusters
38+
39+
Example:
40+
vcluster list
41+
vcluster list --namespace test
42+
#######################################################
43+
`,
44+
Args: cobra.NoArgs,
45+
RunE: func(cobraCmd *cobra.Command, args []string) error {
46+
return cmd.Run(cobraCmd, args)
47+
},
48+
}
49+
50+
cobraCmd.Flags().StringVarP(&cmd.Namespace, "namespace", "n", "", "The namespace the vcluster was created in")
51+
return cobraCmd
52+
}
53+
54+
// Run executes the functionality
55+
func (cmd *ListCmd) Run(cobraCmd *cobra.Command, args []string) error {
56+
// first load the kube config
57+
kubeClientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(clientcmd.NewDefaultClientConfigLoadingRules(), &clientcmd.ConfigOverrides{})
58+
namespace := metav1.NamespaceAll
59+
if cmd.Namespace != "" {
60+
namespace = cmd.Namespace
61+
}
62+
63+
// get all statefulsets with the label app=vcluster
64+
restConfig, err := kubeClientConfig.ClientConfig()
65+
if err != nil {
66+
return err
67+
}
68+
client, err := kubernetes.NewForConfig(restConfig)
69+
if err != nil {
70+
return err
71+
}
72+
73+
statefulSets, err := client.AppsV1().StatefulSets(namespace).List(context.Background(), metav1.ListOptions{LabelSelector: "app=vcluster"})
74+
if err != nil {
75+
if kerrors.IsForbidden(err) {
76+
// try the current namespace instead
77+
namespace, _, err = kubeClientConfig.Namespace()
78+
if err != nil {
79+
return err
80+
} else if namespace == "" {
81+
namespace = "default"
82+
}
83+
84+
statefulSets, err = client.AppsV1().StatefulSets(namespace).List(context.Background(), metav1.ListOptions{LabelSelector: "app=vcluster"})
85+
if err != nil {
86+
return err
87+
}
88+
} else {
89+
return errors.Wrap(err, "list stateful sets")
90+
}
91+
}
92+
93+
header := []string{"NAME", "NAMESPACE", "CREATED"}
94+
values := [][]string{}
95+
for _, s := range statefulSets.Items {
96+
values = append(values, []string{
97+
s.Name,
98+
s.Namespace,
99+
s.CreationTimestamp.String(),
100+
})
101+
}
102+
103+
log.PrintTable(cmd.log, header, values)
104+
return nil
105+
}

cmd/vclusterctl/cmd/root.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ func BuildRoot(log log.Logger) *cobra.Command {
5151
// add top level commands
5252
rootCmd.AddCommand(NewConnectCmd(globalFlags))
5353
rootCmd.AddCommand(NewCreateCmd(globalFlags))
54+
rootCmd.AddCommand(NewListCmd(globalFlags))
5455
rootCmd.AddCommand(NewDeleteCmd(globalFlags))
5556

5657
return rootCmd

pkg/constants/indices.go

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
package constants
22

33
const (
4-
IndexByVName = "IndexByVName"
5-
IndexByAssigned = "IndexByAssigned"
6-
IndexByStorageClass = "IndexByStorageClass"
7-
8-
IndexBySecret = "IndexBySecret"
9-
IndexByConfigMap = "IndexByConfigMap"
4+
IndexByVName = "IndexByVName"
5+
IndexByAssigned = "IndexByAssigned"
6+
IndexByStorageClass = "IndexByStorageClass"
7+
IndexByIngressSecret = "IndexByIngressSecret"
8+
IndexByConfigMap = "IndexByConfigMap"
109
)

pkg/controllers/resources/pods/syncer.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ func (s *syncer) translatePod(vPod *corev1.Pod, pPod *corev1.Pod) error {
261261
ptrServiceList = append(ptrServiceList, &s)
262262
}
263263

264-
return translatePod(pPod, vPod, ptrServiceList, s.clusterDomain, dnsIP, kubeIP, s.serviceAccountName, s.translateImages, s.overrideHosts, s.overrideHostsImage)
264+
return translatePod(pPod, vPod, s.virtualClient, ptrServiceList, s.clusterDomain, dnsIP, kubeIP, s.serviceAccountName, s.translateImages, s.overrideHosts, s.overrideHostsImage)
265265
}
266266

267267
func (s *syncer) findKubernetesIP() (string, error) {

pkg/controllers/resources/pods/translate.go

Lines changed: 90 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
package pods
22

33
import (
4+
"context"
45
"fmt"
6+
"github.com/pkg/errors"
7+
"sigs.k8s.io/controller-runtime/pkg/client"
58
"sort"
69
"strings"
710

@@ -25,7 +28,7 @@ const (
2528
HostsRewriteContainerName = "vcluster-rewrite-hosts"
2629
)
2730

28-
func translatePod(pPod *corev1.Pod, vPod *corev1.Pod, services []*corev1.Service, clusterDomain, dnsIP, kubeIP, serviceAccount string, translator ImageTranslator, enableOverrideHosts bool, overrideHostsImage string) error {
31+
func translatePod(pPod *corev1.Pod, vPod *corev1.Pod, vClient client.Client, services []*corev1.Service, clusterDomain, dnsIP, kubeIP, serviceAccount string, translator ImageTranslator, enableOverrideHosts bool, overrideHostsImage string) error {
2932
pPod.Status = corev1.PodStatus{}
3033
pPod.Spec.DeprecatedServiceAccount = ""
3134
pPod.Spec.ServiceAccountName = serviceAccount
@@ -188,6 +191,13 @@ func translatePod(pPod *corev1.Pod, vPod *corev1.Pod, services []*corev1.Service
188191
if pPod.Spec.Volumes[i].PersistentVolumeClaim != nil {
189192
pPod.Spec.Volumes[i].PersistentVolumeClaim.ClaimName = translate.PhysicalName(pPod.Spec.Volumes[i].PersistentVolumeClaim.ClaimName, vPod.Namespace)
190193
}
194+
if pPod.Spec.Volumes[i].Projected != nil {
195+
// get old service account name
196+
err := translateProjectedVolume(pPod.Spec.Volumes[i].Projected, vClient, vPod)
197+
if err != nil {
198+
return err
199+
}
200+
}
191201
}
192202

193203
// we add an annotation if the pod has a replica set or statefulset owner
@@ -205,6 +215,85 @@ func translatePod(pPod *corev1.Pod, vPod *corev1.Pod, services []*corev1.Service
205215
return nil
206216
}
207217

218+
func secretNameFromServiceAccount(vClient client.Client, vPod *corev1.Pod) (string, error) {
219+
vServiceAccount := ""
220+
if vPod.Spec.ServiceAccountName != "" {
221+
vServiceAccount = vPod.Spec.ServiceAccountName
222+
} else if vPod.Spec.DeprecatedServiceAccount != "" {
223+
vServiceAccount = vPod.Spec.DeprecatedServiceAccount
224+
}
225+
226+
secretList := &corev1.SecretList{}
227+
err := vClient.List(context.Background(), secretList, client.InNamespace(vPod.Namespace))
228+
if err != nil {
229+
return "", errors.Wrap(err, "list secrets in "+vPod.Namespace)
230+
}
231+
for _, secret := range secretList.Items {
232+
if secret.Annotations["kubernetes.io/service-account.name"] == vServiceAccount {
233+
return secret.Name, nil
234+
}
235+
}
236+
237+
return "", nil
238+
}
239+
240+
func translateProjectedVolume(projectedVolume *corev1.ProjectedVolumeSource, vClient client.Client, vPod *corev1.Pod) error {
241+
for i := range projectedVolume.Sources {
242+
if projectedVolume.Sources[i].Secret != nil {
243+
projectedVolume.Sources[i].Secret.Name = translate.PhysicalName(projectedVolume.Sources[i].Secret.Name, vPod.Namespace)
244+
}
245+
if projectedVolume.Sources[i].ConfigMap != nil {
246+
projectedVolume.Sources[i].ConfigMap.Name = translate.PhysicalName(projectedVolume.Sources[i].ConfigMap.Name, vPod.Namespace)
247+
}
248+
if projectedVolume.Sources[i].DownwardAPI != nil {
249+
for j := range projectedVolume.Sources[i].DownwardAPI.Items {
250+
translateFieldRef(projectedVolume.Sources[i].DownwardAPI.Items[j].FieldRef)
251+
}
252+
}
253+
if projectedVolume.Sources[i].ServiceAccountToken != nil {
254+
secretName, err := secretNameFromServiceAccount(vClient, vPod)
255+
if err != nil {
256+
return err
257+
} else if secretName == "" {
258+
return fmt.Errorf("couldn't find service account secret for pod %s/%s", vPod.Namespace, vPod.Name)
259+
}
260+
261+
allRights := int32(0644)
262+
projectedVolume.Sources[i].Secret = &corev1.SecretProjection{
263+
LocalObjectReference: corev1.LocalObjectReference{
264+
Name: translate.PhysicalName(secretName, vPod.Namespace),
265+
},
266+
Items: []corev1.KeyToPath{
267+
{
268+
Path: projectedVolume.Sources[i].ServiceAccountToken.Path,
269+
Key: "token",
270+
Mode: &allRights,
271+
},
272+
},
273+
}
274+
projectedVolume.Sources[i].ServiceAccountToken = nil
275+
}
276+
}
277+
278+
return nil
279+
}
280+
281+
func translateFieldRef(fieldSelector *corev1.ObjectFieldSelector) {
282+
if fieldSelector == nil {
283+
return
284+
}
285+
switch fieldSelector.FieldPath {
286+
case "metadata.name":
287+
fieldSelector.FieldPath = "metadata.annotations['" + NameAnnotation + "']"
288+
case "metadata.namespace":
289+
fieldSelector.FieldPath = "metadata.annotations['" + NamespaceAnnotation + "']"
290+
case "metadata.uid":
291+
fieldSelector.FieldPath = "metadata.annotations['" + UIDAnnotation + "']"
292+
case "spec.serviceAccountName":
293+
fieldSelector.FieldPath = "metadata.annotations['" + ServiceAccountNameAnnotation + "']"
294+
}
295+
}
296+
208297
func stripHostRewriteContainer(pPod *corev1.Pod) *corev1.Pod {
209298
if pPod.Annotations == nil || pPod.Annotations[HostsRewrittenAnnotation] != "true" {
210299
return pPod

pkg/controllers/resources/pods/util.go

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package pods
33
import (
44
"github.com/loft-sh/vcluster/pkg/util/translate"
55
corev1 "k8s.io/api/core/v1"
6+
"sigs.k8s.io/controller-runtime/pkg/client"
67
)
78

89
func ConfigNamesFromPod(pod *corev1.Pod) []string {
@@ -20,11 +21,18 @@ func ConfigNamesFromPod(pod *corev1.Pod) []string {
2021
if pod.Spec.Volumes[i].ConfigMap != nil {
2122
configMaps = append(configMaps, pod.Namespace+"/"+pod.Spec.Volumes[i].ConfigMap.Name)
2223
}
24+
if pod.Spec.Volumes[i].Projected != nil {
25+
for j := range pod.Spec.Volumes[i].Projected.Sources {
26+
if pod.Spec.Volumes[i].Projected.Sources[j].ConfigMap != nil {
27+
configMaps = append(configMaps, pod.Namespace+"/"+pod.Spec.Volumes[i].Projected.Sources[j].ConfigMap.Name)
28+
}
29+
}
30+
}
2331
}
2432
return translate.UniqueSlice(configMaps)
2533
}
2634

27-
func SecretNamesFromPod(pod *corev1.Pod) []string {
35+
func SecretNamesFromPod(vClient client.Client, pod *corev1.Pod) []string {
2836
secrets := []string{}
2937
for _, c := range pod.Spec.Containers {
3038
secrets = append(secrets, SecretNamesFromContainer(pod.Namespace, &c)...)
@@ -42,6 +50,19 @@ func SecretNamesFromPod(pod *corev1.Pod) []string {
4250
if pod.Spec.Volumes[i].Secret != nil {
4351
secrets = append(secrets, pod.Namespace+"/"+pod.Spec.Volumes[i].Secret.SecretName)
4452
}
53+
if pod.Spec.Volumes[i].Projected != nil {
54+
for j := range pod.Spec.Volumes[i].Projected.Sources {
55+
if pod.Spec.Volumes[i].Projected.Sources[j].Secret != nil {
56+
secrets = append(secrets, pod.Namespace+"/"+pod.Spec.Volumes[i].Projected.Sources[j].Secret.Name)
57+
}
58+
if pod.Spec.Volumes[i].Projected.Sources[j].ServiceAccountToken != nil {
59+
secretName, err := secretNameFromServiceAccount(vClient, pod)
60+
if err == nil && secretName != "" {
61+
secrets = append(secrets, pod.Namespace+"/"+secretName)
62+
}
63+
}
64+
}
65+
}
4566
}
4667
return translate.UniqueSlice(secrets)
4768
}

0 commit comments

Comments
 (0)