Skip to content

Commit c4b0bf4

Browse files
authored
Add proxy address in web-console request (vmware-tanzu#61)
1 parent ce7b39a commit c4b0bf4

File tree

6 files changed

+119
-10
lines changed

6 files changed

+119
-10
lines changed

api/v1alpha1/webconsolerequest_types.go

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) 2021 VMware, Inc. All Rights Reserved.
1+
// Copyright (c) 2021-2023 VMware, Inc. All Rights Reserved.
22
// SPDX-License-Identifier: Apache-2.0
33

44
package v1alpha1
@@ -21,6 +21,30 @@ type WebConsoleRequestStatus struct {
2121
Response string `json:"response,omitempty"`
2222
// ExpiryTime is when the ticket referenced in Response will expire.
2323
ExpiryTime metav1.Time `json:"expiryTime,omitempty"`
24+
// ProxyAddr describes the host address and optional port used to access
25+
// the VM's web console.
26+
// The value could be a DNS entry, IPv4, or IPv6 address, followed by an
27+
// optional port. For example, valid values include:
28+
//
29+
// DNS
30+
// * host.com
31+
// * host.com:6443
32+
//
33+
// IPv4
34+
// * 1.2.3.4
35+
// * 1.2.3.4:6443
36+
//
37+
// IPv6
38+
// * 1234:1234:1234:1234:1234:1234:1234:1234
39+
// * [1234:1234:1234:1234:1234:1234:1234:1234]:6443
40+
// * 1234:1234:1234:0000:0000:0000:1234:1234
41+
// * 1234:1234:1234::::1234:1234
42+
// * [1234:1234:1234::::1234:1234]:6443
43+
//
44+
// In other words, the field may be set to any value that is parsable
45+
// by Go's https://pkg.go.dev/net#ResolveIPAddr and
46+
// https://pkg.go.dev/net#ParseIP functions.
47+
ProxyAddr string `json:"proxyAddr,omitempty"`
2448
}
2549

2650
// +kubebuilder:object:root=true

config/crd/bases/vmoperator.vmware.com_webconsolerequests.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,18 @@ spec:
5858
will expire.
5959
format: date-time
6060
type: string
61+
proxyAddr:
62+
description: "ProxyAddr describes the host address and optional port
63+
used to access the VM's web console. The value could be a DNS entry,
64+
IPv4, or IPv6 address, followed by an optional port. For example,
65+
valid values include: \n DNS * host.com * host.com:6443 \n IPv4
66+
* 1.2.3.4 * 1.2.3.4:6443 \n IPv6 * 1234:1234:1234:1234:1234:1234:1234:1234
67+
* [1234:1234:1234:1234:1234:1234:1234:1234]:6443 * 1234:1234:1234:0000:0000:0000:1234:1234
68+
* 1234:1234:1234::::1234:1234 * [1234:1234:1234::::1234:1234]:6443
69+
\n In other words, the field may be set to any value that is parsable
70+
by Go's https://pkg.go.dev/net#ResolveIPAddr and https://pkg.go.dev/net#ParseIP
71+
functions."
72+
type: string
6173
response:
6274
description: Response will be the authenticated ticket corresponding
6375
to this web console request.

controllers/webconsolerequest/webconsolerequest_controller.go

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) 2022 VMware, Inc. All Rights Reserved.
1+
// Copyright (c) 2022-2023 VMware, Inc. All Rights Reserved.
22
// SPDX-License-Identifier: Apache-2.0
33

