Skip to content

Commit 0185a91

Browse files
committed
Factor credentials out from client initialization
1 parent 46a03bd commit 0185a91

File tree

4 files changed

+191
-143
lines changed

4 files changed

+191
-143
lines changed

pkg/webhook/preflight/nutanix/checker.go

Lines changed: 104 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,18 @@ import (
1212

1313
prismv4 "github.com/nutanix-cloud-native/prism-go-client/v4"
1414

15+
carenv1 "github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/api/v1alpha1"
16+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/api/variables"
1517
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/webhook/preflight"
16-
preflightutil "github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/webhook/preflight/util"
1718
)
1819

1920
type Checker struct {
20-
client ctrlclient.Client
21-
cluster *clusterv1.Cluster
22-
23-
nutanixClient *prismv4.Client
24-
variablesGetter *preflightutil.VariablesGetter
21+
client ctrlclient.Client
22+
cluster *clusterv1.Cluster
23+
nutanixSpec *carenv1.NutanixSpec
24+
nutanixControlPlaneNodeSpec *carenv1.NutanixNodeSpec
25+
nutanixNodeSpecByMachineDeploymentName map[string]*carenv1.NutanixNodeSpec
26+
v4client *prismv4.Client
2527
}
2628

2729
func (n *Checker) Init(
@@ -31,49 +33,114 @@ func (n *Checker) Init(
3133
) []preflight.Check {
3234
n.client = client
3335
n.cluster = cluster
34-
n.variablesGetter = preflightutil.NewVariablesGetter(cluster)
3536

36-
// Initialize the Nutanix client. If it fails, return a check that indicates the error.
37-
clusterConfig, err := n.variablesGetter.ClusterConfig()
37+
var err error
38+
39+
n.nutanixSpec, n.nutanixControlPlaneNodeSpec, n.nutanixNodeSpecByMachineDeploymentName, err = getData(cluster)
3840
if err != nil {
3941
return []preflight.Check{
40-
func(ctx context.Context) preflight.CheckResult {
41-
return preflight.CheckResult{
42-
Name: "NutanixClientInitialization",
43-
Allowed: false,
44-
Error: true,
45-
Causes: []preflight.Cause{
46-
{
47-
Message: fmt.Sprintf("failed to read clusterConfig variable: %s", err),
48-
Field: "cluster.spec.topology.variables",
49-
},
50-
},
51-
}
52-
},
42+
errorCheck("TBD", "TBD"),
5343
}
5444
}
5545

56-
n.nutanixClient, err = v4client(ctx, client, cluster, clusterConfig.Nutanix)
57-
// TODO Verify the credentials by making a users API call.
46+
credentials, err := getCredentials(ctx, client, cluster, n.nutanixSpec)
5847
if err != nil {
5948
return []preflight.Check{
49+
errorCheck(
50+
fmt.Sprintf("failed to get Nutanix credentials: %s", err),
51+
"cluster.spec.topology.variables[.name=clusterConfig].nutanix.prismCentralEndpoint.credentials",
52+
),
53+
}
54+
}
55+
56+
n.v4client, err = v4client(credentials, n.nutanixSpec)
57+
if err != nil {
58+
return []preflight.Check{
59+
errorCheck(
60+
fmt.Sprintf("failed to initialize Nutanix v4 client: %s", err),
61+
"cluster.spec.topology.variables[.name=clusterConfig].nutanix",
62+
),
63+
}
64+
}
65+
66+
// Initialize checks.
67+
checks := []preflight.Check{}
68+
if n.nutanixControlPlaneNodeSpec != nil {
69+
checks = append(checks,
70+
func(ctx context.Context) preflight.CheckResult {
71+
return n.vmImageCheckForMachineDetails(
72+
&n.nutanixControlPlaneNodeSpec.MachineDetails,
73+
"cluster.spec.topology[.name=clusterConfig].value.controlPlane.nutanix.machineDetails",
74+
)
75+
},
76+
)
77+
}
78+
for mdName, nodeSpec := range n.nutanixNodeSpecByMachineDeploymentName {
79+
checks = append(checks,
6080
func(ctx context.Context) preflight.CheckResult {
61-
return preflight.CheckResult{
62-
Name: "NutanixClientInitialization",
63-
Allowed: false,
64-
Error: true,
65-
Causes: []preflight.Cause{
66-
{
67-
Message: fmt.Sprintf("failed to initialize Nutanix client: %s", err),
68-
Field: "cluster.spec.topology.variables[.name=clusterConfig].nutanix",
69-
},
70-
},
71-
}
81+
return n.vmImageCheckForMachineDetails(
82+
&nodeSpec.MachineDetails,
83+
fmt.Sprintf(
84+
"cluster.spec.topology.workers.machineDeployments[.name=%s]"+
85+
".overrides[.name=workerConfig].value.nutanix.machineDetails",
86+
mdName,
87+
),
88+
)
89+
},
90+
)
91+
}
92+
return checks
93+
}
94+
95+
func errorCheck(msg, field string) preflight.Check {
96+
return func(ctx context.Context) preflight.CheckResult {
97+
return preflight.CheckResult{
98+
Name: "Nutanix",
99+
Allowed: false,
100+
Error: true,
101+
Causes: []preflight.Cause{
102+
{
103+
Message: msg,
104+
Field: field,
105+
},
72106
},
73107
}
74108
}
109+
}
110+
111+
func getData(
112+
cluster *clusterv1.Cluster,
113+
) (*carenv1.NutanixSpec, *carenv1.NutanixNodeSpec, map[string]*carenv1.NutanixNodeSpec, error) {
114+
nutanixSpec := &carenv1.NutanixSpec{}
115+
controlPlaneNutanixNodeSpec := &carenv1.NutanixNodeSpec{}
116+
nutanixNodeSpecByMachineDeploymentName := make(map[string]*carenv1.NutanixNodeSpec)
75117

76-
return []preflight.Check{
77-
n.VMImages,
118+
clusterConfig, err := variables.UnmarshalClusterConfigVariable(cluster.Spec.Topology.Variables)
119+
if err != nil {
120+
return nil, nil, nil, fmt.Errorf("failed to unmarshal .variables[.name=clusterConfig]: %w", err)
78121
}
122+
if clusterConfig != nil && clusterConfig.Nutanix != nil {
123+
nutanixSpec = clusterConfig.Nutanix
124+
}
125+
126+
if clusterConfig.ControlPlane != nil && clusterConfig.ControlPlane.Nutanix != nil {
127+
controlPlaneNutanixNodeSpec = clusterConfig.ControlPlane.Nutanix
128+
}
129+
130+
if cluster.Spec.Topology.Workers != nil {
131+
for _, md := range cluster.Spec.Topology.Workers.MachineDeployments {
132+
workerConfig, err := variables.UnmarshalWorkerConfigVariable(md.Variables.Overrides)
133+
if err != nil {
134+
return nil, nil, nil, fmt.Errorf(
135+
"failed to unmarshal .variables[.name=workerConfig] for machine deployment %s: %w",
136+
md.Name, err,
137+
)
138+
}
139+
if workerConfig != nil && workerConfig.Nutanix != nil {
140+
nutanixNodeSpecByMachineDeploymentName[md.Name] = workerConfig.Nutanix
141+
}
142+
}
143+
}
144+
145+
return nutanixSpec, controlPlaneNutanixNodeSpec, nutanixNodeSpecByMachineDeploymentName, nil
79146
}
Lines changed: 4 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,31 @@
11
package nutanix
22

33
import (
4-
"context"
54
"fmt"
65

7-
corev1 "k8s.io/api/core/v1"
8-
"k8s.io/apimachinery/pkg/types"
9-
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
10-
ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
11-
126
prism "github.com/nutanix-cloud-native/prism-go-client"
13-
prismcredentials "github.com/nutanix-cloud-native/prism-go-client/environment/credentials"
7+
prismtypes "github.com/nutanix-cloud-native/prism-go-client/environment/types"
148
prismv4 "github.com/nutanix-cloud-native/prism-go-client/v4"
159

1610
carenv1 "github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/api/v1alpha1"
1711
)
1812

19-
func v4client(ctx context.Context,
20-
client ctrlclient.Client,
21-
cluster *clusterv1.Cluster,
13+
func v4client(
14+
credentials *prismtypes.ApiCredentials,
2215
nutanixSpec *carenv1.NutanixSpec,
2316
) (
2417
*prismv4.Client,
2518
error,
2619
) {
27-
if nutanixSpec == nil {
28-
return nil, fmt.Errorf("nutanixSpec is nil")
29-
}
30-
31-
if nutanixSpec.PrismCentralEndpoint.Credentials.SecretRef.Name == "" {
32-
return nil, fmt.Errorf("prism Central credentials reference SecretRef.Name has no value")
33-
}
34-
35-
credentialsSecret := &corev1.Secret{}
36-
if err := client.Get(
37-
ctx,
38-
types.NamespacedName{
39-
Namespace: cluster.Namespace,
40-
Name: nutanixSpec.PrismCentralEndpoint.Credentials.SecretRef.Name,
41-
},
42-
credentialsSecret,
43-
); err != nil {
44-
return nil, fmt.Errorf("failed to get Prism Central credentials Secret: %w", err)
45-
}
46-
47-
// Get username and password
48-
credentials, err := prismcredentials.ParseCredentials(credentialsSecret.Data["credentials"])
49-
if err != nil {
50-
return nil, fmt.Errorf("failed to parse Prism Central credentials from Secret: %w", err)
51-
}
52-
5320
host, port, err := nutanixSpec.PrismCentralEndpoint.ParseURL()
5421
if err != nil {
5522
return nil, fmt.Errorf("failed to parse Prism Central endpoint: %w", err)
5623
}
5724

58-
nutanixClient, err := prismv4.NewV4Client(prism.Credentials{
25+
return prismv4.NewV4Client(prism.Credentials{
5926
Endpoint: fmt.Sprintf("%s:%d", host, port),
6027
Username: credentials.Username,
6128
Password: credentials.Password,
6229
Insecure: nutanixSpec.PrismCentralEndpoint.Insecure,
6330
})
64-
if err != nil {
65-
return nil, fmt.Errorf("failed to create Prism V4 client: %w", err)
66-
}
67-
68-
return nutanixClient, nil
6931
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package nutanix
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
corev1 "k8s.io/api/core/v1"
8+
"k8s.io/apimachinery/pkg/types"
9+
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
10+
ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
11+
12+
prismcredentials "github.com/nutanix-cloud-native/prism-go-client/environment/credentials"
13+
prismtypes "github.com/nutanix-cloud-native/prism-go-client/environment/types"
14+
15+
carenv1 "github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/api/v1alpha1"
16+
)
17+
18+
const credentialsSecretDataKey = "credentials"
19+
20+
func getCredentials(
21+
ctx context.Context,
22+
client ctrlclient.Client,
23+
cluster *clusterv1.Cluster,
24+
nutanixSpec *carenv1.NutanixSpec,
25+
) (*prismtypes.ApiCredentials, error) {
26+
if nutanixSpec.PrismCentralEndpoint.Credentials.SecretRef.Name == "" {
27+
return nil, fmt.Errorf("secretRef.name has no value")
28+
}
29+
30+
credentialsSecret := &corev1.Secret{}
31+
if err := client.Get(
32+
ctx,
33+
types.NamespacedName{
34+
Namespace: cluster.Namespace,
35+
Name: nutanixSpec.PrismCentralEndpoint.Credentials.SecretRef.Name,
36+
},
37+
credentialsSecret,
38+
); err != nil {
39+
return nil, fmt.Errorf("failed to get Secret: %w", err)
40+
}
41+
42+
if len(credentialsSecret.Data) == 0 {
43+
return nil, fmt.Errorf(
44+
"the Secret %s/%s has no data",
45+
cluster.Namespace,
46+
nutanixSpec.PrismCentralEndpoint.Credentials.SecretRef.Name,
47+
)
48+
}
49+
50+
data, ok := credentialsSecret.Data[credentialsSecretDataKey]
51+
if !ok {
52+
return nil, fmt.Errorf(
53+
"the Secret %s/%s has no data for key %s",
54+
cluster.Namespace,
55+
nutanixSpec.PrismCentralEndpoint.Credentials.SecretRef.Name,
56+
credentialsSecretDataKey,
57+
)
58+
}
59+
// Get username and password
60+
return prismcredentials.ParseCredentials(data)
61+
}

0 commit comments

Comments
 (0)