diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index c90887f2..77a8d252 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -16,11 +16,11 @@ jobs: name: lint runs-on: ubuntu-latest steps: + - uses: actions/checkout@v4 - uses: actions/setup-go@v4 with: - go-version: "1.20" + go-version-file: "go.mod" cache: false - - uses: actions/checkout@v3 - name: Run golangci-lint uses: golangci/golangci-lint-action@v3 with: diff --git a/Dockerfile b/Dockerfile index 1e88ea03..1f21362f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # Build the manager binary -FROM golang:1.20 as builder +FROM golang:1.22 as builder WORKDIR /vcluster-hpm-dev ARG TARGETOS diff --git a/cmd/hostpaths/hostpaths.go b/cmd/hostpaths/hostpaths.go index a81debd3..498a234d 100644 --- a/cmd/hostpaths/hostpaths.go +++ b/cmd/hostpaths/hostpaths.go @@ -9,6 +9,7 @@ import ( "strings" "time" + "github.com/loft-sh/vcluster/pkg/controllers/resources/namespaces" podtranslate "github.com/loft-sh/vcluster/pkg/controllers/resources/pods/translate" "github.com/loft-sh/vcluster/pkg/util/clienthelper" @@ -18,13 +19,12 @@ import ( "github.com/loft-sh/vcluster/pkg/util/translate" "github.com/pkg/errors" "github.com/spf13/cobra" - appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" kerrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/fields" + "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" @@ -148,7 +148,7 @@ func Start(ctx context.Context, options *context2.VirtualClusterOptions, init bo kubeClient, err := kubernetes.NewForConfig(virtualClusterConfig) if err != nil { - return false, errors.Wrap(err, "create kube client") + return false, fmt.Errorf("create kube client: %w", err) } _, err = kubeClient.Discovery().ServerVersion() @@ -168,13 +168,17 @@ func Start(ctx context.Context, options *context2.VirtualClusterOptions, init bo return err } - localManager, err := ctrl.NewManager(inClusterConfig, ctrl.Options{ - Scheme: scheme, - MetricsBindAddress: "0", - LeaderElection: false, - Namespace: options.TargetNamespace, - NewClient: pluginhookclient.NewPhysicalPluginClientFactory(blockingcacheclient.NewCacheClient), - }) + kubeClient, err := kubernetes.NewForConfig(inClusterConfig) + if err != nil { + return fmt.Errorf("create kube client: %w", err) + } + + err = findVclusterModeAndSetDefaultTranslation(ctx, kubeClient, options) + if err != nil { + return fmt.Errorf("find vcluster mode: %w", err) + } + + localManager, err := ctrl.NewManager(inClusterConfig, localManagerCtrlOptions(options)) if err != nil { return err } @@ -193,11 +197,6 @@ func Start(ctx context.Context, options *context2.VirtualClusterOptions, init bo startManagers(ctx, localManager, virtualClusterManager) - err = findVclusterModeAndSetDefaultTranslation(ctx, localManager, options) - if err != nil { - return err - } - if init { klog.Info("is init container mode") defer ctx.Done() @@ -216,49 +215,46 @@ func Start(ctx context.Context, options *context2.VirtualClusterOptions, init bo return nil } -func getVclusterObject(ctx context.Context, localManager manager.Manager, vclusterName, vclusterNamespace string, object client.Object) error { - err := localManager.GetClient().Get(ctx, types.NamespacedName{ - Name: vclusterName, - Namespace: vclusterNamespace, - }, object) - if err != nil { - return err - } - - return nil -} - -func getSyncerPodSpec(ctx context.Context, localManager manager.Manager, vclusterName, vclusterNamespace string) (*corev1.PodSpec, error) { +func getSyncerPodSpec(ctx context.Context, kubeClient kubernetes.Interface, vclusterName, vclusterNamespace string) (*corev1.PodSpec, error) { // try looking for the stateful set first - vclusterSts := &appsv1.StatefulSet{} - err := getVclusterObject(ctx, localManager, vclusterName, vclusterNamespace, vclusterSts) - if err != nil { + vclusterSts, err := kubeClient.AppsV1().StatefulSets(vclusterNamespace).Get(ctx, vclusterName, metav1.GetOptions{}) + if kerrors.IsNotFound(err) { + // try looking for deployment - in case of eks/k8s + vclusterDeploy, err := kubeClient.AppsV1().Deployments(vclusterNamespace).Get(ctx, vclusterName, metav1.GetOptions{}) if kerrors.IsNotFound(err) { - // try looking for deployment - in case of eks/k8s - vclusterDeploy := &appsv1.Deployment{} - err := getVclusterObject(ctx, localManager, vclusterName, vclusterNamespace, vclusterDeploy) - if err != nil { - if kerrors.IsNotFound(err) { - klog.Errorf("could not find vcluster either in statefulset or deployment: %v", err) - return nil, err - } - - klog.Errorf("error looking for vcluster deployment: %v", err) - return nil, err - } - - return &vclusterDeploy.Spec.Template.Spec, nil + klog.Errorf("could not find vcluster either in statefulset or deployment: %v", err) + return nil, err + } else if err != nil { + klog.Errorf("error looking for vcluster deployment: %v", err) + return nil, err } + return &vclusterDeploy.Spec.Template.Spec, nil + } else if err != nil { return nil, err } return &vclusterSts.Spec.Template.Spec, nil } -func findVclusterModeAndSetDefaultTranslation(ctx context.Context, localManager manager.Manager, options *context2.VirtualClusterOptions) error { - vclusterPodSpec, err := getSyncerPodSpec(ctx, localManager, options.Name, options.TargetNamespace) +func localManagerCtrlOptions(options *context2.VirtualClusterOptions) manager.Options { + controllerOptions := ctrl.Options{ + Scheme: scheme, + MetricsBindAddress: "0", + LeaderElection: false, + NewClient: pluginhookclient.NewPhysicalPluginClientFactory(blockingcacheclient.NewCacheClient), + } + + if !options.MultiNamespaceMode { + controllerOptions.Cache.Namespaces = []string{options.TargetNamespace} + } + + return controllerOptions +} + +func findVclusterModeAndSetDefaultTranslation(ctx context.Context, kubeClient kubernetes.Interface, options *context2.VirtualClusterOptions) error { + vclusterPodSpec, err := getSyncerPodSpec(ctx, kubeClient, options.Name, options.TargetNamespace) if err != nil { return err } @@ -268,6 +264,7 @@ func findVclusterModeAndSetDefaultTranslation(ctx context.Context, localManager // iterate over command args for _, arg := range container.Args { if strings.Contains(arg, MultiNamespaceMode) { + options.MultiNamespaceMode = true translate.Default = translate.NewMultiNamespaceTranslator(options.TargetNamespace) return nil } @@ -338,22 +335,12 @@ func mapHostPaths(ctx context.Context, pManager, vManager manager.Manager) { options := ctx.Value(optionsKey).(*context2.VirtualClusterOptions) wait.Forever(func() { - podList := &corev1.PodList{} - err := pManager.GetClient().List(ctx, podList, &client.ListOptions{ - Namespace: options.TargetNamespace, - FieldSelector: fields.SelectorFromSet(fields.Set{ - NodeIndexName: os.Getenv(HostpathMapperSelfNodeNameEnvVar), - }), - }) + podMappings, err := getPhysicalPodMap(ctx, options, pManager) if err != nil { - klog.Errorf("unable to list pods: %v", err) + klog.Errorf("unable to get physical pod mapping: %v", err) return } - podMappings := make(PhysicalPodMap) - - fillUpPodMapping(ctx, podList, podMappings) - vPodList := &corev1.PodList{} err = vManager.GetClient().List(ctx, vPodList, &client.ListOptions{ FieldSelector: fields.SelectorFromSet(fields.Set{ @@ -420,6 +407,75 @@ func mapHostPaths(ctx context.Context, pManager, vManager manager.Manager) { }, time.Second*5) } +func getPhysicalPodMap(ctx context.Context, options *context2.VirtualClusterOptions, pManager manager.Manager) (PhysicalPodMap, error) { + podListOptions := &client.ListOptions{ + FieldSelector: fields.SelectorFromSet(fields.Set{ + NodeIndexName: os.Getenv(HostpathMapperSelfNodeNameEnvVar), + }), + } + + if !options.MultiNamespaceMode { + podListOptions.Namespace = options.TargetNamespace + } + + podList := &corev1.PodList{} + err := pManager.GetClient().List(ctx, podList, podListOptions) + if err != nil { + return nil, fmt.Errorf("unable to list pods: %w", err) + } + + var pods []corev1.Pod + if options.MultiNamespaceMode { + // find namespaces managed by the current vcluster + nsList := &corev1.NamespaceList{} + err = pManager.GetClient().List(ctx, nsList, &client.ListOptions{ + LabelSelector: labels.SelectorFromSet(labels.Set{ + namespaces.VclusterNamespaceAnnotation: options.TargetNamespace, + }), + }) + if err != nil { + return nil, fmt.Errorf("unable to list namespaces: %w", err) + } + + vclusterNamespaces := make(map[string]struct{}, len(nsList.Items)) + for _, ns := range nsList.Items { + vclusterNamespaces[ns.Name] = struct{}{} + } + + // Limit Pods + pods = make([]corev1.Pod, 0, len(podList.Items)) + for _, pod := range podList.Items { + if _, ok := vclusterNamespaces[pod.Namespace]; ok { + pods = append(pods, pod) + } + } + } else { + pods = podList.Items + } + + podMappings := make(PhysicalPodMap, len(pods)) + for _, pPod := range pods { + lookupName := fmt.Sprintf("%s_%s_%s", pPod.Namespace, pPod.Name, pPod.UID) + + ok, err := checkIfPathExists(lookupName) + if err != nil { + klog.Errorf("error checking existence for path %s: %v", lookupName, err) + } + + if ok { + // check entry in podMapping + if _, ok := podMappings[pPod.Name]; !ok { + podMappings[pPod.Name] = &PodDetail{ + Target: lookupName, + PhysicalPod: pPod, + } + } + } + } + + return podMappings, nil +} + func cleanupOldContainerPaths(ctx context.Context, existingVPodsWithNS map[string]bool) error { options := ctx.Value(optionsKey).(*context2.VirtualClusterOptions) @@ -591,27 +647,6 @@ func getPhysicalLogFilename(ctx context.Context, physicalContainerFileName strin return fileName, nil } -func fillUpPodMapping(ctx context.Context, pPodList *corev1.PodList, podMappings PhysicalPodMap) { - for _, pPod := range pPodList.Items { - lookupName := fmt.Sprintf("%s_%s_%s", pPod.Namespace, pPod.Name, pPod.UID) - - ok, err := checkIfPathExists(lookupName) - if err != nil { - klog.Errorf("error checking existence for path %s: %v", lookupName, err) - } - - if ok { - // check entry in podMapping - if _, ok := podMappings[pPod.Name]; !ok { - podMappings[pPod.Name] = &PodDetail{ - Target: lookupName, - PhysicalPod: pPod, - } - } - } - } -} - // check if folder exists func checkIfPathExists(path string) (bool, error) { fullPath := filepath.Join(PodLogsMountPath, path) diff --git a/go.mod b/go.mod index aa29b6ae..ef99cb1e 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/loft-sh/vcluster-hostpath-mapper -go 1.20 +go 1.22.0 require ( github.com/go-openapi/loads v0.21.2 diff --git a/go.sum b/go.sum index 3f8c673e..0dcc0ba8 100644 --- a/go.sum +++ b/go.sum @@ -5,6 +5,7 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef h1:46PFijGLmAjMPwCCCo7Jf0W6f9slllCkkv7vyc1yOSg= github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -23,6 +24,7 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= +github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -46,12 +48,14 @@ github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbS github.com/fvbommel/sortorder v1.0.1 h1:dSnXLt4mJYH25uDDGa3biZNQsozaUWDSWeKJ0qqFfzE= github.com/fvbommel/sortorder v1.0.1/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/zapr v1.2.4 h1:QHVo+6stLbfJmYGkQ7uGHUCu5hnAFAj6mDe6Ea0SeOo= +github.com/go-logr/zapr v1.2.4/go.mod h1:FyHWQIzQORZ0QVE1BtVHv3cKtNLuXsbNLtpuhNapBOA= github.com/go-openapi/analysis v0.21.4 h1:ZDFLvSNxpDaomuCueM0BlSXxpANBlFYiBvr+GXrvIHc= github.com/go-openapi/analysis v0.21.4/go.mod h1:4zQ35W4neeZTqh3ol0rv/O8JBbka9QyAgQRPp9y3pfo= github.com/go-openapi/errors v0.20.2 h1:dxy7PGTqEh94zj2E3h1cUmQQWiM1+aeCROfAr02EmK8= @@ -75,6 +79,7 @@ github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/e github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -111,6 +116,7 @@ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -137,11 +143,13 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kubernetes-csi/external-snapshotter/client/v4 v4.2.0 h1:nHHjmvjitIiyPlUHk/ofpgvBcNcawJLtf4PYHORLjAA= +github.com/kubernetes-csi/external-snapshotter/client/v4 v4.2.0/go.mod h1:YBCo4DoEeDndqvAn6eeu0vWM7QdXmHEeI9cFWplmBys= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= github.com/loft-sh/vcluster v0.15.2 h1:ipTthjYi0a/X7bksMHCJ532C+RTr4LHT3NeJehw/j9w= @@ -179,7 +187,9 @@ github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo/v2 v2.9.7 h1:06xGQy5www2oN160RtEZoTvnP2sPhEfePYmCDc2szss= +github.com/onsi/ginkgo/v2 v2.9.7/go.mod h1:cxrmXWykAwTwhQsJOPfdIDiJ+l2RYq7U8hFU+M/1uw0= github.com/onsi/gomega v1.27.7 h1:fVih9JD6ogIiHUN6ePK7HJidyEDpWGVB5mzM7cWNXoU= +github.com/onsi/gomega v1.27.7/go.mod h1:1p8OOlwo2iUUDsHnOrjE5UKYJ+e3W8eQ3qSlRahPmr4= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -197,9 +207,11 @@ github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= @@ -234,8 +246,11 @@ go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0H go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= +go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= +go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -303,6 +318,7 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo= +golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -357,7 +373,9 @@ gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= +gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o= +gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= k8s.io/api v0.27.4 h1:0pCo/AN9hONazBKlNUdhQymmnfLRbSZjd5H5H3f0bSs= diff --git a/vendor/github.com/loft-sh/vcluster/pkg/controllers/resources/namespaces/syncer.go b/vendor/github.com/loft-sh/vcluster/pkg/controllers/resources/namespaces/syncer.go new file mode 100644 index 00000000..1412a4ad --- /dev/null +++ b/vendor/github.com/loft-sh/vcluster/pkg/controllers/resources/namespaces/syncer.go @@ -0,0 +1,125 @@ +package namespaces + +import ( + "fmt" + "strings" + + "github.com/loft-sh/vcluster/pkg/constants" + "github.com/loft-sh/vcluster/pkg/controllers/syncer" + synccontext "github.com/loft-sh/vcluster/pkg/controllers/syncer/context" + "github.com/loft-sh/vcluster/pkg/controllers/syncer/translator" + "github.com/loft-sh/vcluster/pkg/util/translate" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/validation" + "k8s.io/apimachinery/pkg/util/validation/field" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" +) + +// Unsafe annotations based on the docs here: +// https://kubernetes.io/docs/reference/labels-annotations-taints/ +var excludedAnnotations = []string{ + "scheduler.alpha.kubernetes.io/node-selector", + "scheduler.alpha.kubernetes.io/defaultTolerations", +} + +const ( + VclusterNameAnnotation = "vcluster.loft.sh/vcluster-name" + VclusterNamespaceAnnotation = "vcluster.loft.sh/vcluster-namespace" +) + +func New(ctx *synccontext.RegisterContext) (syncer.Object, error) { + namespaceLabels, err := parseNamespaceLabels(ctx.Options.NamespaceLabels) + if err != nil { + return nil, fmt.Errorf("invalid value of the namespace-labels flag: %v", err) + } + + namespaceLabels[VclusterNameAnnotation] = ctx.Options.Name + namespaceLabels[VclusterNamespaceAnnotation] = ctx.CurrentNamespace + + return &namespaceSyncer{ + Translator: translator.NewClusterTranslator(ctx, "namespace", &corev1.Namespace{}, NamespaceNameTranslator, excludedAnnotations...), + workloadServiceAccountName: ctx.Options.ServiceAccount, + namespaceLabels: namespaceLabels, + }, nil +} + +type namespaceSyncer struct { + translator.Translator + workloadServiceAccountName string + namespaceLabels map[string]string +} + +var _ syncer.IndicesRegisterer = &namespaceSyncer{} + +func (s *namespaceSyncer) RegisterIndices(ctx *synccontext.RegisterContext) error { + return ctx.VirtualManager.GetFieldIndexer().IndexField(ctx.Context, &corev1.Namespace{}, constants.IndexByPhysicalName, func(rawObj client.Object) []string { + return []string{NamespaceNameTranslator(rawObj.GetName(), rawObj)} + }) +} + +var _ syncer.Syncer = &namespaceSyncer{} + +func (s *namespaceSyncer) SyncDown(ctx *synccontext.SyncContext, vObj client.Object) (ctrl.Result, error) { + newNamespace := s.translate(ctx.Context, vObj.(*corev1.Namespace)) + ctx.Log.Infof("create physical namespace %s", newNamespace.Name) + err := ctx.PhysicalClient.Create(ctx.Context, newNamespace) + if err != nil { + ctx.Log.Infof("error syncing %s to physical cluster: %v", vObj.GetName(), err) + return ctrl.Result{}, err + } + + return ctrl.Result{}, s.EnsureWorkloadServiceAccount(ctx, newNamespace.Name) +} + +func (s *namespaceSyncer) Sync(ctx *synccontext.SyncContext, pObj client.Object, vObj client.Object) (ctrl.Result, error) { + updated := s.translateUpdate(ctx.Context, pObj.(*corev1.Namespace), vObj.(*corev1.Namespace)) + if updated != nil { + ctx.Log.Infof("updating physical namespace %s, because virtual namespace has changed", updated.Name) + translator.PrintChanges(pObj, updated, ctx.Log) + err := ctx.PhysicalClient.Update(ctx.Context, updated) + if err != nil { + return ctrl.Result{}, err + } + } + + return ctrl.Result{}, s.EnsureWorkloadServiceAccount(ctx, pObj.GetName()) +} + +func (s *namespaceSyncer) EnsureWorkloadServiceAccount(ctx *synccontext.SyncContext, pNamespace string) error { + if s.workloadServiceAccountName == "" { + return nil + } + + svc := &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: pNamespace, + Name: s.workloadServiceAccountName, + }, + } + _, err := controllerutil.CreateOrPatch(ctx.Context, ctx.PhysicalClient, svc, func() error { return nil }) + return err +} + +func NamespaceNameTranslator(vName string, _ client.Object) string { + return translate.Default.PhysicalNamespace(vName) +} + +func parseNamespaceLabels(labels []string) (map[string]string, error) { + out := map[string]string{} + for _, v := range labels { + parts := strings.SplitN(v, "=", 2) + if len(parts) != 2 { + return nil, fmt.Errorf("incorrect format, expected: key=value got: %s", v) + } + out[parts[0]] = parts[1] + } + errs := validation.ValidateLabels(out, field.NewPath("namespace-labels")) + if len(errs) != 0 { + return nil, fmt.Errorf("invalid labels: %v", errs) + } + + return out, nil +} diff --git a/vendor/github.com/loft-sh/vcluster/pkg/controllers/resources/namespaces/translate.go b/vendor/github.com/loft-sh/vcluster/pkg/controllers/resources/namespaces/translate.go new file mode 100644 index 00000000..4ab8c56c --- /dev/null +++ b/vendor/github.com/loft-sh/vcluster/pkg/controllers/resources/namespaces/translate.go @@ -0,0 +1,41 @@ +package namespaces + +import ( + "context" + + "github.com/loft-sh/vcluster/pkg/controllers/syncer/translator" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/equality" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +func (s *namespaceSyncer) translate(ctx context.Context, vObj client.Object) *corev1.Namespace { + newNamespace := s.TranslateMetadata(ctx, vObj).(*corev1.Namespace) + + // add user defined namespace labels + for k, v := range s.namespaceLabels { + newNamespace.Labels[k] = v + } + + return newNamespace +} + +func (s *namespaceSyncer) translateUpdate(ctx context.Context, pObj, vObj *corev1.Namespace) *corev1.Namespace { + var updated *corev1.Namespace + + _, updatedAnnotations, updatedLabels := s.TranslateMetadataUpdate(ctx, vObj, pObj) + // add user defined namespace labels + for k, v := range s.namespaceLabels { + updatedLabels[k] = v + } + // set the kubernetes.io/metadata.name label + updatedLabels[corev1.LabelMetadataName] = pObj.Name + // check if any labels or annotations changed + if !equality.Semantic.DeepEqual(updatedAnnotations, pObj.GetAnnotations()) || !equality.Semantic.DeepEqual(updatedLabels, pObj.GetLabels()) { + updated = translator.NewIfNil(updated, pObj) + updated.Annotations = updatedAnnotations + updated.Labels = updatedLabels + } + + return updated +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 6f6047a1..c519d007 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -156,6 +156,7 @@ github.com/liggitt/tabwriter github.com/loft-sh/vcluster/cmd/vcluster/context github.com/loft-sh/vcluster/pkg/constants github.com/loft-sh/vcluster/pkg/controllers/resources/configmaps +github.com/loft-sh/vcluster/pkg/controllers/resources/namespaces github.com/loft-sh/vcluster/pkg/controllers/resources/pods/translate github.com/loft-sh/vcluster/pkg/controllers/resources/priorityclasses github.com/loft-sh/vcluster/pkg/controllers/syncer