Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CFE-1051: Add the webhook validation for "CapacityReservationGroupID" to "AzureMachineProviderSpec" in openshift/machine-api-operator #1234

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ endif
PROJECT_DIR := $(shell dirname $(abspath $(lastword $(MAKEFILE_LIST))))
ENVTEST = go run ${PROJECT_DIR}/vendor/sigs.k8s.io/controller-runtime/tools/setup-envtest
# ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary.
ENVTEST_K8S_VERSION = 1.29
ENVTEST_K8S_VERSION = 1.29.1

.PHONY: vendor
vendor:
Expand Down Expand Up @@ -103,7 +103,7 @@ test-sec:
test: unit

unit:
KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path --bin-dir $(PROJECT_DIR)/bin)" ./hack/ci-test.sh
KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path --bin-dir $(PROJECT_DIR)/bin --remote-bucket openshift-kubebuilder-tools)" ./hack/ci-test.sh

.PHONY: image
image: ## Build docker image
Expand Down
3 changes: 2 additions & 1 deletion cmd/vsphere/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"time"

configv1 "github.com/openshift/api/config/v1"
apifeatures "github.com/openshift/api/features"
machinev1 "github.com/openshift/api/machine/v1beta1"
configv1client "github.com/openshift/client-go/config/clientset/versioned"
configinformers "github.com/openshift/client-go/config/informers/externalversions"
Expand Down Expand Up @@ -173,7 +174,7 @@ func main() {
klog.Fatalf("unable to retrieve current feature gates: %v", err)
}
// read featuregate read and usage to set a variable to pass to a controller
staticIPFeatureGateEnabled := featureGates.Enabled(configv1.FeatureGateVSphereStaticIPs)
staticIPFeatureGateEnabled := featureGates.Enabled(apifeatures.FeatureGateVSphereStaticIPs)

