Skip to content

Commit

Permalink
Merge pull request #7 from loft-sh/kubernetes-update
Browse files Browse the repository at this point in the history
feat: support for kubernetes v1.21.0
  • Loading branch information
FabianKramm authored May 7, 2021
2 parents 24eeae5 + 8f589f2 commit c8422e3
Show file tree
Hide file tree
Showing 10 changed files with 256 additions and 40 deletions.
2 changes: 1 addition & 1 deletion chart/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ syncer:
resources: {}

vcluster:
image: rancher/k3s:v1.19.1-k3s1
image: rancher/k3s:v1.21.0-k3s1
command:
- /bin/k3s
baseArgs:
Expand Down
9 changes: 5 additions & 4 deletions cmd/vclusterctl/cmd/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
)

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

image, ok := VersionMap[serverVersionString]
if !ok {
if serverMinorInt > 20 {
log.Infof("officially unsupported host server version %s, will fallback to virtual cluster version v1.20", serverVersionString)
image = VersionMap["1.20"]
serverVersionString = "1.20"
if serverMinorInt > 21 {
log.Infof("officially unsupported host server version %s, will fallback to virtual cluster version v1.21", serverVersionString)
image = VersionMap["1.21"]
serverVersionString = "1.21"
} else {
log.Infof("officially unsupported host server version %s, will fallback to virtual cluster version v1.16", serverVersionString)
image = VersionMap["1.16"]
Expand Down
105 changes: 105 additions & 0 deletions cmd/vclusterctl/cmd/list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package cmd

import (
"context"
"github.com/loft-sh/vcluster/cmd/vclusterctl/flags"
"github.com/loft-sh/vcluster/cmd/vclusterctl/log"
"github.com/pkg/errors"
"github.com/spf13/cobra"
kerrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
)

// ListCmd holds the login cmd flags
type ListCmd struct {
*flags.GlobalFlags

Namespace string
log log.Logger
}

// NewListCmd creates a new command
func NewListCmd(globalFlags *flags.GlobalFlags) *cobra.Command {
cmd := &ListCmd{
GlobalFlags: globalFlags,
log: log.GetInstance(),
}

cobraCmd := &cobra.Command{
Use: "list",
Short: "Lists all virtual clusters",
Long: `
#######################################################
#################### vcluster list ####################
#######################################################
Lists all virtual clusters
Example:
vcluster list
vcluster list --namespace test
#######################################################
`,
Args: cobra.NoArgs,
RunE: func(cobraCmd *cobra.Command, args []string) error {
return cmd.Run(cobraCmd, args)
},
}

cobraCmd.Flags().StringVarP(&cmd.Namespace, "namespace", "n", "", "The namespace the vcluster was created in")
return cobraCmd
}

// Run executes the functionality
func (cmd *ListCmd) Run(cobraCmd *cobra.Command, args []string) error {
// first load the kube config
kubeClientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(clientcmd.NewDefaultClientConfigLoadingRules(), &clientcmd.ConfigOverrides{})
namespace := metav1.NamespaceAll
if cmd.Namespace != "" {
namespace = cmd.Namespace
}

// get all statefulsets with the label app=vcluster
restConfig, err := kubeClientConfig.ClientConfig()
if err != nil {
return err
}
client, err := kubernetes.NewForConfig(restConfig)
if err != nil {
return err
}

statefulSets, err := client.AppsV1().StatefulSets(namespace).List(context.Background(), metav1.ListOptions{LabelSelector: "app=vcluster"})
if err != nil {
if kerrors.IsForbidden(err) {
// try the current namespace instead
namespace, _, err = kubeClientConfig.Namespace()
if err != nil {
return err
} else if namespace == "" {
namespace = "default"
}

statefulSets, err = client.AppsV1().StatefulSets(namespace).List(context.Background(), metav1.ListOptions{LabelSelector: "app=vcluster"})
if err != nil {
return err
}
} else {
return errors.Wrap(err, "list stateful sets")
}
}

header := []string{"NAME", "NAMESPACE", "CREATED"}
values := [][]string{}
for _, s := range statefulSets.Items {
values = append(values, []string{
s.Name,
s.Namespace,
s.CreationTimestamp.String(),
})
}

log.PrintTable(cmd.log, header, values)
return nil
}
1 change: 1 addition & 0 deletions cmd/vclusterctl/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ func BuildRoot(log log.Logger) *cobra.Command {
// add top level commands
rootCmd.AddCommand(NewConnectCmd(globalFlags))
rootCmd.AddCommand(NewCreateCmd(globalFlags))
rootCmd.AddCommand(NewListCmd(globalFlags))
rootCmd.AddCommand(NewDeleteCmd(globalFlags))

return rootCmd
Expand Down
11 changes: 5 additions & 6 deletions pkg/constants/indices.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
package constants

const (
IndexByVName = "IndexByVName"
IndexByAssigned = "IndexByAssigned"
IndexByStorageClass = "IndexByStorageClass"

IndexBySecret = "IndexBySecret"
IndexByConfigMap = "IndexByConfigMap"
IndexByVName = "IndexByVName"
IndexByAssigned = "IndexByAssigned"
IndexByStorageClass = "IndexByStorageClass"
IndexByIngressSecret = "IndexByIngressSecret"
IndexByConfigMap = "IndexByConfigMap"
)
2 changes: 1 addition & 1 deletion pkg/controllers/resources/pods/syncer.go
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ func (s *syncer) translatePod(vPod *corev1.Pod, pPod *corev1.Pod) error {
ptrServiceList = append(ptrServiceList, &s)
}

return translatePod(pPod, vPod, ptrServiceList, s.clusterDomain, dnsIP, kubeIP, s.serviceAccountName, s.translateImages, s.overrideHosts, s.overrideHostsImage)
return translatePod(pPod, vPod, s.virtualClient, ptrServiceList, s.clusterDomain, dnsIP, kubeIP, s.serviceAccountName, s.translateImages, s.overrideHosts, s.overrideHostsImage)
}

func (s *syncer) findKubernetesIP() (string, error) {
Expand Down
91 changes: 90 additions & 1 deletion pkg/controllers/resources/pods/translate.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package pods

import (
"context"
"fmt"
"github.com/pkg/errors"
"sigs.k8s.io/controller-runtime/pkg/client"
"sort"
"strings"

Expand All @@ -25,7 +28,7 @@ const (
HostsRewriteContainerName = "vcluster-rewrite-hosts"
)

func translatePod(pPod *corev1.Pod, vPod *corev1.Pod, services []*corev1.Service, clusterDomain, dnsIP, kubeIP, serviceAccount string, translator ImageTranslator, enableOverrideHosts bool, overrideHostsImage string) error {
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 {
pPod.Status = corev1.PodStatus{}
pPod.Spec.DeprecatedServiceAccount = ""
pPod.Spec.ServiceAccountName = serviceAccount
Expand Down Expand Up @@ -188,6 +191,13 @@ func translatePod(pPod *corev1.Pod, vPod *corev1.Pod, services []*corev1.Service
if pPod.Spec.Volumes[i].PersistentVolumeClaim != nil {
pPod.Spec.Volumes[i].PersistentVolumeClaim.ClaimName = translate.PhysicalName(pPod.Spec.Volumes[i].PersistentVolumeClaim.ClaimName, vPod.Namespace)
}
if pPod.Spec.Volumes[i].Projected != nil {
// get old service account name
err := translateProjectedVolume(pPod.Spec.Volumes[i].Projected, vClient, vPod)
if err != nil {
return err
}
}
}

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

func secretNameFromServiceAccount(vClient client.Client, vPod *corev1.Pod) (string, error) {
vServiceAccount := ""
if vPod.Spec.ServiceAccountName != "" {
vServiceAccount = vPod.Spec.ServiceAccountName
} else if vPod.Spec.DeprecatedServiceAccount != "" {
vServiceAccount = vPod.Spec.DeprecatedServiceAccount
}

secretList := &corev1.SecretList{}
err := vClient.List(context.Background(), secretList, client.InNamespace(vPod.Namespace))
if err != nil {
return "", errors.Wrap(err, "list secrets in "+vPod.Namespace)
}
for _, secret := range secretList.Items {
if secret.Annotations["kubernetes.io/service-account.name"] == vServiceAccount {
return secret.Name, nil
}
}

return "", nil
}

func translateProjectedVolume(projectedVolume *corev1.ProjectedVolumeSource, vClient client.Client, vPod *corev1.Pod) error {
for i := range projectedVolume.Sources {
if projectedVolume.Sources[i].Secret != nil {
projectedVolume.Sources[i].Secret.Name = translate.PhysicalName(projectedVolume.Sources[i].Secret.Name, vPod.Namespace)
}
if projectedVolume.Sources[i].ConfigMap != nil {
projectedVolume.Sources[i].ConfigMap.Name = translate.PhysicalName(projectedVolume.Sources[i].ConfigMap.Name, vPod.Namespace)
}
if projectedVolume.Sources[i].DownwardAPI != nil {
for j := range projectedVolume.Sources[i].DownwardAPI.Items {
translateFieldRef(projectedVolume.Sources[i].DownwardAPI.Items[j].FieldRef)
}
}
if projectedVolume.Sources[i].ServiceAccountToken != nil {
secretName, err := secretNameFromServiceAccount(vClient, vPod)
if err != nil {
return err
} else if secretName == "" {
return fmt.Errorf("couldn't find service account secret for pod %s/%s", vPod.Namespace, vPod.Name)
}

allRights := int32(0644)
projectedVolume.Sources[i].Secret = &corev1.SecretProjection{
LocalObjectReference: corev1.LocalObjectReference{
Name: translate.PhysicalName(secretName, vPod.Namespace),
},
Items: []corev1.KeyToPath{
{
Path: projectedVolume.Sources[i].ServiceAccountToken.Path,
Key: "token",
Mode: &allRights,
},
},
}
projectedVolume.Sources[i].ServiceAccountToken = nil
}
}

return nil
}

func translateFieldRef(fieldSelector *corev1.ObjectFieldSelector) {
if fieldSelector == nil {
return
}
switch fieldSelector.FieldPath {
case "metadata.name":
fieldSelector.FieldPath = "metadata.annotations['" + NameAnnotation + "']"
case "metadata.namespace":
fieldSelector.FieldPath = "metadata.annotations['" + NamespaceAnnotation + "']"
case "metadata.uid":
fieldSelector.FieldPath = "metadata.annotations['" + UIDAnnotation + "']"
case "spec.serviceAccountName":
fieldSelector.FieldPath = "metadata.annotations['" + ServiceAccountNameAnnotation + "']"
}
}

func stripHostRewriteContainer(pPod *corev1.Pod) *corev1.Pod {
if pPod.Annotations == nil || pPod.Annotations[HostsRewrittenAnnotation] != "true" {
return pPod
Expand Down
23 changes: 22 additions & 1 deletion pkg/controllers/resources/pods/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package pods
import (
"github.com/loft-sh/vcluster/pkg/util/translate"
corev1 "k8s.io/api/core/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
)

func ConfigNamesFromPod(pod *corev1.Pod) []string {
Expand All @@ -20,11 +21,18 @@ func ConfigNamesFromPod(pod *corev1.Pod) []string {
if pod.Spec.Volumes[i].ConfigMap != nil {
configMaps = append(configMaps, pod.Namespace+"/"+pod.Spec.Volumes[i].ConfigMap.Name)
}
if pod.Spec.Volumes[i].Projected != nil {
for j := range pod.Spec.Volumes[i].Projected.Sources {
if pod.Spec.Volumes[i].Projected.Sources[j].ConfigMap != nil {
configMaps = append(configMaps, pod.Namespace+"/"+pod.Spec.Volumes[i].Projected.Sources[j].ConfigMap.Name)
}
}
}
}
return translate.UniqueSlice(configMaps)
}

func SecretNamesFromPod(pod *corev1.Pod) []string {
func SecretNamesFromPod(vClient client.Client, pod *corev1.Pod) []string {
secrets := []string{}
for _, c := range pod.Spec.Containers {
secrets = append(secrets, SecretNamesFromContainer(pod.Namespace, &c)...)
Expand All @@ -42,6 +50,19 @@ func SecretNamesFromPod(pod *corev1.Pod) []string {
if pod.Spec.Volumes[i].Secret != nil {
secrets = append(secrets, pod.Namespace+"/"+pod.Spec.Volumes[i].Secret.SecretName)
}
if pod.Spec.Volumes[i].Projected != nil {
for j := range pod.Spec.Volumes[i].Projected.Sources {
if pod.Spec.Volumes[i].Projected.Sources[j].Secret != nil {
secrets = append(secrets, pod.Namespace+"/"+pod.Spec.Volumes[i].Projected.Sources[j].Secret.Name)
}
if pod.Spec.Volumes[i].Projected.Sources[j].ServiceAccountToken != nil {
secretName, err := secretNameFromServiceAccount(vClient, pod)
if err == nil && secretName != "" {
secrets = append(secrets, pod.Namespace+"/"+secretName)
}
}
}
}
}
return translate.UniqueSlice(secrets)
}
Expand Down
Loading

0 comments on commit c8422e3

Please sign in to comment.