Skip to content

Commit

Permalink
Use API Server DNS Names by default when logging into VM Web Console
Browse files Browse the repository at this point in the history
In certain environments, a load balancer, and therefore a virtual IP, may not be present. In these cases, rather than relying on the virtual IP to log into the VM web console, we need to instead rely on an FQDN / DNS name to login.
This change plumbs the API Server DNS Names from the app platform CRD, and uses that by default to login to the VM Web Console. If no DNS Name is found, then we fall back to the previous method of using the virtual IP to login.

Testing Done:

Used an existing testbed - note that this setup has a load balancer already

Setup steps:

Deployed a VM named my-vm on test-namespace
On control plane VM, changed /usr/lib/vmware-wcp/objects/PodVM-GuestCluster/30-vmop/vmop.yaml file to add rbac permissions for appplatform (since vmop tar won't load those changes) and re-applied yaml
Loaded vmop tar image using make docker-build, docker save docker.io/library/vmoperator-controller:latest > vmopfqdn.tar, and deploy-wcp.sh
Next, verified that web console returns API Server DNS name for login:

root@localhost [ ~ ]# kubectl vsphere vm web-console my-vm -n test-namespace
Successfully created a new WebConsoleRequest 'my-vm-b498r' in namespace 'test-namespace'
Waiting for the above WebConsoleRequest to be processed...

Web-Console URL: https://domain-1.test/vm/web-console?host=10.167.71.251&namespace=test-namespace&port=443&ticket=36fcf5b74000d104f4b3a5038381326f&uuid=77c1da1f-dbea-4bc2-af55-78303afc21d4
This URL is for one-time use and will expire at 2024-10-17T22:12:56Z (in about 2m0s)
  • Loading branch information
ammujumdar-bcom committed Nov 8, 2024
1 parent d107a82 commit 558b911
Show file tree
Hide file tree
Showing 23 changed files with 896 additions and 51 deletions.
4 changes: 4 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ linters-settings:
pkg: github.com/vmware-tanzu/vm-operator/external/byok/api/v1alpha1
- alias: capv1
pkg: github.com/vmware-tanzu/vm-operator/external/capabilities/api/v1alpha1
- alias: appv1a1
pkg: github.com/vmware-tanzu/vm-operator/external/appplatform/api/v1alpha1
- alias: proxyaddr
pkg: github.com/vmware-tanzu/vm-operator/pkg/util/kube/proxyaddr

- alias: pkgcfg
pkg: github.com/vmware-tanzu/vm-operator/pkg/config
Expand Down
8 changes: 8 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,9 @@ generate-go: ## Generate golang sources
$(CONTROLLER_GEN) \
paths=github.com/vmware-tanzu/vm-operator/external/capabilities/... \
object:headerFile=./hack/boilerplate/boilerplate.generatego.txt
$(CONTROLLER_GEN) \
paths=github.com/vmware-tanzu/vm-operator/external/appplatform/... \
object:headerFile=./hack/boilerplate/boilerplate.generatego.txt
$(MAKE) -C ./pkg/util/cloudinit/schema $@

.PHONY: generate-manifests
Expand Down Expand Up @@ -406,6 +409,11 @@ generate-external-manifests: ## Generate manifests for the external types for te
crd:crdVersions=v1 \
output:crd:dir=$(EXTERNAL_CRD_ROOT) \
output:none
$(CONTROLLER_GEN) \
paths=github.com/vmware-tanzu/vm-operator/external/appplatform/... \
crd:crdVersions=v1 \
output:crd:dir=$(EXTERNAL_CRD_ROOT) \
output:none

.PHONY: generate-go-conversions
generate-go-conversions: ## Generate conversions go code
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.16.1
name: supervisorproperties.appplatform.vmware.com
spec:
group: appplatform.vmware.com
names:
kind: SupervisorProperties
listKind: SupervisorPropertiesList
plural: supervisorproperties
singular: supervisorproperty
scope: Namespaced
versions:
- name: v1alpha1
schema:
openAPIV3Schema:
description: SupervisorProperties is the Schema for the SupervisorProperties
API
properties:
apiVersion:
description: |-
APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
type: string
kind:
description: |-
Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
type: string
metadata:
type: object
spec:
description: SupervisorPropertiesSpec defines the values of the properties
requested by a Supervisor Service Package.
properties:
apiServerDNSNames:
description: APIServerDNSNames indicates the API server DNS Names
associated with the supervisor.
items:
type: string
type: array
capabilities:
description: Capabilities defines the capabilities the Supervisor
has. The common case of the capability is the feature supported
of the vCenter.
items:
description: Capability defines the feature supported by the Supervisor.
properties:
name:
description: The name of the capability.
type: string
value:
default: false
description: The value indicates if the capability is supported.
type: boolean
required:
- name
- value
type: object
type: array
cloudVC:
description: CloudVCenter indicates if the vCenter is deployed on
cloud.
type: boolean
controlPlaneCount:
description: ControlPlaneCount indicates the number of control planes
enabled on the Supervisor.
type: integer
cpVMSize:
description: ControlPlaneVMSize indicates the capacity of the Supervisor
Control Plane. It's derived from Supervisor's tshirt size.
enum:
- TINY
- SMALL
- MEDIUM
- LARGE
type: string
namespacesCLIPluginVersion:
description: NamespacesCLIPluginVersion indicates the Supervisor recommended
namespaces CLIPlugin CR version.
type: string
networkProvider:
description: NetworkProvider indicates the Network Provider used on
Supervisor. (e.g. NSX, nsx-vpc, or vsphere-network)
type: string
podVMSupported:
description: PodVMSupported indicates if the Supervisor supports PodVMs.
type: boolean
ssoDomain:
description: SSODomain indicates the name of the default SSO domain
configured in vCenter.
type: string
stretchedSupervisor:
description: StretchedSupervisor indicates if the Supervisor is enabled
on a set of vSphere Zones.
type: boolean
tmcNamespace:
description: TMCNamespace indicates the namespace used for TMC to
be deployed.
type: string
vcPNID:
description: VCenterPNID indicates the Primary Network Identifier
of vCenter.
type: string
vcPort:
description: VCenterPort indicates the port of vCenter.
type: string
vcPublicKeys:
description: VCenterPublicKeys indicates the base64 encoded vCenter
OIDC issuer, client audience and the public keys in JWKS format.
type: string
virtualIP:
description: VirtualIP indicates the IP address of the Kubernetes
LoadBalancer type service fronting the apiservers.
type: string
type: object
type: object
served: true
storage: true
8 changes: 8 additions & 0 deletions config/rbac/supervisor_properties_role.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: supervisor-properties-reader
rules:
- apiGroups: ["appplatform.vmware.com"]
resources: ["supervisorproperties"]
verbs: ["get", "list"]
11 changes: 11 additions & 0 deletions config/rbac/supervisor_properties_role_binding.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: supervisor-properties-reader-binding
roleRef:
name: supervisor-properties-reader
kind: ClusterRole
subjects:
- kind: ServiceAccount
name: default
namespace: vmware-system-vmop
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2022-2023 VMware, Inc. All Rights Reserved.
// Copyright (c) 2022-2024 VMware, Inc. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package v1alpha1
Expand All @@ -12,7 +12,6 @@ import (
"time"

"github.com/go-logr/logr"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
Expand All @@ -26,14 +25,12 @@ import (
pkgctx "github.com/vmware-tanzu/vm-operator/pkg/context"
"github.com/vmware-tanzu/vm-operator/pkg/providers"
"github.com/vmware-tanzu/vm-operator/pkg/record"
proxyaddr "github.com/vmware-tanzu/vm-operator/pkg/util/kube/proxyaddr"
)

const (
DefaultExpiryTime = time.Second * 120
UUIDLabelKey = "vmoperator.vmware.com/webconsolerequest-uuid"

ProxyAddrServiceName = "kube-apiserver-lb-svc"
ProxyAddrServiceNamespace = "kube-system"
)

// AddToManager adds this package's controller to the provided manager.
Expand Down Expand Up @@ -187,18 +184,11 @@ func (r *Reconciler) ReconcileNormal(ctx *pkgctx.WebConsoleRequestContext) error
ctx.WebConsoleRequest.Status.Response = ticket
ctx.WebConsoleRequest.Status.ExpiryTime = metav1.NewTime(metav1.Now().Add(DefaultExpiryTime))

// Retrieve the proxy address from the load balancer service ingress IP.
proxySvc := &corev1.Service{}
proxySvcObjectKey := client.ObjectKey{Name: ProxyAddrServiceName, Namespace: ProxyAddrServiceNamespace}
err = r.Get(ctx, proxySvcObjectKey, proxySvc)
proxyAddr, err := proxyaddr.ProxyAddress(ctx, r)
if err != nil {
return fmt.Errorf("failed to get proxy address service %s: %w", proxySvcObjectKey, err)
}
if len(proxySvc.Status.LoadBalancer.Ingress) == 0 {
return fmt.Errorf("no ingress found for proxy address service %s", proxySvcObjectKey)
return err
}

ctx.WebConsoleRequest.Status.ProxyAddr = proxySvc.Status.LoadBalancer.Ingress[0].IP
ctx.WebConsoleRequest.Status.ProxyAddr = proxyAddr

// Add UUID as a Label to the current WebConsoleRequest resource after acquiring the ticket.
// This will be used when validating the connection request from users to the web console URL.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2022 VMware, Inc. All Rights Reserved.
// Copyright (c) 2022-2024 VMware, Inc. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package v1alpha1_test
Expand All @@ -19,6 +19,7 @@ import (
vmopv1 "github.com/vmware-tanzu/vm-operator/api/v1alpha3"
webconsolerequest "github.com/vmware-tanzu/vm-operator/controllers/virtualmachinewebconsolerequest/v1alpha1"
"github.com/vmware-tanzu/vm-operator/pkg/constants/testlabels"
proxyaddr "github.com/vmware-tanzu/vm-operator/pkg/util/kube/proxyaddr"
"github.com/vmware-tanzu/vm-operator/test/builder"
)

Expand Down Expand Up @@ -82,8 +83,8 @@ func intgTestsReconcile() {

proxySvc = &corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: webconsolerequest.ProxyAddrServiceName,
Namespace: webconsolerequest.ProxyAddrServiceNamespace,
Name: proxyaddr.ProxyAddrServiceName,
Namespace: proxyaddr.ProxyAddrServiceNamespace,
},
Spec: corev1.ServiceSpec{
Type: corev1.ServiceTypeLoadBalancer,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,12 @@ import (
vmopv1a1 "github.com/vmware-tanzu/vm-operator/api/v1alpha1"
vmopv1 "github.com/vmware-tanzu/vm-operator/api/v1alpha3"
webconsolerequest "github.com/vmware-tanzu/vm-operator/controllers/virtualmachinewebconsolerequest/v1alpha1"
appv1a1 "github.com/vmware-tanzu/vm-operator/external/appplatform/api/v1alpha1"
pkgcfg "github.com/vmware-tanzu/vm-operator/pkg/config"
"github.com/vmware-tanzu/vm-operator/pkg/constants/testlabels"
pkgctx "github.com/vmware-tanzu/vm-operator/pkg/context"
providerfake "github.com/vmware-tanzu/vm-operator/pkg/providers/fake"
proxyaddr "github.com/vmware-tanzu/vm-operator/pkg/util/kube/proxyaddr"
"github.com/vmware-tanzu/vm-operator/test/builder"
)

Expand All @@ -42,11 +45,12 @@ func unitTestsReconcile() {
ctx *builder.UnitTestContextForController
fakeVMProvider *providerfake.VMProvider

reconciler *webconsolerequest.Reconciler
wcrCtx *pkgctx.WebConsoleRequestContext
wcr *vmopv1a1.WebConsoleRequest
vm *vmopv1a1.VirtualMachine
proxySvc *corev1.Service
reconciler *webconsolerequest.Reconciler
wcrCtx *pkgctx.WebConsoleRequestContext
wcr *vmopv1a1.WebConsoleRequest
vm *vmopv1a1.VirtualMachine
proxySvc *corev1.Service
proxySvcDNS *appv1a1.SupervisorProperties
)

BeforeEach(func() {
Expand All @@ -68,8 +72,8 @@ func unitTestsReconcile() {

proxySvc = &corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: webconsolerequest.ProxyAddrServiceName,
Namespace: webconsolerequest.ProxyAddrServiceNamespace,
Name: proxyaddr.ProxyAddrServiceName,
Namespace: proxyaddr.ProxyAddrServiceNamespace,
},
Status: corev1.ServiceStatus{
LoadBalancer: corev1.LoadBalancerStatus{
Expand Down Expand Up @@ -141,5 +145,36 @@ func unitTestsReconcile() {
// Checking the label key only because UID will not be set to a resource during unit test.
Expect(wcrCtx.WebConsoleRequest.Labels).To(HaveKey(webconsolerequest.UUIDLabelKey))
})

When("SimplifiedEnablement is enabled", func() {
JustBeforeEach(func() {
pkgcfg.UpdateContext(ctx,
func(config *pkgcfg.Config) {
config.Features.SimplifiedEnablement = true
},
)
})

DescribeTable("DNS Names",
func(apiServerDNSName []string, expectedProxy string) {
proxySvcDNS = &appv1a1.SupervisorProperties{
ObjectMeta: metav1.ObjectMeta{
Name: "supervisor-env-props",
Namespace: "vmware-system-supervisor-services",
},
Spec: appv1a1.SupervisorPropertiesSpec{
APIServerDNSNames: apiServerDNSName,
},
}
Expect(ctx.Client.Create(ctx, proxySvcDNS)).To(Succeed())

Expect(reconciler.ReconcileNormal(wcrCtx)).To(Succeed())
Expect(wcrCtx.WebConsoleRequest.Status.ProxyAddr).To(Equal(expectedProxy))
},
Entry("API Server DNS Name is set", []string{"domain-1.test"}, "domain-1.test"),
Entry("API Server DNS Name is not set", []string{}, "dummy-proxy-ip"),
)
})
})

}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2022-2023 VMware, Inc. All Rights Reserved.
// Copyright (c) 2022-2024 VMware, Inc. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package v1alpha2
Expand All @@ -12,7 +12,6 @@ import (
"time"

"github.com/go-logr/logr"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
Expand All @@ -25,14 +24,12 @@ import (
"github.com/vmware-tanzu/vm-operator/pkg/patch"
"github.com/vmware-tanzu/vm-operator/pkg/providers"
"github.com/vmware-tanzu/vm-operator/pkg/record"
proxyaddr "github.com/vmware-tanzu/vm-operator/pkg/util/kube/proxyaddr"
)

const (
DefaultExpiryTime = time.Second * 120
UUIDLabelKey = "vmoperator.vmware.com/webconsolerequest-uuid"

ProxyAddrServiceName = "kube-apiserver-lb-svc"
ProxyAddrServiceNamespace = "kube-system"
)

// AddToManager adds this package's controller to the provided manager.
Expand Down Expand Up @@ -179,18 +176,11 @@ func (r *Reconciler) ReconcileNormal(ctx *pkgctx.WebConsoleRequestContextV1) err
ctx.WebConsoleRequest.Status.Response = ticket
ctx.WebConsoleRequest.Status.ExpiryTime = metav1.NewTime(metav1.Now().Add(DefaultExpiryTime))

// Retrieve the proxy address from the load balancer service ingress IP.
proxySvc := &corev1.Service{}
proxySvcObjectKey := client.ObjectKey{Name: ProxyAddrServiceName, Namespace: ProxyAddrServiceNamespace}
err = r.Get(ctx, proxySvcObjectKey, proxySvc)
proxyAddr, err := proxyaddr.ProxyAddress(ctx, r)
if err != nil {
return fmt.Errorf("failed to get proxy address service %s: %w", proxySvcObjectKey, err)
}
if len(proxySvc.Status.LoadBalancer.Ingress) == 0 {
return fmt.Errorf("no ingress found for proxy address service %s", proxySvcObjectKey)
return err
}

ctx.WebConsoleRequest.Status.ProxyAddr = proxySvc.Status.LoadBalancer.Ingress[0].IP
ctx.WebConsoleRequest.Status.ProxyAddr = proxyAddr

// Add UUID as a Label to the current WebConsoleRequest resource after acquiring the ticket.
// This will be used when validating the connection request from users to the web console URL.
Expand Down
Loading

0 comments on commit 558b911

Please sign in to comment.