diff --git a/api/v1alpha1/azurevalidator_types.go b/api/v1alpha1/azurevalidator_types.go index 0efcaf4d..f9afec26 100644 --- a/api/v1alpha1/azurevalidator_types.go +++ b/api/v1alpha1/azurevalidator_types.go @@ -17,6 +17,7 @@ limitations under the License. package v1alpha1 import ( + "github.com/validator-labs/validator-plugin-azure/pkg/constants" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -33,6 +34,11 @@ type AzureValidatorSpec struct { Auth AzureAuth `json:"auth" yaml:"auth"` } +// PluginCode returns the Azure validator's plugin code. +func (s AzureValidatorSpec) PluginCode() string { + return constants.PluginCode +} + // ResultCount returns the number of validation results expected for an AzureValidatorSpec. func (s AzureValidatorSpec) ResultCount() int { return len(s.RBACRules) + len(s.CommunityGalleryImageRules) @@ -139,6 +145,16 @@ type AzureValidator struct { Status AzureValidatorStatus `json:"status,omitempty"` } +// PluginCode returns the Azure validator's plugin code. +func (v AzureValidator) PluginCode() string { + return v.Spec.PluginCode() +} + +// ResultCount returns the number of validation results expected for an AzureValidator. +func (v AzureValidator) ResultCount() int { + return v.Spec.ResultCount() +} + //+kubebuilder:object:root=true // AzureValidatorList contains a list of AzureValidator diff --git a/go.mod b/go.mod index 12d9d084..e79020c3 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/go-logr/logr v1.4.2 github.com/onsi/ginkgo/v2 v2.19.1 github.com/onsi/gomega v1.34.1 - github.com/validator-labs/validator v0.1.0 + github.com/validator-labs/validator v0.1.1 golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 k8s.io/api v0.30.3 k8s.io/apimachinery v0.30.3 diff --git a/go.sum b/go.sum index 7b78df29..5296ee24 100644 --- a/go.sum +++ b/go.sum @@ -155,8 +155,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/validator-labs/validator v0.1.0 h1:GVekIT5sG+kcyUbT04qb/pURmd9eE6NNKnSR9yJ1sQk= -github.com/validator-labs/validator v0.1.0/go.mod h1:OeJMHGKW3pWGkvKxHLN7HzjelSILJg2k8w3Z9SdML1g= +github.com/validator-labs/validator v0.1.1 h1:BzUWeSAP5eGHX2oOulJWZxXr+Zz6Uh6ZqP5sYudnv3I= +github.com/validator-labs/validator v0.1.1/go.mod h1:to8CMM+LlTcEzbqxVyHND9/uJO2+ORTFk9ovqVOnCU8= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= diff --git a/internal/controller/azurevalidator_controller.go b/internal/controller/azurevalidator_controller.go index 9cca3916..f8909b7e 100644 --- a/internal/controller/azurevalidator_controller.go +++ b/internal/controller/azurevalidator_controller.go @@ -20,14 +20,12 @@ package controller import ( "context" "errors" - "fmt" "os" "time" "github.com/go-logr/logr" corev1 "k8s.io/api/core/v1" apierrs "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" ktypes "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/cluster-api/util/patch" @@ -35,12 +33,8 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "github.com/validator-labs/validator-plugin-azure/api/v1alpha1" - "github.com/validator-labs/validator-plugin-azure/internal/constants" - azure_utils "github.com/validator-labs/validator-plugin-azure/internal/utils/azure" - "github.com/validator-labs/validator-plugin-azure/internal/validators" + "github.com/validator-labs/validator-plugin-azure/pkg/validate" vapi "github.com/validator-labs/validator/api/v1alpha1" - "github.com/validator-labs/validator/pkg/types" - "github.com/validator-labs/validator/pkg/util" vres "github.com/validator-labs/validator/pkg/validationresult" ) @@ -91,16 +85,16 @@ func (r *AzureValidatorReconciler) Reconcile(ctx context.Context, req ctrl.Reque return ctrl.Result{}, err } nn := ktypes.NamespacedName{ - Name: validationResultName(validator), + Name: vres.Name(validator), Namespace: req.Namespace, } if err := r.Get(ctx, nn, vr); err == nil { - vres.HandleExistingValidationResult(vr, r.Log) + vres.HandleExisting(vr, r.Log) } else { if !apierrs.IsNotFound(err) { l.Error(err, "unexpected error getting ValidationResult") } - if err := vres.HandleNewValidationResult(ctx, r.Client, p, buildValidationResult(validator), r.Log); err != nil { + if err := vres.HandleNew(ctx, r.Client, p, vres.Build(validator), r.Log); err != nil { return ctrl.Result{}, err } return ctrl.Result{RequeueAfter: time.Millisecond}, nil @@ -109,50 +103,11 @@ func (r *AzureValidatorReconciler) Reconcile(ctx context.Context, req ctrl.Reque // Always update the expected result count in case the validator's rules have changed vr.Spec.ExpectedResults = validator.Spec.ResultCount() - resp := types.ValidationResponse{ - ValidationRuleResults: make([]*types.ValidationRuleResult, 0, vr.Spec.ExpectedResults), - ValidationRuleErrors: make([]error, 0, vr.Spec.ExpectedResults), - } - - azureAPI, err := azure_utils.NewAzureAPI() - if err != nil { - l.Error(err, "failed to create Azure API object") - } else { - azureCtx := context.WithoutCancel(ctx) - if os.Getenv("IS_TEST") == "true" { - var cancel context.CancelFunc - azureCtx, cancel = context.WithDeadline(ctx, time.Now().Add(azure_utils.TestClientTimeout)) - defer cancel() - } - - daClient := azure_utils.NewDenyAssignmentsClient(azureCtx, azureAPI.DenyAssignmentsClient) - raClient := azure_utils.NewRoleAssignmentsClient(azureCtx, azureAPI.RoleAssignmentsClient) - rdClient := azure_utils.NewRoleDefinitionsClient(azureCtx, azureAPI.RoleDefinitionsClient) - cgiClient := azure_utils.NewCommunityGalleryImagesClient(azureCtx, azureAPI.CommunityGalleryImagesClientProducer) - - // RBAC rules - rbacSvc := validators.NewRBACRuleService(daClient, raClient, rdClient) - for _, rule := range validator.Spec.RBACRules { - vrr, err := rbacSvc.ReconcileRBACRule(rule) - if err != nil { - l.Error(err, "failed to reconcile RBAC rule") - } - resp.AddResult(vrr, err) - } - - // Community gallery image rules - cgiSvc := validators.NewCommunityGalleryImageRuleService(cgiClient, r.Log) - for _, rule := range validator.Spec.CommunityGalleryImageRules { - vrr, err := cgiSvc.ReconcileCommunityGalleryImageRule(rule) - if err != nil { - l.Error(err, "failed to reconcile community gallery image rule") - } - resp.AddResult(vrr, err) - } - } + // Validate the rules + resp := validate.Validate(validator.Spec, r.Log) // Patch the ValidationResult with the latest ValidationRuleResults - if err := vres.SafeUpdateValidationResult(ctx, p, vr, resp, r.Log); err != nil { + if err := vres.SafeUpdate(ctx, p, vr, resp, r.Log); err != nil { return ctrl.Result{}, err } @@ -185,29 +140,3 @@ func (r *AzureValidatorReconciler) SetupWithManager(mgr ctrl.Manager) error { For(&v1alpha1.AzureValidator{}). Complete(r) } - -func buildValidationResult(validator *v1alpha1.AzureValidator) *vapi.ValidationResult { - return &vapi.ValidationResult{ - ObjectMeta: metav1.ObjectMeta{ - Name: validationResultName(validator), - Namespace: validator.Namespace, - OwnerReferences: []metav1.OwnerReference{ - { - APIVersion: validator.APIVersion, - Kind: validator.Kind, - Name: validator.Name, - UID: validator.UID, - Controller: util.Ptr(true), - }, - }, - }, - Spec: vapi.ValidationResultSpec{ - Plugin: constants.PluginCode, - ExpectedResults: validator.Spec.ResultCount(), - }, - } -} - -func validationResultName(validator *v1alpha1.AzureValidator) string { - return fmt.Sprintf("validator-plugin-azure-%s", validator.Name) -} diff --git a/internal/controller/azurevalidator_controller_test.go b/internal/controller/azurevalidator_controller_test.go index 73239a6e..5a602c5c 100644 --- a/internal/controller/azurevalidator_controller_test.go +++ b/internal/controller/azurevalidator_controller_test.go @@ -8,6 +8,7 @@ import ( . "github.com/onsi/gomega" "github.com/validator-labs/validator-plugin-azure/api/v1alpha1" vapi "github.com/validator-labs/validator/api/v1alpha1" + vres "github.com/validator-labs/validator/pkg/validationresult" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" @@ -159,7 +160,7 @@ var _ = Describe("AzureValidator controller", Ordered, func() { } vr := &vapi.ValidationResult{} - vrKey := types.NamespacedName{Name: validationResultName(val), Namespace: validatorNamespace} + vrKey := types.NamespacedName{Name: vres.Name(val), Namespace: validatorNamespace} valEmptySecretName := val.DeepCopy() valEmptySecretName.Name = fmt.Sprintf("%s-empty-secret-name", azureValidatorName) diff --git a/internal/validators/community_image.go b/pkg/azure/community_image.go similarity index 94% rename from internal/validators/community_image.go rename to pkg/azure/community_image.go index 78ea3eaa..32c2091e 100644 --- a/internal/validators/community_image.go +++ b/pkg/azure/community_image.go @@ -1,5 +1,5 @@ -// Package validators contains services that reconcile the validation rules supported by the plugin. -package validators +// Package azure contains services that reconcile the validation rules supported by the plugin. +package azure import ( "fmt" @@ -10,7 +10,7 @@ import ( corev1 "k8s.io/api/core/v1" "github.com/validator-labs/validator-plugin-azure/api/v1alpha1" - "github.com/validator-labs/validator-plugin-azure/internal/constants" + "github.com/validator-labs/validator-plugin-azure/pkg/constants" vapi "github.com/validator-labs/validator/api/v1alpha1" vapiconstants "github.com/validator-labs/validator/pkg/constants" vapitypes "github.com/validator-labs/validator/pkg/types" diff --git a/internal/validators/community_image_test.go b/pkg/azure/community_image_test.go similarity index 99% rename from internal/validators/community_image_test.go rename to pkg/azure/community_image_test.go index 3698c95e..6b60e396 100644 --- a/internal/validators/community_image_test.go +++ b/pkg/azure/community_image_test.go @@ -1,4 +1,4 @@ -package validators +package azure import ( "errors" diff --git a/internal/validators/rbac.go b/pkg/azure/rbac.go similarity index 97% rename from internal/validators/rbac.go rename to pkg/azure/rbac.go index 73b5dec6..049541a3 100644 --- a/internal/validators/rbac.go +++ b/pkg/azure/rbac.go @@ -1,4 +1,4 @@ -package validators +package azure import ( "fmt" @@ -8,8 +8,8 @@ import ( corev1 "k8s.io/api/core/v1" "github.com/validator-labs/validator-plugin-azure/api/v1alpha1" - "github.com/validator-labs/validator-plugin-azure/internal/constants" - azerr "github.com/validator-labs/validator-plugin-azure/internal/utils/azureerrors" + "github.com/validator-labs/validator-plugin-azure/pkg/constants" + azerr "github.com/validator-labs/validator-plugin-azure/pkg/utils/azureerrors" vapi "github.com/validator-labs/validator/api/v1alpha1" vapiconstants "github.com/validator-labs/validator/pkg/constants" vapitypes "github.com/validator-labs/validator/pkg/types" diff --git a/internal/validators/rbac_permissions.go b/pkg/azure/rbac_permissions.go similarity index 99% rename from internal/validators/rbac_permissions.go rename to pkg/azure/rbac_permissions.go index b86cc258..26a0952d 100644 --- a/internal/validators/rbac_permissions.go +++ b/pkg/azure/rbac_permissions.go @@ -1,4 +1,4 @@ -package validators +package azure import ( "fmt" @@ -7,7 +7,7 @@ import ( "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v2" "golang.org/x/exp/maps" - map_utils "github.com/validator-labs/validator-plugin-azure/internal/utils/maps" + map_utils "github.com/validator-labs/validator-plugin-azure/pkg/utils/maps" ) const ( diff --git a/internal/validators/rbac_permissions_test.go b/pkg/azure/rbac_permissions_test.go similarity index 99% rename from internal/validators/rbac_permissions_test.go rename to pkg/azure/rbac_permissions_test.go index 12e6160c..ddeacba9 100644 --- a/internal/validators/rbac_permissions_test.go +++ b/pkg/azure/rbac_permissions_test.go @@ -1,4 +1,4 @@ -package validators +package azure import ( "reflect" diff --git a/internal/validators/rbac_test.go b/pkg/azure/rbac_test.go similarity index 99% rename from internal/validators/rbac_test.go rename to pkg/azure/rbac_test.go index cf207d77..b1ac6236 100644 --- a/internal/validators/rbac_test.go +++ b/pkg/azure/rbac_test.go @@ -1,4 +1,4 @@ -package validators +package azure import ( "errors" diff --git a/internal/constants/constants.go b/pkg/constants/constants.go similarity index 100% rename from internal/constants/constants.go rename to pkg/constants/constants.go diff --git a/internal/utils/azure/azure.go b/pkg/utils/azure/azure.go similarity index 100% rename from internal/utils/azure/azure.go rename to pkg/utils/azure/azure.go diff --git a/internal/utils/azure/azure_test.go b/pkg/utils/azure/azure_test.go similarity index 100% rename from internal/utils/azure/azure_test.go rename to pkg/utils/azure/azure_test.go diff --git a/internal/utils/azureerrors/errors.go b/pkg/utils/azureerrors/errors.go similarity index 100% rename from internal/utils/azureerrors/errors.go rename to pkg/utils/azureerrors/errors.go diff --git a/internal/utils/maps/maps.go b/pkg/utils/maps/maps.go similarity index 100% rename from internal/utils/maps/maps.go rename to pkg/utils/maps/maps.go diff --git a/internal/utils/maps/maps_test.go b/pkg/utils/maps/maps_test.go similarity index 100% rename from internal/utils/maps/maps_test.go rename to pkg/utils/maps/maps_test.go diff --git a/pkg/validate/validate.go b/pkg/validate/validate.go new file mode 100644 index 00000000..dd39aa7b --- /dev/null +++ b/pkg/validate/validate.go @@ -0,0 +1,63 @@ +// Package validate defines a Validate function that evaluates an AzureValidatorSpec and returns a ValidationResponse. +package validate + +import ( + "context" + "os" + "time" + + "github.com/go-logr/logr" + "github.com/validator-labs/validator/pkg/types" + + "github.com/validator-labs/validator-plugin-azure/api/v1alpha1" + "github.com/validator-labs/validator-plugin-azure/pkg/azure" + utils "github.com/validator-labs/validator-plugin-azure/pkg/utils/azure" +) + +// Validate validates the AzureValidatorSpec and returns a ValidationResponse. +func Validate(spec v1alpha1.AzureValidatorSpec, log logr.Logger) types.ValidationResponse { + resp := types.ValidationResponse{ + ValidationRuleResults: make([]*types.ValidationRuleResult, 0, spec.ResultCount()), + ValidationRuleErrors: make([]error, 0, spec.ResultCount()), + } + + azureAPI, err := utils.NewAzureAPI() + if err != nil { + log.Error(err, "failed to create Azure API object") + return resp + } + + ctx := context.Background() + if os.Getenv("IS_TEST") == "true" { + var cancel context.CancelFunc + ctx, cancel = context.WithDeadline(ctx, time.Now().Add(utils.TestClientTimeout)) + defer cancel() + } + + daClient := utils.NewDenyAssignmentsClient(ctx, azureAPI.DenyAssignmentsClient) + raClient := utils.NewRoleAssignmentsClient(ctx, azureAPI.RoleAssignmentsClient) + rdClient := utils.NewRoleDefinitionsClient(ctx, azureAPI.RoleDefinitionsClient) + cgiClient := utils.NewCommunityGalleryImagesClient(ctx, azureAPI.CommunityGalleryImagesClientProducer) + + // RBAC rules + rbacSvc := azure.NewRBACRuleService(daClient, raClient, rdClient) + for _, rule := range spec.RBACRules { + vrr, err := rbacSvc.ReconcileRBACRule(rule) + if err != nil { + log.Error(err, "failed to reconcile RBAC rule") + } + resp.AddResult(vrr, err) + } + + // Community gallery image rules + cgiSvc := azure.NewCommunityGalleryImageRuleService(cgiClient, log) + for _, rule := range spec.CommunityGalleryImageRules { + vrr, err := cgiSvc.ReconcileCommunityGalleryImageRule(rule) + if err != nil { + log.Error(err, "failed to reconcile community gallery image rule") + } + resp.AddResult(vrr, err) + } + + return resp +}