Skip to content
This repository was archived by the owner on Apr 11, 2024. It is now read-only.

Commit 25d0c9c

Browse files
committed
feat: adds a validation hook
1 parent 3f9425f commit 25d0c9c

File tree

7 files changed

+157
-5
lines changed

7 files changed

+157
-5
lines changed

api/variables/variables.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package variables
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
7+
runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1"
8+
)
9+
10+
func UnmarshalRuntimeVariable[T any](runtimeVariable *runtimehooksv1.Variable, obj *T) error {
11+
err := json.Unmarshal(runtimeVariable.Value.Raw, obj)
12+
if err != nil {
13+
return fmt.Errorf("error unmarshalling variable: %w", err)
14+
}
15+
16+
return nil
17+
}
18+
19+
func GetRuntimhookVariableByName(
20+
name string,
21+
variables []runtimehooksv1.Variable,
22+
) (*runtimehooksv1.Variable, int) {
23+
for i, runtimevar := range variables {
24+
if runtimevar.Name == name {
25+
return &runtimevar, i
26+
}
27+
}
28+
return nil, -1
29+
}

cmd/main.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import (
3434
dockermutation "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/docker/mutation"
3535
dockerworkerconfig "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/docker/workerconfig"
3636
"github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/lifecycle"
37+
"github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/validation"
3738
nutanixclusterconfig "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/nutanix/clusterconfig"
3839
nutanixmutation "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/nutanix/mutation"
3940
nutanixworkerconfig "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/nutanix/workerconfig"
@@ -84,6 +85,7 @@ func main() {
8485

8586
genericLifecycleHandlers := lifecycle.New(globalOptions)
8687

88+
validationHandlers := validation.New()
8789
// Initialize and parse command line flags.
8890
logs.AddFlags(pflag.CommandLine, logs.SkipLoggingConfigurationFlags())
8991
logsv1.AddFlags(logOptions, pflag.CommandLine)
@@ -142,6 +144,7 @@ func main() {
142144
}
143145

144146
var allHandlers []handlers.Named
147+
allHandlers = append(allHandlers, validationHandlers.AllHandlers(mgr)...)
145148
allHandlers = append(allHandlers, genericLifecycleHandlers.AllHandlers(mgr)...)
146149
allHandlers = append(allHandlers, awsMetaHandlers...)
147150
allHandlers = append(allHandlers, dockerMetaHandlers...)
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package validation
2+
3+
import (
4+
"sigs.k8s.io/controller-runtime/pkg/manager"
5+
6+
"github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers"
7+
"github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/validation/helm"
8+
)
9+
10+
type Handlers struct{}
11+
12+
func New() *Handlers {
13+
return &Handlers{}
14+
}
15+
16+
func (h *Handlers) AllHandlers(mgr manager.Manager) []handlers.Named {
17+
validationHandler := helm.New(mgr.GetClient())
18+
return []handlers.Named{
19+
validationHandler,
20+
}
21+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package helm
2+
3+
import (
4+
"context"
5+
"crypto/tls"
6+
"fmt"
7+
"net/http"
8+
9+
runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1"
10+
ctrl "sigs.k8s.io/controller-runtime"
11+
ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
12+
13+
"github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/v1alpha1"
14+
"github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/variables"
15+
"github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/clusterconfig"
16+
)
17+
18+
type HelmRegistryValidator struct {
19+
client ctrlclient.Client
20+
variableName string
21+
}
22+
23+
func New(
24+
c ctrlclient.Client,
25+
) *HelmRegistryValidator {
26+
return &HelmRegistryValidator{
27+
client: c,
28+
variableName: clusterconfig.MetaVariableName,
29+
}
30+
}
31+
32+
func (h *HelmRegistryValidator) Name() string {
33+
return "HelmRegistryValidator"
34+
}
35+
36+
func (h *HelmRegistryValidator) ValidateTopology(
37+
ctx context.Context,
38+
req *runtimehooksv1.ValidateTopologyRequest,
39+
res *runtimehooksv1.ValidateTopologyResponse,
40+
) {
41+
log := ctrl.LoggerFrom(ctx)
42+
clusterVar, ind := variables.GetRuntimhookVariableByName(h.variableName, req.Variables)
43+
if ind == -1 {
44+
log.V(5).Info(fmt.Sprintf("did not find variable %s in %v", h.variableName, req.Variables))
45+
return
46+
}
47+
var cluster v1alpha1.ClusterConfig
48+
if err := variables.UnmarshalRuntimeVariable[v1alpha1.ClusterConfig](clusterVar, &cluster); err != nil {
49+
failString := fmt.Sprintf("failed to unmarshal variable %v to clusterConfig", clusterVar)
50+
log.Error(err, failString)
51+
res.SetStatus(runtimehooksv1.ResponseStatusFailure)
52+
res.SetMessage(failString)
53+
return
54+
}
55+
helmChartRepo := cluster.Spec.Addons.HelmChartRepository
56+
cl := &http.Client{
57+
Transport: &http.Transport{
58+
//nolint:gosec // this is done because customers can occasionally have self signed
59+
// or no certificates to OCI registries
60+
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
61+
},
62+
}
63+
resp, err := cl.Get(fmt.Sprintf("%s/v2", *helmChartRepo))
64+
if err != nil {
65+
failString := fmt.Sprintf("failed to ping provided helm registry %s", *helmChartRepo)
66+
log.Error(err, failString)
67+
res.SetStatus(runtimehooksv1.ResponseStatusFailure)
68+
res.SetMessage(failString)
69+
return
70+
}
71+
defer resp.Body.Close()
72+
if resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusUnauthorized {
73+
res.SetStatus(runtimehooksv1.ResponseStatusSuccess)
74+
return
75+
}
76+
failString := fmt.Sprintf(
77+
"failed to get 401 or 200 response from hitting registry: %s got status: %d",
78+
*helmChartRepo,
79+
resp.StatusCode,
80+
)
81+
log.Error(err, failString)
82+
res.SetStatus(runtimehooksv1.ResponseStatusFailure)
83+
res.SetMessage(failString)
84+
}

pkg/handlers/nutanix/mutation/controlplaneendpoint/variables_test.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@ import (
1717
nutanixclusterconfig "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/nutanix/clusterconfig"
1818
)
1919

20-
var testPrismCentralURL = fmt.Sprintf("https://prism-central.nutanix.com:%d", v1alpha1.DefaultPrismCentralPort)
20+
var testPrismCentralURL = fmt.Sprintf(
21+
"https://prism-central.nutanix.com:%d",
22+
v1alpha1.DefaultPrismCentralPort,
23+
)
2124

2225
func TestVariableValidation(t *testing.T) {
2326
capitest.ValidateDiscoverVariables(

pkg/handlers/nutanix/mutation/prismcentralendpoint/inject_test.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,10 @@ var _ = Describe("Generate Nutanix Prism Central Endpoint patches", func() {
9898
"address",
9999
gomega.BeEquivalentTo("prism-central.nutanix.com"),
100100
),
101-
gomega.HaveKeyWithValue("port", gomega.BeEquivalentTo(v1alpha1.DefaultPrismCentralPort)),
101+
gomega.HaveKeyWithValue(
102+
"port",
103+
gomega.BeEquivalentTo(v1alpha1.DefaultPrismCentralPort),
104+
),
102105
gomega.HaveKeyWithValue("insecure", true),
103106
gomega.HaveKey("credentialRef"),
104107
gomega.Not(gomega.HaveKey("additionalTrustBundle")),

pkg/handlers/nutanix/mutation/prismcentralendpoint/variables_test.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,10 @@ func TestVariableValidation(t *testing.T) {
2929
Vals: v1alpha1.ClusterConfigSpec{
3030
Nutanix: &v1alpha1.NutanixSpec{
3131
PrismCentralEndpoint: v1alpha1.NutanixPrismCentralEndpointSpec{
32-
URL: fmt.Sprintf("https://prism-central.nutanix.com:%d", v1alpha1.DefaultPrismCentralPort),
32+
URL: fmt.Sprintf(
33+
"https://prism-central.nutanix.com:%d",
34+
v1alpha1.DefaultPrismCentralPort,
35+
),
3336
Insecure: false,
3437
Credentials: corev1.LocalObjectReference{
3538
Name: "credentials",
@@ -48,7 +51,10 @@ func TestVariableValidation(t *testing.T) {
4851
Vals: v1alpha1.ClusterConfigSpec{
4952
Nutanix: &v1alpha1.NutanixSpec{
5053
PrismCentralEndpoint: v1alpha1.NutanixPrismCentralEndpointSpec{
51-
URL: fmt.Sprintf("https://10.0.0.1:%d", v1alpha1.DefaultPrismCentralPort),
54+
URL: fmt.Sprintf(
55+
"https://10.0.0.1:%d",
56+
v1alpha1.DefaultPrismCentralPort,
57+
),
5258
Insecure: false,
5359
Credentials: corev1.LocalObjectReference{
5460
Name: "credentials",
@@ -145,7 +151,10 @@ func TestVariableValidation(t *testing.T) {
145151
Vals: v1alpha1.ClusterConfigSpec{
146152
Nutanix: &v1alpha1.NutanixSpec{
147153
PrismCentralEndpoint: v1alpha1.NutanixPrismCentralEndpointSpec{
148-
URL: fmt.Sprintf("https://prism-central.nutanix.com:%d", v1alpha1.DefaultPrismCentralPort),
154+
URL: fmt.Sprintf(
155+
"https://prism-central.nutanix.com:%d",
156+
v1alpha1.DefaultPrismCentralPort,
157+
),
149158
Insecure: false,
150159
},
151160
// ControlPlaneEndpoint is a required field and must always be set

0 commit comments

Comments
 (0)