// Initialize machine actuator.
machineActuator := machine.NewActuator(machine.ActuatorParams{
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ require (
github.com/google/uuid v1.4.0
github.com/onsi/ginkgo/v2 v2.14.0
github.com/onsi/gomega v1.30.0
github.com/openshift/api v0.0.0-20240124164020-e2ce40831f2e
github.com/openshift/api v0.0.0-20240521141249-8af21b7ed3e3
github.com/openshift/client-go v0.0.0-20240115204758-e6bf7d631d5e
github.com/openshift/library-go v0.0.0-20240116081341-964bcb3f545c
github.com/prometheus/client_golang v1.18.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -479,8 +479,8 @@ github.com/onsi/ginkgo/v2 v2.14.0 h1:vSmGj2Z5YPb9JwCWT6z6ihcUvDhuXLc3sJiqd3jMKAY
github.com/onsi/ginkgo/v2 v2.14.0/go.mod h1:JkUdW7JkN0V6rFvsHcJ478egV3XH9NxpD27Hal/PhZw=
github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8=
github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
github.com/openshift/api v0.0.0-20240124164020-e2ce40831f2e h1:cxgCNo/R769CO23AK5TCh45H9SMUGZ8RukiF2/Qif3o=
github.com/openshift/api v0.0.0-20240124164020-e2ce40831f2e/go.mod h1:CxgbWAlvu2iQB0UmKTtRu1YfepRg1/vJ64n2DlIEVz4=
github.com/openshift/api v0.0.0-20240521141249-8af21b7ed3e3 h1:fxqjG8C/fU1UfUalZhNB01jqIQDlBsCVsFnWZ1V17Rg=
github.com/openshift/api v0.0.0-20240521141249-8af21b7ed3e3/go.mod h1:CxgbWAlvu2iQB0UmKTtRu1YfepRg1/vJ64n2DlIEVz4=
github.com/openshift/client-go v0.0.0-20240115204758-e6bf7d631d5e h1:qGjfKX8i0h4efMNEnhgTdxcdx6gwwOwhTfBJ20WFqA8=
github.com/openshift/client-go v0.0.0-20240115204758-e6bf7d631d5e/go.mod h1:2am3qrggh9LlDCf/MDGzcFWMhdaushxFQi0+ZZDhdVk=
github.com/openshift/library-go v0.0.0-20240116081341-964bcb3f545c h1:gLylEQQryG+A6nqWYIwE1wUzn1eFUmthjADvflMWKnM=
Expand Down
6 changes: 3 additions & 3 deletions hack/crds-sync.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
set -euo pipefail

# map names of CRD files between the vendored openshift/api repository and the ./install directory
CRDS_MAPPING=( "vendor/github.com/openshift/api/machine/v1beta1/0000_10_machine.crd.yaml:0000_30_machine-api-operator_02_machine.crd.yaml"
"vendor/github.com/openshift/api/machine/v1beta1/0000_10_machineset.crd.yaml:0000_30_machine-api-operator_03_machineset.crd.yaml"
"vendor/github.com/openshift/api/machine/v1beta1/0000_10_machinehealthcheck.yaml:0000_30_machine-api-operator_07_machinehealthcheck.crd.yaml")
CRDS_MAPPING=( "vendor/github.com/openshift/api/machine/v1beta1/zz_generated.crd-manifests/0000_10_machine-api_01_machines.crd.yaml:0000_30_machine-api-operator_02_machine.crd.yaml"
"vendor/github.com/openshift/api/machine/v1beta1/zz_generated.crd-manifests/0000_10_machine-api_01_machinesets.crd.yaml:0000_30_machine-api-operator_03_machineset.crd.yaml"
"vendor/github.com/openshift/api/machine/v1beta1/zz_generated.crd-manifests/0000_10_machine-api_01_machinehealthchecks.crd.yaml:0000_30_machine-api-operator_07_machinehealthcheck.crd.yaml")

for crd in "${CRDS_MAPPING[@]}" ; do
SRC="${crd%%:*}"
Expand Down
737 changes: 447 additions & 290 deletions install/0000_30_machine-api-operator_02_machine.crd.yaml

Large diffs are not rendered by default.

840 changes: 522 additions & 318 deletions install/0000_30_machine-api-operator_03_machineset.crd.yaml

Large diffs are not rendered by default.

404 changes: 236 additions & 168 deletions install/0000_30_machine-api-operator_07_machinehealthcheck.crd.yaml

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion pkg/controller/vsphere/actuator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ func TestMachineEvents(t *testing.T) {
testEnv := &envtest.Environment{
CRDDirectoryPaths: []string{
filepath.Join("..", "..", "..", "install"),
filepath.Join("..", "..", "..", "vendor", "github.com", "openshift", "api", "config", "v1"),
filepath.Join("..", "..", "..", "vendor", "github.com", "openshift", "api", "config", "v1", "zz_generated.crd-manifests"),
filepath.Join("..", "..", "..", "third_party", "cluster-api", "crd")},
}

Expand Down
4 changes: 2 additions & 2 deletions pkg/controller/vsphere/machine_scope_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ func TestPatchMachine(t *testing.T) {
testEnv := &envtest.Environment{
CRDDirectoryPaths: []string{
filepath.Join("..", "..", "..", "install"),
filepath.Join("..", "..", "..", "vendor", "github.com", "openshift", "api", "config", "v1")},
filepath.Join("..", "..", "..", "vendor", "github.com", "openshift", "api", "config", "v1", "zz_generated.crd-manifests")},
}

cfg, err := testEnv.Start()
Expand Down Expand Up @@ -546,7 +546,7 @@ func TestNodeGetter(t *testing.T) {
testEnv := &envtest.Environment{
CRDDirectoryPaths: []string{
filepath.Join("..", "..", "..", "install"),
filepath.Join("..", "..", "..", "vendor", "github.com", "openshift", "api", "config", "v1")},
filepath.Join("..", "..", "..", "vendor", "github.com", "openshift", "api", "config", "v1", "zz_generated.crd-manifests")},
}

cfg, err := testEnv.Start()
Expand Down
2 changes: 1 addition & 1 deletion pkg/controller/vsphere/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import (

const (
globalInfrastuctureName = "cluster"
openshiftConfigNamespace = "openshift-config"
openshiftConfigNamespace = "openshift-config-1"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is significance of this new namespace?

Copy link
Contributor Author

@anirudhAgniRedhat anirudhAgniRedhat May 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@TrilokGeer Have to update this because the existing suite test was failing because the old namespace is forbidden to delete.

)

// vSphereConfig is a copy of the Kubernetes vSphere cloud provider config type
Expand Down
7 changes: 4 additions & 3 deletions pkg/operator/operator.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"time"

osconfigv1 "github.com/openshift/api/config/v1"
apifeatures "github.com/openshift/api/features"
osoperatorv1 "github.com/openshift/api/operator/v1"
osclientset "github.com/openshift/client-go/config/clientset/versioned"
configinformersv1 "github.com/openshift/client-go/config/informers/externalversions/config/v1"
Expand Down Expand Up @@ -171,9 +172,9 @@ func New(

klog.V(4).InfoS("FeatureGates changed", "enabled", featureChange.New.Enabled, "disabled", featureChange.New.Disabled)
prevDisableMHC := featuregates.NewFeatureGate(featureChange.Previous.Enabled, featureChange.Previous.Disabled).
Enabled(osconfigv1.FeatureGateMachineAPIOperatorDisableMachineHealthCheckController)
Enabled(apifeatures.FeatureGateMachineAPIOperatorDisableMachineHealthCheckController)
newDisableMHC := featuregates.NewFeatureGate(featureChange.New.Enabled, featureChange.New.Disabled).
Enabled(osconfigv1.FeatureGateMachineAPIOperatorDisableMachineHealthCheckController)
Enabled(apifeatures.FeatureGateMachineAPIOperatorDisableMachineHealthCheckController)

if prevDisableMHC != newDisableMHC {
klog.V(2).InfoS("Resync for modified feature gate",
Expand Down Expand Up @@ -461,7 +462,7 @@ func (optr *Operator) maoConfigFromInfrastructure() (*OperatorConfig, error) {
if err != nil {
return nil, err
}
if featureGates.Enabled(osconfigv1.FeatureGateMachineAPIOperatorDisableMachineHealthCheckController) {
if featureGates.Enabled(apifeatures.FeatureGateMachineAPIOperatorDisableMachineHealthCheckController) {
klog.V(2).Info("Disabling MHC controller")
mhcImage = ""
}
Expand Down
7 changes: 4 additions & 3 deletions pkg/operator/operator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (

. "github.com/onsi/gomega"
openshiftv1 "github.com/openshift/api/config/v1"
apifeatures "github.com/openshift/api/features"
fakeos "github.com/openshift/client-go/config/clientset/versioned/fake"
configinformersv1 "github.com/openshift/client-go/config/informers/externalversions"
fakemachine "github.com/openshift/client-go/machine/clientset/versioned/fake"
Expand Down Expand Up @@ -61,7 +62,7 @@ func newFakeOperator(kubeObjects, osObjects, machineObjects []runtime.Object, im
{
Version: "",
Enabled: []openshiftv1.FeatureGateAttributes{},
Disabled: []openshiftv1.FeatureGateAttributes{{Name: openshiftv1.FeatureGateMachineAPIOperatorDisableMachineHealthCheckController}},
Disabled: []openshiftv1.FeatureGateAttributes{{Name: apifeatures.FeatureGateMachineAPIOperatorDisableMachineHealthCheckController}},
},
},
},
Expand Down Expand Up @@ -571,7 +572,7 @@ func TestMAOConfigFromInfrastructure(t *testing.T) {
{
Version: "",
Enabled: []openshiftv1.FeatureGateAttributes{},
Disabled: []openshiftv1.FeatureGateAttributes{{Name: openshiftv1.FeatureGateMachineAPIOperatorDisableMachineHealthCheckController}},
Disabled: []openshiftv1.FeatureGateAttributes{{Name: apifeatures.FeatureGateMachineAPIOperatorDisableMachineHealthCheckController}},
},
},
},
Expand Down Expand Up @@ -603,7 +604,7 @@ func TestMAOConfigFromInfrastructure(t *testing.T) {
FeatureGates: []openshiftv1.FeatureGateDetails{
{
Version: "",
Enabled: []openshiftv1.FeatureGateAttributes{{Name: openshiftv1.FeatureGateMachineAPIOperatorDisableMachineHealthCheckController}},
Enabled: []openshiftv1.FeatureGateAttributes{{Name: apifeatures.FeatureGateMachineAPIOperatorDisableMachineHealthCheckController}},
Disabled: []openshiftv1.FeatureGateAttributes{},
},
},
Expand Down
75 changes: 75 additions & 0 deletions pkg/webhooks/machine_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,12 @@ const (
powerVSImage = "image"
powerVSSystemTypeE880 = "e880"
powerVSSystemTypeE980 = "e980"
azureProviderIDPrefix = "azure://"
azureProvidersKey = "providers"
azureSubscriptionsKey = "subscriptions"
azureResourceGroupsLowerKey = "resourcegroups"
azureLocationsKey = "locations"
azureBuiltInResourceNamespace = "Microsoft.Resources"
)

// GCP Confidential VM supports Compute Engine machine types in the following series:
Expand Down Expand Up @@ -925,6 +931,13 @@ func validateAzure(m *machinev1beta1.Machine, config *admissionConfig) (bool, []
fmt.Sprintf("osDisk.diskSettings.ephemeralStorageLocation can either be omitted or set to %s", azureEphemeralStorageLocationLocal)))
}

if providerSpec.CapacityReservationGroupID != "" {
err := validateAzureCapacityReservationGroupID(providerSpec.CapacityReservationGroupID)
if err != nil {
errs = append(errs, field.Invalid(field.NewPath("providerSpec", "capacityReservationGroupID"), providerSpec.CapacityReservationGroupID, err.Error()))
}
}

switch providerSpec.OSDisk.CachingType {
case azureCachingTypeNone, azureCachingTypeReadOnly, azureCachingTypeReadWrite, "":
// Valid scenarios, do nothing
Expand Down Expand Up @@ -2055,3 +2068,65 @@ func validateGVK(gvk schema.GroupVersionKind, platform osconfigv1.PlatformType)
return true
}
}

func validateAzureCapacityReservationGroupID(capacityReservationGroupID string) error {
id := strings.TrimPrefix(capacityReservationGroupID, azureProviderIDPrefix)
err := parseAzureResourceID(id)
if err != nil {
return err
}
return nil
}

// parseAzureResourceID parses a string to an instance of ResourceID
func parseAzureResourceID(id string) error {
if len(id) == 0 {
return fmt.Errorf("invalid resource ID: id cannot be empty")
}
if !strings.HasPrefix(id, "/") {
return fmt.Errorf("invalid resource ID: resource id '%s' must start with '/'", id)
}
parts := splitStringAndOmitEmpty(id, "/")

if len(parts) < 2 {
return fmt.Errorf("invalid resource ID: %s", id)
}
if !strings.EqualFold(parts[0], azureSubscriptionsKey) && !strings.EqualFold(parts[0], azureProvidersKey) {
return fmt.Errorf("invalid resource ID: %s", id)
}
return appendNextAzureResourceIDValidation(parts, id)
}

func splitStringAndOmitEmpty(v, sep string) []string {
r := make([]string, 0)
for _, s := range strings.Split(v, sep) {
if len(s) == 0 {
continue
}
r = append(r, s)
}
return r
}

func appendNextAzureResourceIDValidation(parts []string, id string) error {
if len(parts) == 0 {
return nil
}
if len(parts) == 1 {
// subscriptions and resourceGroups are not valid ids without their names
if strings.EqualFold(parts[0], azureSubscriptionsKey) || strings.EqualFold(parts[0], azureResourceGroupsLowerKey) {
return fmt.Errorf("invalid resource ID: %s", id)
}
return nil
}
if strings.EqualFold(parts[0], azureProvidersKey) && (len(parts) == 2 || strings.EqualFold(parts[2], azureProvidersKey)) {
return appendNextAzureResourceIDValidation(parts[2:], id)
}
if len(parts) > 3 && strings.EqualFold(parts[0], azureProvidersKey) {
return appendNextAzureResourceIDValidation(parts[4:], id)
}
if len(parts) > 1 && !strings.EqualFold(parts[0], azureProvidersKey) {
return appendNextAzureResourceIDValidation(parts[2:], id)
}
return fmt.Errorf("invalid resource ID: %s", id)
}
83 changes: 83 additions & 0 deletions pkg/webhooks/machine_webhook_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,48 @@ func TestMachineCreation(t *testing.T) {
},
expectedError: "",
},
{
name: "with Azure and CapacityReservationID is empty",
platformType: osconfigv1.AzurePlatformType,
clusterID: "azure-cluster",
providerSpecValue: &kruntime.RawExtension{
Object: &machinev1beta1.AzureMachineProviderSpec{
OSDisk: machinev1beta1.OSDisk{
DiskSizeGB: 128,
},
CapacityReservationGroupID: "",
},
},
expectedError: "",
},
{
name: "with Azure and CapacityReservationID is valid",
platformType: osconfigv1.AzurePlatformType,
clusterID: "azure-cluster",
providerSpecValue: &kruntime.RawExtension{
Object: &machinev1beta1.AzureMachineProviderSpec{
OSDisk: machinev1beta1.OSDisk{
DiskSizeGB: 128,
},
CapacityReservationGroupID: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroupName/providers/Microsoft.Compute/capacityReservationGroups/myCapacityReservationGroup",
},
},
expectedError: "",
},
{
name: "with Azure and CapacityReservationID is not valid",
platformType: osconfigv1.AzurePlatformType,
clusterID: "azure-cluster",
providerSpecValue: &kruntime.RawExtension{
Object: &machinev1beta1.AzureMachineProviderSpec{
OSDisk: machinev1beta1.OSDisk{
DiskSizeGB: 128,
},
CapacityReservationGroupID: "/subscri/00000000-0000-0000-0000-000000000000/resour/myResourceGroupName/providers/Microsoft.Compute/capacityReservationGroups/myCapacityReservationGroup",
},
},
expectedError: "admission webhook \"validation.machine.machine.openshift.io\" denied the request: providerSpec.capacityReservationGroupID: Invalid value: \"/subscri/00000000-0000-0000-0000-000000000000/resour/myResourceGroupName/providers/Microsoft.Compute/capacityReservationGroups/myCapacityReservationGroup\": invalid resource ID: /subscri/00000000-0000-0000-0000-000000000000/resour/myResourceGroupName/providers/Microsoft.Compute/capacityReservationGroups/myCapacityReservationGroup",
},
{
name: "with Azure and a Premium Disk Data Disk set",
platformType: osconfigv1.AzurePlatformType,
Expand Down Expand Up @@ -5040,3 +5082,44 @@ func TestValidateNutanixProviderSpec(t *testing.T) {
})
}
}

func TestValidateAzureCapacityReservationGroupID(t *testing.T) {
testCases := []struct {
name string
inputID string
expectError bool
}{
{
name: "validation for capacityReservationGroupID should return nil error if field input is valid",
inputID: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroupName/providers/Microsoft.Compute/capacityReservationGroups/myCapacityReservationGroup",
expectError: false,
},
{
name: "validation for capacityReservationGroupID should return error if field input does not start with '/'",
inputID: "subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroupName/providers/Microsoft.Compute/capacityReservationGroups/myCapacityReservationGroup",
expectError: true,
},
{
name: "validation for capacityReservationGroupID should return error if field input does not have field name subscriptions",
inputID: "/subscripti/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroupName/providers/Microsoft.Compute/capacityReservationGroups/myCapacityReservationGroup",
expectError: true,
},
{
name: "validation for capacityReservationGroupID should return error if field input is empty",
inputID: "",
expectError: true,
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
g := NewWithT(t)
err := validateAzureCapacityReservationGroupID(tc.inputID)
if tc.expectError {
g.Expect(err).To(HaveOccurred())
} else {
g.Expect(err).ToNot(HaveOccurred())
}
})
}
}
2 changes: 1 addition & 1 deletion pkg/webhooks/v1beta1_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ func TestMain(m *testing.M) {
testEnv = &envtest.Environment{
CRDDirectoryPaths: []string{
filepath.Join("..", "..", "install"),
filepath.Join("..", "..", "..", "..", "vendor", "github.com", "openshift", "api", "config", "v1"),
filepath.Join("..", "..", "..", "..", "vendor", "github.com", "openshift", "api", "config", "v1", "zz_generated.crd-manifests"),
},
WebhookInstallOptions: envtest.WebhookInstallOptions{
MutatingWebhooks: []*admissionregistrationv1.MutatingWebhookConfiguration{NewMachineMutatingWebhookConfiguration()},
Expand Down
2 changes: 2 additions & 0 deletions tools.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,7 @@ package tools
import (
_ "github.com/golangci/golangci-lint/cmd/golangci-lint"
_ "github.com/onsi/ginkgo/v2/ginkgo"
_ "github.com/openshift/api/config/v1/zz_generated.crd-manifests"
_ "github.com/openshift/api/machine/v1beta1/zz_generated.crd-manifests"
_ "sigs.k8s.io/controller-runtime/tools/setup-envtest"
)
6 changes: 4 additions & 2 deletions vendor/github.com/openshift/api/Makefile

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading