Skip to content

Commit 70819fe

Browse files
committed
✨ Add support for defining an identity in the vspheremachine and defaulting to this indentity if it exists.
1 parent 357565e commit 70819fe

11 files changed

+198
-11
lines changed

apis/v1alpha3/zz_generated.conversion.go

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

apis/v1alpha4/zz_generated.conversion.go

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

apis/v1beta1/types.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,10 @@ type VirtualMachineCloneSpec struct {
209209
// +listMapKey=name
210210
// +kubebuilder:validation:MaxItems=29
211211
DataDisks []VSphereDisk `json:"dataDisks,omitempty"`
212+
// IdentityRef is a reference to either a Secret or VSphereClusterIdentity that contains
213+
// the identity to use when reconciling the virtual machine.
214+
// +optional
215+
IdentityRef *VSphereIdentityReference `json:"identityRef,omitempty"`
212216
}
213217

214218
// VSphereDisk is an additional disk to add to the VM that is not part of the VM OVA template.

apis/v1beta1/zz_generated.deepcopy.go

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/default/crd/bases/infrastructure.cluster.x-k8s.io_vspheremachines.yaml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1053,6 +1053,26 @@ spec:
10531053
virtual machine is cloned.
10541054
Check the compatibility with the ESXi version before setting the value.
10551055
type: string
1056+
identityRef:
1057+
description: |-
1058+
IdentityRef is a reference to either a Secret or VSphereClusterIdentity that contains
1059+
the identity to use when reconciling the virtual machine.
1060+
properties:
1061+
kind:
1062+
description: Kind of the identity. Can either be VSphereClusterIdentity
1063+
or Secret
1064+
enum:
1065+
- VSphereClusterIdentity
1066+
- Secret
1067+
type: string
1068+
name:
1069+
description: Name of the identity.
1070+
minLength: 1
1071+
type: string
1072+
required:
1073+
- kind
1074+
- name
1075+
type: object
10561076
memoryMiB:
10571077
description: |-
10581078
MemoryMiB is the size of a virtual machine's memory, in MiB.

config/default/crd/bases/infrastructure.cluster.x-k8s.io_vspheremachinetemplates.yaml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -923,6 +923,26 @@ spec:
923923
virtual machine is cloned.
924924
Check the compatibility with the ESXi version before setting the value.
925925
type: string
926+
identityRef:
927+
description: |-
928+
IdentityRef is a reference to either a Secret or VSphereClusterIdentity that contains
929+
the identity to use when reconciling the virtual machine.
930+
properties:
931+
kind:
932+
description: Kind of the identity. Can either be VSphereClusterIdentity
933+
or Secret
934+
enum:
935+
- VSphereClusterIdentity
936+
- Secret
937+
type: string
938+
name:
939+
description: Name of the identity.
940+
minLength: 1
941+
type: string
942+
required:
943+
- kind
944+
- name
945+
type: object
926946
memoryMiB:
927947
description: |-
928948
MemoryMiB is the size of a virtual machine's memory, in MiB.

config/default/crd/bases/infrastructure.cluster.x-k8s.io_vspherevms.yaml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1138,6 +1138,26 @@ spec:
11381138
virtual machine is cloned.
11391139
Check the compatibility with the ESXi version before setting the value.
11401140
type: string
1141+
identityRef:
1142+
description: |-
1143+
IdentityRef is a reference to either a Secret or VSphereClusterIdentity that contains
1144+
the identity to use when reconciling the virtual machine.
1145+
properties:
1146+
kind:
1147+
description: Kind of the identity. Can either be VSphereClusterIdentity
1148+
or Secret
1149+
enum:
1150+
- VSphereClusterIdentity
1151+
- Secret
1152+
type: string
1153+
name:
1154+
description: Name of the identity.
1155+
minLength: 1
1156+
type: string
1157+
required:
1158+
- kind
1159+
- name
1160+
type: object
11411161
memoryMiB:
11421162
description: |-
11431163
MemoryMiB is the size of a virtual machine's memory, in MiB.

controllers/vspherevm_controller.go

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -588,14 +588,25 @@ func (r vmReconciler) ipAddressClaimToVSphereVM(_ context.Context, a ctrlclient.
588588

589589
func (r vmReconciler) retrieveVcenterSession(ctx context.Context, vsphereVM *infrav1.VSphereVM) (*session.Session, error) {
590590
log := ctrl.LoggerFrom(ctx)
591-
// Get cluster object and then get VSphereCluster object
592591

593592
params := session.NewParams().
594593
WithServer(vsphereVM.Spec.Server).
595594
WithDatacenter(vsphereVM.Spec.Datacenter).
596-
WithUserInfo(r.ControllerManagerContext.Username, r.ControllerManagerContext.Password).
597595
WithThumbprint(vsphereVM.Spec.Thumbprint)
598596

597+
// if there is an identityref coming with the vsphereVM, we use that regardless of the state/existence of the cluster & vspherecluster
598+
if vsphereVM.Spec.IdentityRef != nil {
599+
creds, err := identity.GetCredentialsFromVshpereVM(ctx, r.Client, vsphereVM, r.Namespace)
600+
if err != nil {
601+
return nil, err
602+
}
603+
params = params.WithUserInfo(creds.Username, creds.Password)
604+
log.V(4).Info("Using credentials attached to the VsphereVM")
605+
return session.GetOrCreate(ctx, params)
606+
} else {
607+
// if the vsphereVM doesn't have an identityRef, set the default user identity to that provided by the ControllerManager
608+
params = params.WithUserInfo(r.ControllerManagerContext.Username, r.ControllerManagerContext.Password)
609+
}
599610
cluster, err := clusterutilv1.GetClusterFromMetadata(ctx, r.Client, vsphereVM.ObjectMeta)
600611
if err != nil {
601612
log.V(4).Info("Using credentials provided to the manager to create the authenticated session, VSphereVM is missing cluster label or cluster does not exist")

docs/multi-vcenter.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Multi VCenter support
2+
3+
Cluster API Provider vSphere (CAPV) supports multiple VCenter for a single. Therefore CAPV is allowing to define the used identity for each machine. CAPV will check on every Machine first, if there is a local identity otherwise it fallback on the default selection method.
4+
5+
In order to run a CAPV cluster in multiple VCenter, you have to configure CPI & CSI to support multi VCenter, see [guide](https://docs.vmware.com/en/VMware-vSphere-Container-Storage-Plug-in/3.0/vmware-vsphere-csp-getting-started/GUID-8B3B9004-DE37-4E6B-9AA1-234CDA1BD7F9.html). Trivia, `VSphereCluster` can be only in single VCenter. This will just used as a fallback, if you haven't configured a different identity for a `VSphereMachine``.
6+
7+
## Examples
8+
9+
Deploy a `VSphereMachine` with a custom identityRef:
10+
11+
```yaml
12+
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
13+
kind: VSphereMachine
14+
metadata:
15+
name: new-workload-cluster
16+
spec:
17+
server: vcenter
18+
identityRef:
19+
kind: VSphereClusterIdentity
20+
name: identityName
21+
...
22+
```
23+
24+
Deploy a `VSphereMachineTemplate` with a custom identityRef:
25+
26+
```yaml
27+
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
28+
kind: VSphereMachineTemplate
29+
metadata:
30+
name: new-workload-cluster
31+
spec:
32+
template:
33+
spec:
34+
server: vcenter
35+
identityRef:
36+
kind: VSphereClusterIdentity
37+
name: identityName
38+
...
39+
```

pkg/identity/identity.go

Lines changed: 72 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,76 @@ type Credentials struct {
4444
Password string
4545
}
4646

47-
// GetCredentials returns the VCenter credentials for the VSphereCluster.
47+
func GetCredentialsFromVshpereVM(ctx context.Context, c client.Client, machine *infrav1.VSphereVM, controllerNamespace string) (*Credentials, error) {
48+
if err := validateInputs(c, machine.Namespace, machine.Spec.IdentityRef); err != nil {
49+
return nil, err
50+
}
51+
52+
ref := machine.Spec.IdentityRef
53+
secret := &corev1.Secret{}
54+
var secretKey client.ObjectKey
55+
56+
switch ref.Kind {
57+
case infrav1.SecretKind:
58+
secretKey = client.ObjectKey{
59+
Namespace: machine.Namespace,
60+
Name: ref.Name,
61+
}
62+
case infrav1.VSphereClusterIdentityKind:
63+
identity := &infrav1.VSphereClusterIdentity{}
64+
key := client.ObjectKey{
65+
Name: ref.Name,
66+
}
67+
if err := c.Get(ctx, key, identity); err != nil {
68+
return nil, err
69+
}
70+
71+
if !identity.Status.Ready {
72+
return nil, errors.New("identity isn't ready to be used yet")
73+
}
74+
75+
if identity.Spec.AllowedNamespaces == nil {
76+
return nil, errors.New("allowedNamespaces set to nil, no namespaces are allowed to use this identity")
77+
}
78+
79+
selector, err := metav1.LabelSelectorAsSelector(&identity.Spec.AllowedNamespaces.Selector)
80+
if err != nil {
81+
return nil, errors.New("failed to build selector")
82+
}
83+
84+
ns := &corev1.Namespace{}
85+
nsKey := client.ObjectKey{
86+
Name: machine.Namespace,
87+
}
88+
if err := c.Get(ctx, nsKey, ns); err != nil {
89+
return nil, err
90+
}
91+
if !selector.Matches(labels.Set(ns.GetLabels())) {
92+
return nil, fmt.Errorf("namespace %s is not allowed to use specifified identity", machine.Namespace)
93+
}
94+
95+
secretKey = client.ObjectKey{
96+
Name: identity.Spec.SecretName,
97+
Namespace: controllerNamespace,
98+
}
99+
default:
100+
return nil, fmt.Errorf("unknown type %s used for Identity", ref.Kind)
101+
}
102+
103+
if err := c.Get(ctx, secretKey, secret); err != nil {
104+
return nil, err
105+
}
106+
107+
credentials := &Credentials{
108+
Username: getData(secret, UsernameKey),
109+
Password: getData(secret, PasswordKey),
110+
}
111+
112+
return credentials, nil
113+
}
114+
48115
func GetCredentials(ctx context.Context, c client.Client, cluster *infrav1.VSphereCluster, controllerNamespace string) (*Credentials, error) {
49-
if err := validateInputs(c, cluster); err != nil {
116+
if err := validateInputs(c, cluster.Namespace, cluster.Spec.IdentityRef); err != nil {
50117
return nil, err
51118
}
52119

@@ -113,15 +180,14 @@ func GetCredentials(ctx context.Context, c client.Client, cluster *infrav1.VSphe
113180
return credentials, nil
114181
}
115182

116-
func validateInputs(c client.Client, cluster *infrav1.VSphereCluster) error {
183+
func validateInputs(c client.Client, namespace string, identityRef *infrav1.VSphereIdentityReference) error {
117184
if c == nil {
118185
return errors.New("kubernetes client is required")
119186
}
120-
if cluster == nil {
187+
if namespace == "" {
121188
return errors.New("vsphere cluster is required")
122189
}
123-
ref := cluster.Spec.IdentityRef
124-
if ref == nil {
190+
if identityRef == nil {
125191
return errors.New("IdentityRef is required")
126192
}
127193
return nil

0 commit comments

Comments
 (0)