Skip to content

Commit

Permalink
Merge pull request Azure#19 from m1kola/api_registration_refactoring
Browse files Browse the repository at this point in the history
API registration refactoring & front-end tests
  • Loading branch information
jim-minter committed Jan 6, 2020
2 parents a14de1e + da8f271 commit ddd6d46
Show file tree
Hide file tree
Showing 29 changed files with 973 additions and 314 deletions.
4 changes: 3 additions & 1 deletion cmd/aro/rp.go
Expand Up @@ -12,6 +12,8 @@ import (
uuid "github.com/satori/go.uuid"
"github.com/sirupsen/logrus"

"github.com/Azure/ARO-RP/pkg/api"
_ "github.com/Azure/ARO-RP/pkg/api/v20191231preview"
"github.com/Azure/ARO-RP/pkg/backend"
"github.com/Azure/ARO-RP/pkg/database"
"github.com/Azure/ARO-RP/pkg/env"
Expand Down Expand Up @@ -42,7 +44,7 @@ func rp(ctx context.Context, log *logrus.Entry) error {
return err
}

f, err := frontend.NewFrontend(ctx, log.WithField("component", "frontend"), env, db)
f, err := frontend.NewFrontend(ctx, log.WithField("component", "frontend"), env, db, api.APIs)
if err != nil {
return err
}
Expand Down
39 changes: 19 additions & 20 deletions pkg/api/register.go
Expand Up @@ -6,31 +6,30 @@ package api
import (
"context"

"github.com/Azure/go-autorest/autorest"
"github.com/Azure/ARO-RP/pkg/env"
)

// OpenShiftClusterToExternal is implemented by all APIs - it enables conversion
// of the internal OpenShiftCluster representation to the API-specific versioned
// external representation
type OpenShiftClusterToExternal interface {
OpenShiftClusterToExternal(*OpenShiftCluster) interface{}
type OpenShiftClusterConverter interface {
ToExternal(*OpenShiftCluster) interface{}
ToExternalList([]*OpenShiftCluster) interface{}
ToInternal(interface{}, *OpenShiftCluster)
}

// OpenShiftClustersToExternal is implemented by APIs that can convert multiple
// internal OpenShiftCluster representations to the API-specific versioned
// external representation
type OpenShiftClustersToExternal interface {
OpenShiftClustersToExternal([]*OpenShiftCluster) interface{}
type OpenShiftClusterValidator interface {
Static(interface{}, *OpenShiftCluster) error
Dynamic(context.Context, *OpenShiftCluster) error
}

// OpenShiftClusterToInternal is implemented by APIs that can convert their
// API-specific versioned external representation to the internal
// OpenShiftCluster representation. It also includes validators
type OpenShiftClusterToInternal interface {
OpenShiftClusterToInternal(interface{}, *OpenShiftCluster)
ValidateOpenShiftCluster(string, string, interface{}, *OpenShiftCluster) error
ValidateOpenShiftClusterDynamic(context.Context, func(string, string) (autorest.Authorizer, error), *OpenShiftCluster) error
type OpenShiftClusterCredentialsConverter interface {
ToExternal(*OpenShiftCluster) interface{}
}

// APIs is the map of registered external APIs
var APIs = map[string]map[string]interface{}{}
// Version is a set of endpoints implemented by each API version
type Version struct {
OpenShiftClusterConverter func() OpenShiftClusterConverter
OpenShiftClusterValidator func(env.Interface, string) OpenShiftClusterValidator
OpenShiftClusterCredentialsConverter func() OpenShiftClusterCredentialsConverter
}

// APIs is the map of registered API versions
var APIs = map[string]*Version{}
33 changes: 18 additions & 15 deletions pkg/api/v20191231preview/openshiftcluster_convert.go
Expand Up @@ -7,12 +7,13 @@ import (
"github.com/Azure/ARO-RP/pkg/api"
)

// openShiftClusterToExternal returns a new external representation of the
// internal object, reading from the subset of the internal object's fields that
// appear in the external representation. ToExternal does not modify its
// argument; there is no pointer aliasing between the passed and returned
// objects
func openShiftClusterToExternal(oc *api.OpenShiftCluster) *OpenShiftCluster {
type openShiftClusterConverter struct{}

// ToExternal returns a new external representation of the internal object,
// reading from the subset of the internal object's fields that appear in the
// external representation. ToExternal does not modify its argument; there is
// no pointer aliasing between the passed and returned objects
func (c *openShiftClusterConverter) ToExternal(oc *api.OpenShiftCluster) interface{} {
out := &OpenShiftCluster{
ID: oc.ID,
Name: oc.Name,
Expand Down Expand Up @@ -76,25 +77,27 @@ func openShiftClusterToExternal(oc *api.OpenShiftCluster) *OpenShiftCluster {
return out
}

// openShiftClustersToExternal returns a slice of external representations of
// the internal objects
func openShiftClustersToExternal(ocs []*api.OpenShiftCluster) *OpenShiftClusterList {
// ToExternalList returns a slice of external representations of the internal
// objects
func (c *openShiftClusterConverter) ToExternalList(ocs []*api.OpenShiftCluster) interface{} {
l := &OpenShiftClusterList{
OpenShiftClusters: make([]*OpenShiftCluster, 0, len(ocs)),
}

for _, oc := range ocs {
l.OpenShiftClusters = append(l.OpenShiftClusters, openShiftClusterToExternal(oc))
l.OpenShiftClusters = append(l.OpenShiftClusters, c.ToExternal(oc).(*OpenShiftCluster))
}

return l
}

// openShiftClusterToInternal overwrites in place a pre-existing internal
// object, setting (only) all mapped fields from the external representation.
// ToInternal modifies its argument; there is no pointer aliasing between the
// passed and returned objects
func openShiftClusterToInternal(oc *OpenShiftCluster, out *api.OpenShiftCluster) {
// ToInternal overwrites in place a pre-existing internal object, setting (only)
// all mapped fields from the external representation. ToInternal modifies its
// argument; there is no pointer aliasing between the passed and returned
// objects
func (c *openShiftClusterConverter) ToInternal(_oc interface{}, out *api.OpenShiftCluster) {
oc := _oc.(*OpenShiftCluster)

out.ID = oc.ID
out.Name = oc.Name
out.Type = oc.Type
Expand Down
45 changes: 22 additions & 23 deletions pkg/api/v20191231preview/openshiftcluster_validatedynamic.go
Expand Up @@ -20,6 +20,7 @@ import (
"k8s.io/apimachinery/pkg/util/wait"

"github.com/Azure/ARO-RP/pkg/api"
"github.com/Azure/ARO-RP/pkg/env"
"github.com/Azure/ARO-RP/pkg/util/azureclient/mgmt/authorization"
utilpermissions "github.com/Azure/ARO-RP/pkg/util/permissions"
"github.com/Azure/ARO-RP/pkg/util/subnet"
Expand All @@ -33,60 +34,58 @@ func (*azureClaim) Valid() error {
return fmt.Errorf("unimplemented")
}

type dynamicValidator struct {
type openShiftClusterDynamicValidator struct {
env env.Interface

subnets subnet.Manager

oc *api.OpenShiftCluster
r azure.Resource
}

// validateOpenShiftClusterDynamic validates an OpenShift cluster
func validateOpenShiftClusterDynamic(ctx context.Context, getFPAuthorizer func(string, string) (autorest.Authorizer, error), oc *api.OpenShiftCluster) error {
// Dynamic validates an OpenShift cluster
func (v *openShiftClusterValidator) Dynamic(ctx context.Context, oc *api.OpenShiftCluster) error {
v.dv.oc = oc

r, err := azure.ParseResourceID(oc.ID)
if err != nil {
return err
}

v := &dynamicValidator{
oc: oc,
r: r,
}

// TODO: pre-check that the cluster domain doesn't already exist

spAuthorizer, err := v.validateServicePrincipalProfile()
spAuthorizer, err := v.dv.validateServicePrincipalProfile()
if err != nil {
return err
}

err = v.validateServicePrincipalRole()
err = v.dv.validateServicePrincipalRole()
if err != nil {
return err
}

spPermissions := authorization.NewPermissionsClient(v.r.SubscriptionID, spAuthorizer)
err = v.validateVnetPermissions(ctx, spPermissions, api.CloudErrorCodeInvalidServicePrincipalPermissions, "provided service principal")
spPermissions := authorization.NewPermissionsClient(r.SubscriptionID, spAuthorizer)
err = v.dv.validateVnetPermissions(ctx, spPermissions, api.CloudErrorCodeInvalidServicePrincipalPermissions, "provided service principal")
if err != nil {
return err
}

fpAuthorizer, err := getFPAuthorizer(oc.Properties.ServicePrincipalProfile.TenantID, azure.PublicCloud.ResourceManagerEndpoint)
fpAuthorizer, err := v.dv.env.FPAuthorizer(oc.Properties.ServicePrincipalProfile.TenantID, azure.PublicCloud.ResourceManagerEndpoint)
if err != nil {
return err
}

fpPermissions := authorization.NewPermissionsClient(v.r.SubscriptionID, fpAuthorizer)
err = v.validateVnetPermissions(ctx, fpPermissions, api.CloudErrorCodeInvalidResourceProviderPermissions, "resource provider")
fpPermissions := authorization.NewPermissionsClient(r.SubscriptionID, fpAuthorizer)
err = v.dv.validateVnetPermissions(ctx, fpPermissions, api.CloudErrorCodeInvalidResourceProviderPermissions, "resource provider")
if err != nil {
return err
}

v.subnets = subnet.NewManager(r.SubscriptionID, spAuthorizer)
v.dv.subnets = subnet.NewManager(r.SubscriptionID, spAuthorizer)

return v.validateSubnets(ctx)
return v.dv.validateSubnets(ctx)
}

func (dv *dynamicValidator) validateServicePrincipalProfile() (autorest.Authorizer, error) {
func (dv *openShiftClusterDynamicValidator) validateServicePrincipalProfile() (autorest.Authorizer, error) {
spp := &dv.oc.Properties.ServicePrincipalProfile
conf := auth.NewClientCredentialsConfig(spp.ClientID, spp.ClientSecret, spp.TenantID)

Expand All @@ -106,7 +105,7 @@ func (dv *dynamicValidator) validateServicePrincipalProfile() (autorest.Authoriz
return autorest.NewBearerAuthorizer(token), nil
}

func (dv *dynamicValidator) validateServicePrincipalRole() error {
func (dv *openShiftClusterDynamicValidator) validateServicePrincipalRole() error {
spp := &dv.oc.Properties.ServicePrincipalProfile
conf := auth.NewClientCredentialsConfig(spp.ClientID, spp.ClientSecret, spp.TenantID)
conf.Resource = azure.PublicCloud.GraphEndpoint
Expand Down Expand Up @@ -140,7 +139,7 @@ func (dv *dynamicValidator) validateServicePrincipalRole() error {
return nil
}

func (dv *dynamicValidator) validateVnetPermissions(ctx context.Context, client authorization.PermissionsClient, code, typ string) error {
func (dv *openShiftClusterDynamicValidator) validateVnetPermissions(ctx context.Context, client authorization.PermissionsClient, code, typ string) error {
vnetID, _, err := subnet.Split(dv.oc.Properties.MasterProfile.SubnetID)
if err != nil {
return err
Expand Down Expand Up @@ -179,7 +178,7 @@ func (dv *dynamicValidator) validateVnetPermissions(ctx context.Context, client
return nil
}

func (dv *dynamicValidator) validateSubnets(ctx context.Context) error {
func (dv *openShiftClusterDynamicValidator) validateSubnets(ctx context.Context) error {
master, err := dv.validateSubnet(ctx, "properties.masterProfile.subnetId", "master", dv.oc.Properties.MasterProfile.SubnetID)
if err != nil {
return err
Expand Down Expand Up @@ -208,7 +207,7 @@ func (dv *dynamicValidator) validateSubnets(ctx context.Context) error {
return nil
}

func (dv *dynamicValidator) validateSubnet(ctx context.Context, path, typ, subnetID string) (*net.IPNet, error) {
func (dv *openShiftClusterDynamicValidator) validateSubnet(ctx context.Context, path, typ, subnetID string) (*net.IPNet, error) {
s, err := dv.subnets.Get(ctx, subnetID)
if err != nil {
if err, ok := err.(autorest.DetailedError); ok {
Expand Down

0 comments on commit ddd6d46

Please sign in to comment.