Skip to content

Commit

Permalink
Make it possible to create vcluster with digits only name
Browse files Browse the repository at this point in the history
  • Loading branch information
johannesfrey committed Sep 26, 2024
1 parent 83cace1 commit 5759162
Show file tree
Hide file tree
Showing 15 changed files with 115 additions and 34 deletions.
8 changes: 8 additions & 0 deletions chart/templates/_helper.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,11 @@
{{ .repository }}:{{ .tag }}
{{- end -}}
{{- end -}}

{{- define "vcluster.name" -}}
{{- if regexMatch "^[0-9]+$" .Release.Name -}}
{{- printf "vc-%s" .Release.Name -}}
{{- else -}}
{{- printf "%s" .Release.Name }}
{{- end -}}
{{- end -}}
2 changes: 1 addition & 1 deletion chart/templates/headless-service.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}-headless
name: {{ template "vcluster.name" . }}-headless
namespace: {{ .Release.Namespace }}
labels:
app: vcluster
Expand Down
4 changes: 2 additions & 2 deletions chart/templates/service.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}
name: {{ template "vcluster.name" . }}
namespace: {{ .Release.Namespace }}
labels:
app: vcluster
Expand Down Expand Up @@ -41,6 +41,6 @@ spec:
{{- if and (not .Values.controlPlane.service.spec.selector) (not .Values.experimental.isolatedControlPlane.headless) }}
selector:
app: vcluster
release: {{ .Release.Name }}
release: {{ .Release.Name | quote }}
{{- end }}
{{- end }}
10 changes: 5 additions & 5 deletions chart/templates/statefulset.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
apiVersion: apps/v1
kind: {{ include "vcluster.kind" . }}
metadata:
name: {{ .Release.Name }}
name: {{ .Release.Name | quote }}
namespace: {{ .Release.Namespace }}
labels:
app: vcluster
Expand All @@ -21,13 +21,13 @@ spec:
selector:
matchLabels:
app: vcluster
release: {{ .Release.Name }}
release: {{ .Release.Name | quote }}
{{- if eq (include "vcluster.kind" .) "StatefulSet" }}
{{- if ge (int .Capabilities.KubeVersion.Minor) 27 }}
persistentVolumeClaimRetentionPolicy:
whenDeleted: {{ .Values.controlPlane.statefulSet.persistence.volumeClaim.retentionPolicy }}
{{- end }}
serviceName: {{ .Release.Name }}-headless
serviceName: {{ template "vcluster.name" . }}-headless
podManagementPolicy: {{ .Values.controlPlane.statefulSet.scheduling.podManagementPolicy }}
{{ include "vcluster.persistence" . | indent 2 }}
{{- else }}
Expand All @@ -51,7 +51,7 @@ spec:
{{- end }}
labels:
app: vcluster
release: {{ .Release.Name }}
release: {{ .Release.Name | quote }}
{{- if .Values.controlPlane.statefulSet.pods.labels }}
{{ toYaml .Values.controlPlane.statefulSet.pods.labels | indent 8 }}
{{- end }}
Expand Down Expand Up @@ -87,7 +87,7 @@ spec:
dnsPolicy: {{ .Values.controlPlane.statefulSet.dnsPolicy }}
{{- end }}
{{- if .Values.controlPlane.statefulSet.dnsConfig }}
dnsConfig:
dnsConfig:
{{ toYaml .Values.controlPlane.statefulSet.dnsConfig | indent 8 }}
{{- end }}
{{- if .Values.controlPlane.advanced.serviceAccount.name }}
Expand Down
10 changes: 10 additions & 0 deletions chart/tests/headless-service_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,16 @@ tests:
path: metadata.namespace
value: my-namespace

- it: should prepend vc to service name when only digits are given
release:
name: 1234
asserts:
- hasDocuments:
count: 1
- equal:
path: metadata.name
value: vc-1234-headless

- it: embedded-etcd
set:
controlPlane:
Expand Down
10 changes: 10 additions & 0 deletions chart/tests/service_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,16 @@ tests:
path: spec.ports
count: 2

- it: should prepend vc to service name when only digits are given
release:
name: 1234
asserts:
- hasDocuments:
count: 1
- equal:
path: metadata.name
value: vc-1234

- it: isolated control plane
release:
name: my-release
Expand Down
7 changes: 4 additions & 3 deletions devspace.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ vars:
COMMON_VALUES: ./test/commonValues.yaml
PRO_VALUES: ./test/proValues.yaml
VALUES_FILE: ./test/e2e/values.yaml
RELEASE_NAME: "vcluster"