44
package webconsolerequest
@@ -13,6 +13,7 @@ import (
1313

1414
"github.com/go-logr/logr"
1515
"github.com/pkg/errors"
16+
corev1 "k8s.io/api/core/v1"
1617
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1718
ctrl "sigs.k8s.io/controller-runtime"
1819
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -30,6 +31,9 @@ import (
3031
const (
3132
DefaultExpiryTime = time.Second * 120
3233
UUIDLabelKey = "vmoperator.vmware.com/webconsolerequest-uuid"
34+
35+
ProxyAddrServiceName = "kube-apiserver-lb-svc"
36+
ProxyAddrServiceNamespace = "kube-system"
3337
)
3438

3539
// AddToManager adds this package's controller to the provided manager.
@@ -79,6 +83,8 @@ type Reconciler struct {
7983
// +kubebuilder:rbac:groups=vmoperator.vmware.com,resources=webconsolerequests,verbs=get;list;watch;create;update;patch;delete
8084
// +kubebuilder:rbac:groups=vmoperator.vmware.com,resources=webconsolerequests/status,verbs=get;update;patch
8185
// +kubebuilder:rbac:groups=vmoperator.vmware.com,resources=virtualmachines,verbs=get;list
86+
// +kubebuilder:rbac:groups="",resources=services,verbs=get;list;watch
87+
// +kubebuilder:rbac:groups="",resources=services/status,verbs=get
8288

8389
func (r *Reconciler) Reconcile(ctx goctx.Context, req ctrl.Request) (_ ctrl.Result, reterr error) {
8490
webconsolerequest := &vmopv1alpha1.WebConsoleRequest{}
@@ -143,9 +149,10 @@ func (r *Reconciler) ReconcileEarlyNormal(ctx *context.WebConsoleRequestContext)
143149
return true, nil
144150
}
145151

146-
if ctx.WebConsoleRequest.Status.Response != "" {
147-
// If the response is already set, no need to reconcile anymore
148-
ctx.Logger.Info("Response already set, skip reconciling")
152+
if ctx.WebConsoleRequest.Status.Response != "" &&
153+
ctx.WebConsoleRequest.Status.ProxyAddr != "" {
154+
// If the response and proxy address are already set, no need to reconcile anymore
155+
ctx.Logger.Info("Response and proxy address already set, skip reconciling")
149156
return true, nil
150157
}
151158

@@ -167,8 +174,21 @@ func (r *Reconciler) ReconcileNormal(ctx *context.WebConsoleRequestContext) erro
167174
ctx.WebConsoleRequest.Status.Response = ticket
168175
ctx.WebConsoleRequest.Status.ExpiryTime = metav1.NewTime(metav1.Now().Add(DefaultExpiryTime))
169176

177+
// Retrieve the proxy address from the load balancer service ingress IP.
178+
proxySvc := &corev1.Service{}
179+
proxySvcObjectKey := client.ObjectKey{Name: ProxyAddrServiceName, Namespace: ProxyAddrServiceNamespace}
180+
err = r.Get(ctx, proxySvcObjectKey, proxySvc)
181+
if err != nil {
182+
return errors.Wrapf(err, "failed to get proxy address service %s", proxySvcObjectKey)
183+
}
184+
if len(proxySvc.Status.LoadBalancer.Ingress) == 0 {
185+
return errors.Errorf("no ingress found for proxy address service %s", proxySvcObjectKey)
186+
}
187+
188+
ctx.WebConsoleRequest.Status.ProxyAddr = proxySvc.Status.LoadBalancer.Ingress[0].IP
189+
170190
// Add UUID as a Label to the current WebConsoleRequest resource after acquiring the ticket.
171-
// This will be used when validating the connection request from users to the web-console URL.
191+
// This will be used when validating the connection request from users to the web console URL.
172192
if ctx.WebConsoleRequest.Labels == nil {
173193
ctx.WebConsoleRequest.Labels = make(map[string]string)
174194
}

controllers/webconsolerequest/webconsolerequest_intg_test.go

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
. "github.com/onsi/ginkgo"
1111
. "github.com/onsi/gomega"
1212

13+
corev1 "k8s.io/api/core/v1"
1314
k8serrors "k8s.io/apimachinery/pkg/api/errors"
1415
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1516
"k8s.io/apimachinery/pkg/types"
@@ -26,9 +27,10 @@ func intgTests() {
2627

2728
func webConsoleRequestReconcile() {
2829
var (
29-
ctx *builder.IntegrationTestContext
30-
wcr *v1alpha1.WebConsoleRequest
31-
vm *v1alpha1.VirtualMachine
30+
ctx *builder.IntegrationTestContext
31+
wcr *v1alpha1.WebConsoleRequest
32+
vm *v1alpha1.VirtualMachine
33+
proxySvc *corev1.Service
3234
)
3335

3436
getWebConsoleRequest := func(ctx *builder.IntegrationTestContext, objKey types.NamespacedName) *v1alpha1.WebConsoleRequest {
@@ -66,6 +68,21 @@ func webConsoleRequestReconcile() {
6668
},
6769
}
6870

71+
proxySvc = &corev1.Service{
72+
ObjectMeta: metav1.ObjectMeta{
73+
Name: webconsolerequest.ProxyAddrServiceName,
74+
Namespace: webconsolerequest.ProxyAddrServiceNamespace,
75+
},
76+
Spec: corev1.ServiceSpec{
77+
Ports: []corev1.ServicePort{
78+
{
79+
Name: "dummy-proxy-port",
80+
Port: 443,
81+
},
82+
},
83+
},
84+
}
85+
6986
fakeVMProvider.Lock()
7087
defer fakeVMProvider.Unlock()
7188
fakeVMProvider.GetVirtualMachineWebMKSTicketFn = func(ctx context.Context, vm *v1alpha1.VirtualMachine, pubKey string) (string, error) {
@@ -83,6 +100,17 @@ func webConsoleRequestReconcile() {
83100
BeforeEach(func() {
84101
Expect(ctx.Client.Create(ctx, vm)).To(Succeed())
85102
Expect(ctx.Client.Create(ctx, wcr)).To(Succeed())
103+
Expect(ctx.Client.Create(ctx, proxySvc)).To(Succeed())
104+
proxySvc.Status = corev1.ServiceStatus{
105+
LoadBalancer: corev1.LoadBalancerStatus{
106+
Ingress: []corev1.LoadBalancerIngress{
107+
{
108+
IP: "192.168.0.1",
109+
},
110+
},
111+
},
112+
}
113+
Expect(ctx.Client.Status().Update(ctx, proxySvc)).To(Succeed())
86114
})
87115

88116
AfterEach(func() {
@@ -100,6 +128,7 @@ func webConsoleRequestReconcile() {
100128
}
101129
return false
102130
}).Should(BeTrue(), "waiting for webconsolerequest to be")
131+
Expect(wcr.Status.ProxyAddr).To(Equal("192.168.0.1"))
103132
Expect(wcr.Status.Response).ToNot(BeEmpty())
104133
Expect(wcr.Status.ExpiryTime.Time).To(BeTemporally("~", time.Now(), webconsolerequest.DefaultExpiryTime))
105134
Expect(wcr.Labels).To(HaveKeyWithValue(webconsolerequest.UUIDLabelKey, string(wcr.UID)))

controllers/webconsolerequest/webconsolerequest_unit_test.go

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
. "github.com/onsi/ginkgo"
1111
. "github.com/onsi/gomega"
1212

13+
corev1 "k8s.io/api/core/v1"
1314
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1415
"sigs.k8s.io/controller-runtime/pkg/client"
1516

@@ -35,6 +36,7 @@ func unitTestsReconcile() {
3536
wcrCtx *vmopContext.WebConsoleRequestContext
3637
wcr *v1alpha1.WebConsoleRequest
3738
vm *v1alpha1.VirtualMachine
39+
proxySvc *corev1.Service
3840
)
3941

4042
BeforeEach(func() {
@@ -53,6 +55,22 @@ func unitTestsReconcile() {
5355
PublicKey: "",
5456
},
5557
}
58+
59+
proxySvc = &corev1.Service{
60+
ObjectMeta: metav1.ObjectMeta{
61+
Name: webconsolerequest.ProxyAddrServiceName,
62+
Namespace: webconsolerequest.ProxyAddrServiceNamespace,
63+
},
64+
Status: corev1.ServiceStatus{
65+
LoadBalancer: corev1.LoadBalancerStatus{
66+
Ingress: []corev1.LoadBalancerIngress{
67+
{
68+
IP: "dummy-proxy-ip",
69+
},
70+
},
71+
},
72+
},
73+
}
5674
})
5775

5876
JustBeforeEach(func() {
@@ -83,7 +101,7 @@ func unitTestsReconcile() {
83101

84102
Context("ReconcileNormal", func() {
85103
BeforeEach(func() {
86-
initObjects = append(initObjects, wcr, vm)
104+
initObjects = append(initObjects, wcr, vm, proxySvc)
87105
})
88106

89107
JustBeforeEach(func() {
@@ -97,6 +115,7 @@ func unitTestsReconcile() {
97115
err := reconciler.ReconcileNormal(wcrCtx)
98116
Expect(err).ToNot(HaveOccurred())
99117

118+
Expect(wcrCtx.WebConsoleRequest.Status.ProxyAddr).To(Equal("dummy-proxy-ip"))
100119
Expect(wcrCtx.WebConsoleRequest.Status.Response).ToNot(BeEmpty())
101120
Expect(wcrCtx.WebConsoleRequest.Status.ExpiryTime.Time).To(BeTemporally("~", time.Now(), webconsolerequest.DefaultExpiryTime))
102121
// Checking the label key only because UID will not be set to a resource during unit test.

docs/apis/v1alpha1.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1140,3 +1140,8 @@ _Appears in:_
11401140
| --- | --- |
11411141
| `response` _string_ | Response will be the authenticated ticket corresponding to this web console request. |
11421142
| `expiryTime` _[Time](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#time-v1-meta)_ | ExpiryTime is when the ticket referenced in Response will expire. |
1143+
| `proxyAddr` _string_ | ProxyAddr describes the host address and optional port used to access the VM's web console. The value could be a DNS entry, IPv4, or IPv6 address, followed by an optional port. For example, valid values include:
1144+
DNS * host.com * host.com:6443
1145+
IPv4 * 1.2.3.4 * 1.2.3.4:6443
1146+
IPv6 * 1234:1234:1234:1234:1234:1234:1234:1234 * [1234:1234:1234:1234:1234:1234:1234:1234]:6443 * 1234:1234:1234:0000:0000:0000:1234:1234 * 1234:1234:1234::::1234:1234 * [1234:1234:1234::::1234:1234]:6443
1147+
In other words, the field may be set to any value that is parsable by Go's https://pkg.go.dev/net#ResolveIPAddr and https://pkg.go.dev/net#ParseIP functions. |

0 commit comments

Comments
 (0)