# Images DevSpace will build for vcluster
images:
Expand All @@ -23,7 +24,7 @@ images:
deployments:
vcluster-k8s:
helm:
releaseName: vcluster
releaseName: ${RELEASE_NAME}
chart:
name: ./chart
values:
Expand Down Expand Up @@ -51,7 +52,7 @@ deployments:

vcluster-k3s:
helm:
releaseName: vcluster
releaseName: ${RELEASE_NAME}
chart:
name: ./chart
values:
Expand Down Expand Up @@ -79,7 +80,7 @@ deployments:

vcluster-k0s:
helm:
releaseName: vcluster
releaseName: ${RELEASE_NAME}
chart:
name: ./chart
values:
Expand Down
4 changes: 1 addition & 3 deletions pkg/certs/ensure.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,7 @@ func EnsureCerts(

ownerRef := []metav1.OwnerReference{}
if options.Experimental.SyncSettings.SetOwner {
// options.ServiceName gets rewritten to the workload service name so we use options.Name as the helm chart
// directly uses the release name for the service name
controlPlaneService, err := currentNamespaceClient.CoreV1().Services(currentNamespace).Get(ctx, options.Name, metav1.GetOptions{})
controlPlaneService, err := currentNamespaceClient.CoreV1().Services(currentNamespace).Get(ctx, options.ControlPlaneService, metav1.GetOptions{})
if err != nil {
return fmt.Errorf("get vcluster service: %w", err)
}
Expand Down
26 changes: 13 additions & 13 deletions pkg/cli/connect_helm.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ func (cmd *connectHelm) connect(ctx context.Context, vCluster *find.VCluster, co
}

// retrieve vcluster kube config
kubeConfig, err := cmd.getVClusterKubeConfig(ctx, vCluster.Name, command)
kubeConfig, err := cmd.getVClusterKubeConfig(ctx, vCluster, command)
if err != nil {
return err
}
Expand Down Expand Up @@ -272,15 +272,15 @@ func (cmd *connectHelm) prepare(ctx context.Context, vCluster *find.VCluster) er
return nil
}

func (cmd *connectHelm) getVClusterKubeConfig(ctx context.Context, vclusterName string, command []string) (*clientcmdapi.Config, error) {
func (cmd *connectHelm) getVClusterKubeConfig(ctx context.Context, vcluster *find.VCluster, command []string) (*clientcmdapi.Config, error) {
var err error
podName := cmd.PodName
if podName == "" {
waitErr := wait.PollUntilContextTimeout(ctx, time.Second, time.Second*30, true, func(ctx context.Context) (bool, error) {
// get vcluster pod name
var pods *corev1.PodList
pods, err = cmd.kubeClient.CoreV1().Pods(cmd.Namespace).List(ctx, metav1.ListOptions{
LabelSelector: "app=vcluster,release=" + vclusterName,
LabelSelector: "app=vcluster,release=" + vcluster.Name,
})
if err != nil {
return false, err
Expand Down Expand Up @@ -310,7 +310,7 @@ func (cmd *connectHelm) getVClusterKubeConfig(ctx context.Context, vclusterName
cmd.Log.Debugf("Successfully found vCluster pod for connecting %s", podName)

// get the kube config from the Secret
kubeConfig, err := clihelper.GetKubeConfig(ctx, cmd.kubeClient, vclusterName, cmd.Namespace, cmd.Log)
kubeConfig, err := clihelper.GetKubeConfig(ctx, cmd.kubeClient, vcluster.Name, cmd.Namespace, cmd.Log)
if err != nil {
return nil, fmt.Errorf("failed to parse kube config: %w", err)
}
Expand All @@ -322,14 +322,14 @@ func (cmd *connectHelm) getVClusterKubeConfig(ctx context.Context, vclusterName
}

// exchange context name in virtual kube config
err = cmd.exchangeContextName(kubeConfig, vclusterName)
err = cmd.exchangeContextName(kubeConfig, vcluster.Name)
if err != nil {
return nil, err
}

// check if the vcluster is exposed and set server
if vclusterName != "" && cmd.Server == "" && len(command) == 0 {
err = cmd.setServerIfExposed(ctx, vclusterName, kubeConfig)
if vcluster.Name != "" && cmd.Server == "" && len(command) == 0 {
err = cmd.setServerIfExposed(ctx, vcluster, kubeConfig)
if err != nil {
return nil, err
}
Expand All @@ -338,7 +338,7 @@ func (cmd *connectHelm) getVClusterKubeConfig(ctx context.Context, vclusterName
if cmd.Server == "" && cmd.BackgroundProxy {
if localkubernetes.IsDockerInstalledAndUpAndRunning() {
// start background container
server, err := localkubernetes.CreateBackgroundProxyContainer(ctx, vclusterName, cmd.Namespace, cmd.kubeClientConfig, kubeConfig, cmd.LocalPort, cmd.Log)
server, err := localkubernetes.CreateBackgroundProxyContainer(ctx, vcluster.Name, cmd.Namespace, cmd.kubeClientConfig, kubeConfig, cmd.LocalPort, cmd.Log)
if err != nil {
cmd.Log.Warnf("Error exposing local vcluster, will fallback to port-forwarding: %v", err)
cmd.BackgroundProxy = false
Expand Down Expand Up @@ -419,12 +419,12 @@ func (cmd *connectHelm) getVClusterKubeConfig(ctx context.Context, vclusterName
return kubeConfig, nil
}

func (cmd *connectHelm) setServerIfExposed(ctx context.Context, vClusterName string, vClusterConfig *clientcmdapi.Config) error {
func (cmd *connectHelm) setServerIfExposed(ctx context.Context, vcluster *find.VCluster, vClusterConfig *clientcmdapi.Config) error {
printedWaiting := false
err := wait.PollUntilContextTimeout(ctx, time.Second*2, time.Minute*5, true, func(ctx context.Context) (done bool, err error) {
// first check for load balancer service, look for the other service if it's not there
loadBalancerMissing := false
service, err := cmd.kubeClient.CoreV1().Services(cmd.Namespace).Get(ctx, vClusterName, metav1.GetOptions{})
service, err := cmd.kubeClient.CoreV1().Services(cmd.Namespace).Get(ctx, vcluster.ServiceName, metav1.GetOptions{})
if err != nil {
if kerrors.IsNotFound(err) {
loadBalancerMissing = true
Expand All @@ -433,7 +433,7 @@ func (cmd *connectHelm) setServerIfExposed(ctx context.Context, vClusterName str
}
}
if loadBalancerMissing {
service, err = cmd.kubeClient.CoreV1().Services(cmd.Namespace).Get(ctx, vClusterName, metav1.GetOptions{})
service, err = cmd.kubeClient.CoreV1().Services(cmd.Namespace).Get(ctx, vcluster.ServiceName, metav1.GetOptions{})
if kerrors.IsNotFound(err) {
return true, nil
} else if err != nil {
Expand All @@ -446,7 +446,7 @@ func (cmd *connectHelm) setServerIfExposed(ctx context.Context, vClusterName str

// not a load balancer? Then don't wait
if service.Spec.Type == corev1.ServiceTypeNodePort {
server, err := localkubernetes.ExposeLocal(ctx, vClusterName, cmd.Namespace, &cmd.rawConfig, vClusterConfig, service, cmd.LocalPort, cmd.Log)
server, err := localkubernetes.ExposeLocal(ctx, vcluster.Name, cmd.Namespace, &cmd.rawConfig, vClusterConfig, service, cmd.LocalPort, cmd.Log)
if err != nil {
cmd.Log.Warnf("Error exposing local vcluster, will fallback to port-forwarding: %v", err)
}
Expand Down Expand Up @@ -476,7 +476,7 @@ func (cmd *connectHelm) setServerIfExposed(ctx context.Context, vClusterName str
return false, nil
}

cmd.Log.Infof("Using vcluster %s load balancer endpoint: %s", vClusterName, cmd.Server)
cmd.Log.Infof("Using vcluster %s load balancer endpoint: %s", vcluster.Name, cmd.Server)
return true, nil
})
if err != nil {
Expand Down
35 changes: 35 additions & 0 deletions pkg/cli/find/find.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,14 @@ import (

const VirtualClusterSelector = "app=vcluster"

var digitsOnlyRegex = regexp.MustCompile("^[0-9]+$")

type VCluster struct {
ClientFactory clientcmd.ClientConfig `json:"-"`
Created metav1.Time
Name string
Namespace string
ServiceName string
Annotations map[string]string
Labels map[string]string
Status Status
Expand Down Expand Up @@ -399,6 +402,11 @@ func getVCluster(ctx context.Context, object client.Object, context, release str
status = string(StatusUnknown)
}

service, err := getService(ctx, client, kubeClientConfig, namespace, release)
if err != nil {
return VCluster{}, err
}

switch vclusterObject := object.(type) {
case *appsv1.StatefulSet:
for _, container := range vclusterObject.Spec.Template.Spec.Containers {
Expand All @@ -425,6 +433,7 @@ func getVCluster(ctx context.Context, object client.Object, context, release str
return VCluster{
Name: release,
Namespace: namespace,
ServiceName: service.Name,
Annotations: object.GetAnnotations(),
Labels: object.GetLabels(),
Status: Status(status),
Expand All @@ -435,6 +444,32 @@ func getVCluster(ctx context.Context, object client.Object, context, release str
}, nil
}

func getService(ctx context.Context, client *kubernetes.Clientset, kubeClientConfig clientcmd.ClientConfig, namespace, name string) (*corev1.Service, error) {
ctx, cancel := context.WithTimeout(ctx, time.Second*30)
defer cancel()

var svcName string
if digitsOnlyRegex.MatchString(name) {
svcName = fmt.Sprintf("vc-%s", name)
} else {
svcName = name
}

service, err := client.CoreV1().Services(namespace).Get(ctx, svcName, metav1.GetOptions{})
if err != nil {
if kerrors.IsForbidden(err) {
// try the current namespace instead
if namespace, err = getAccessibleNS(kubeClientConfig); err != nil {
return nil, err
}
return client.CoreV1().Services(namespace).Get(ctx, svcName, metav1.GetOptions{})
}
return nil, err
}

return service, nil
}

func getPods(ctx context.Context, client *kubernetes.Clientset, kubeClientConfig clientcmd.ClientConfig, namespace, podSelector string) (*corev1.PodList, error) {
ctx, cancel := context.WithTimeout(ctx, time.Second*30)
defer cancel()
Expand Down
13 changes: 12 additions & 1 deletion pkg/config/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@ package config
import (
"fmt"
"os"
"regexp"

"github.com/loft-sh/vcluster/config"
"github.com/loft-sh/vcluster/pkg/strvals"
"github.com/pkg/errors"
"sigs.k8s.io/yaml"
)

var digitsOnlyRegex = regexp.MustCompile("^[0-9]+$")

func ParseConfig(path, name string, setValues []string) (*VirtualClusterConfig, error) {
// check if name is empty
if name == "" {
Expand Down Expand Up @@ -37,10 +40,18 @@ func ParseConfig(path, name string, setValues []string) (*VirtualClusterConfig,
}

// build config

var svcName string
if digitsOnlyRegex.MatchString(name) {
svcName = fmt.Sprintf("vc-%s", name)
} else {
svcName = name
}

retConfig := &VirtualClusterConfig{
Config: *rawConfig,
Name: name,
ControlPlaneService: name,
ControlPlaneService: svcName,
}

// validate config
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ func (n *nodeServiceProvider) Unlock() {
}

func (n *nodeServiceProvider) GetNodeIP(ctx context.Context, name string) (string, error) {
serviceName := translate.SafeConcatName(translate.VClusterName, "node", strings.ReplaceAll(name, ".", "-"))
serviceName := translate.SafeConcatName(translate.VClusterServiceName, "node", strings.ReplaceAll(name, ".", "-"))

service := &corev1.Service{}
err := n.currentNamespaceClient.Get(ctx, types.NamespacedName{Name: serviceName, Namespace: n.currentNamespace}, service)
Expand Down
1 change: 1 addition & 0 deletions pkg/setup/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ func InitAndValidateConfig(ctx context.Context, vConfig *config.VirtualClusterCo

// set global vCluster name
translate.VClusterName = vConfig.Name
translate.VClusterServiceName = vConfig.WorkloadService

// set workload namespace
err = os.Setenv("NAMESPACE", vConfig.WorkloadNamespace)
Expand Down
3 changes: 2 additions & 1 deletion pkg/util/translate/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ var (
NamespaceLabelPrefix = "vcluster.loft.sh/ns-label"

// VClusterName is the vcluster name, usually set at start time
VClusterName = "suffix"
VClusterName = "suffix"
VClusterServiceName = "vcluster"

ManagedAnnotationsAnnotation = "vcluster.loft.sh/managed-annotations"
ManagedLabelsAnnotation = "vcluster.loft.sh/managed-labels"
Expand Down
Loading

0 comments on commit 5759162

Please sign in to comment.