Skip to content

Commit

Permalink
Merge branch 'master' into feature/propertyset
Browse files Browse the repository at this point in the history
  • Loading branch information
theunrepentantgeek authored Jul 22, 2021
2 parents 307399e + 1e5b3d2 commit e92d643
Show file tree
Hide file tree
Showing 41 changed files with 1,660 additions and 250 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,19 @@
* Licensed under the MIT license.
*/

package storage

import "github.com/Azure/azure-service-operator/hack/generator/pkg/astmodel"
package astmodel

// FunctionInjector is a utility for injecting function definitions into resources and objects
type FunctionInjector struct {
// visitor is used to do the actual injection
visitor astmodel.TypeVisitor
visitor TypeVisitor
}

// NewFunctionInjector creates a new function injector for modifying resources and objects
func NewFunctionInjector() *FunctionInjector {
result := &FunctionInjector{}

result.visitor = astmodel.TypeVisitorBuilder{
result.visitor = TypeVisitorBuilder{
VisitObjectType: result.injectFunctionIntoObject,
VisitResourceType: result.injectFunctionIntoResource,
}.Build()
Expand All @@ -26,22 +24,32 @@ func NewFunctionInjector() *FunctionInjector {
}

// Inject modifies the passed type definition by injecting the passed function
func (fi *FunctionInjector) Inject(def astmodel.TypeDefinition, fn astmodel.Function) (astmodel.TypeDefinition, error) {
return fi.visitor.VisitDefinition(def, fn)
func (fi *FunctionInjector) Inject(def TypeDefinition, fns ...Function) (TypeDefinition, error) {
result := def

for _, fn := range fns {
var err error
result, err = fi.visitor.VisitDefinition(result, fn)
if err != nil {
return TypeDefinition{}, err
}
}

return result, nil
}

// injectFunctionIntoObject takes the function provided as a context and includes it on the
// provided object type
func (_ *FunctionInjector) injectFunctionIntoObject(
_ *astmodel.TypeVisitor, ot *astmodel.ObjectType, ctx interface{}) (astmodel.Type, error) {
fn := ctx.(astmodel.Function)
_ *TypeVisitor, ot *ObjectType, ctx interface{}) (Type, error) {
fn := ctx.(Function)
return ot.WithFunction(fn), nil
}

// injectFunctionIntoResource takes the function provided as a context and includes it on the
// provided resource type
func (_ *FunctionInjector) injectFunctionIntoResource(
_ *astmodel.TypeVisitor, rt *astmodel.ResourceType, ctx interface{}) (astmodel.Type, error) {
fn := ctx.(astmodel.Function)
_ *TypeVisitor, rt *ResourceType, ctx interface{}) (Type, error) {
fn := ctx.(Function)
return rt.WithFunction(fn), nil
}
5 changes: 5 additions & 0 deletions hack/generator/pkg/astmodel/interface_implementation.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ func (iface *InterfaceImplementation) References() TypeNameSet {
return results
}

// FunctionCount returns the number of included functions
func (iface *InterfaceImplementation) FunctionCount() int {
return len(iface.functions)
}

// Equals determines if this interface is equal to another interface
func (iface *InterfaceImplementation) Equals(other *InterfaceImplementation) bool {
if len(iface.functions) != len(other.functions) {
Expand Down
49 changes: 49 additions & 0 deletions hack/generator/pkg/astmodel/interface_injector.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright (c) Microsoft Corporation.
* Licensed under the MIT license.
*/

package astmodel

// InterfaceInjector is a utility for injecting interface implementations into resources and objects
type InterfaceInjector struct {
// visitor is used to do the actual injection
visitor TypeVisitor
}

// NewInterfaceInjector creates a new interface injector for modifying resources and objects
func NewInterfaceInjector() *InterfaceInjector {
result := &InterfaceInjector{}

result.visitor = TypeVisitorBuilder{
VisitObjectType: result.injectInterfaceIntoObject,
VisitResourceType: result.injectInterfaceIntoResource,
}.Build()

return result
}

// Inject modifies the passed type definition by injecting the passed function
func (i *InterfaceInjector) Inject(def TypeDefinition, implementation *InterfaceImplementation) (TypeDefinition, error) {
result, err := i.visitor.VisitDefinition(def, implementation)
if err != nil {
return TypeDefinition{}, err
}
return result, nil
}

// injectFunctionIntoObject takes the function provided as a context and includes it on the
// provided object type
func (i *InterfaceInjector) injectInterfaceIntoObject(
_ *TypeVisitor, ot *ObjectType, ctx interface{}) (Type, error) {
implementation := ctx.(*InterfaceImplementation)
return ot.WithInterface(implementation), nil
}

// injectFunctionIntoResource takes the function provided as a context and includes it on the
// provided resource type
func (i *InterfaceInjector) injectInterfaceIntoResource(
_ *TypeVisitor, rt *ResourceType, ctx interface{}) (Type, error) {
fn := ctx.(*InterfaceImplementation)
return rt.WithInterface(fn), nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,19 @@
* Licensed under the MIT license.
*/

package storage

import "github.com/Azure/azure-service-operator/hack/generator/pkg/astmodel"
package astmodel

// PropertyInjector is a utility for injecting property definitions into resources and objects
type PropertyInjector struct {
// visitor is used to do the actual injection
visitor astmodel.TypeVisitor
visitor TypeVisitor
}

// NewPropertyInjector creates a new property injector for modifying resources and objects
func NewPropertyInjector() *PropertyInjector {
result := &PropertyInjector{}

result.visitor = astmodel.TypeVisitorBuilder{
result.visitor = TypeVisitorBuilder{
VisitObjectType: result.injectPropertyIntoObject,
VisitResourceType: result.injectPropertyIntoResource,
}.Build()
Expand All @@ -26,20 +24,20 @@ func NewPropertyInjector() *PropertyInjector {
}

// Inject modifies the passed type definition by injecting the passed property
func (pi *PropertyInjector) Inject(def astmodel.TypeDefinition, prop *astmodel.PropertyDefinition) (astmodel.TypeDefinition, error) {
func (pi *PropertyInjector) Inject(def TypeDefinition, prop *PropertyDefinition) (TypeDefinition, error) {
return pi.visitor.VisitDefinition(def, prop)
}

// injectPropertyIntoObject takes the property provided as a context and includes it on the provided object type
func (pi *PropertyInjector) injectPropertyIntoObject(
_ *astmodel.TypeVisitor, ot *astmodel.ObjectType, ctx interface{}) (astmodel.Type, error) {
prop := ctx.(*astmodel.PropertyDefinition)
_ *TypeVisitor, ot *ObjectType, ctx interface{}) (Type, error) {
prop := ctx.(*PropertyDefinition)
return ot.WithProperty(prop), nil
}

// injectPropertyIntoResource takes the property provided as a context and includes it on the provided resource type
func (pi *PropertyInjector) injectPropertyIntoResource(
_ *astmodel.TypeVisitor, rt *astmodel.ResourceType, ctx interface{}) (astmodel.Type, error) {
prop := ctx.(*astmodel.PropertyDefinition)
_ *TypeVisitor, rt *ResourceType, ctx interface{}) (Type, error) {
prop := ctx.(*PropertyDefinition)
return rt.WithProperty(prop), nil
}
72 changes: 71 additions & 1 deletion hack/generator/pkg/astmodel/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ func (types Types) ResolveResourceStatusDefinition(
}

// Process applies a func to transform all members of this set of type definitions, returning a new set of type
// definitions containing the results of the transfomration, or possibly an error
// definitions containing the results of the transformation, or possibly an error
// Only definitions returned by the func will be included in the results of the function. The func may return a nil
// TypeDefinition if it doesn't want to include anything in the output set.
func (types Types) Process(transformation func(definition TypeDefinition) (*TypeDefinition, error)) (Types, error) {
Expand All @@ -262,3 +262,73 @@ func (types Types) Process(transformation func(definition TypeDefinition) (*Type

return result, nil
}

// FindResourceTypes walks the provided set of TypeDefinitions and returns all the resource types
func FindResourceTypes(types Types) Types {
result := make(Types)

// Find all our resources and extract all their Specs
for _, def := range types {
_, ok := AsResourceType(def.Type())
if !ok {
continue
}

// We have a resource type
result.Add(def)
}

return result
}

// FindSpecTypes walks the provided set of TypeDefinitions and returns all the spec types
func FindSpecTypes(types Types) Types {
result := make(Types)

// Find all our resources and extract all their Specs
for _, def := range types {
rt, ok := AsResourceType(def.Type())
if !ok {
continue
}

// We have a resource type
tn, ok := AsTypeName(rt.SpecType())
if !ok {
continue
}

// Add the named spec type to our results
if spec, ok := types.TryGet(tn); ok {
result.Add(spec)
}
}

return result
}

// FindStatusTypes walks the provided set of TypeDefinitions and returns all the status types
func FindStatusTypes(types Types) Types {
result := make(Types)

// Find all our resources and extract all their Statuses
for _, def := range types {
rt, ok := AsResourceType(def.Type())
if !ok {
continue
}

// We have a resource type
tn, ok := AsTypeName(rt.StatusType())
if !ok {
continue
}

// Add the named status type to our results
if status, ok := types.TryGet(tn); ok {
result.Add(status)
}
}

return result
}
71 changes: 71 additions & 0 deletions hack/generator/pkg/astmodel/types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,48 @@ func Test_TypesOverlayWith_GivenOverlappingSets_PrefersTypeInOverlay(t *testing.
g.Expect(set).NotTo(ContainElement(deltaDefinition))
}

/*
* FindSpecTypes() tests
*/

func TestFindSpecTypes(t *testing.T) {
g := NewGomegaWithT(t)

// Define a test resource
spec := createTestSpec("Person", fullNameProperty, knownAsProperty)
status := createTestStatus("Person")
resource := createTestResource("Person", spec, status)

types := make(Types)
types.AddAll(resource, status, spec)

specs := FindSpecTypes(types)

g.Expect(specs).To(HaveLen(1))
g.Expect(specs.Contains(spec.Name())).To(BeTrue())
}

/*
* FindStatusTypes() tests
*/

func TestFindStatusTypes(t *testing.T) {
g := NewGomegaWithT(t)

// Define a test resource
spec := createTestSpec("Person", fullNameProperty, knownAsProperty)
status := createTestStatus("Person")
resource := createTestResource("Person", spec, status)

types := make(Types)
types.AddAll(resource, status, spec)

statuses := FindStatusTypes(types)

g.Expect(statuses).To(HaveLen(1))
g.Expect(statuses.Contains(status.Name())).To(BeTrue())
}

/*
* Utility functions
*/
Expand All @@ -192,3 +234,32 @@ func createTestTypes(defs ...TypeDefinition) Types {

return result
}

// CreateTestResource makes a resource for testing
func createTestResource(
name string,
spec TypeDefinition,
status TypeDefinition) TypeDefinition {

resourceType := NewResourceType(spec.Name(), status.Name())
return MakeTypeDefinition(MakeTypeName(pkg, name), resourceType)
}

// createTestSpec makes a spec for testing
func createTestSpec(
name string,
properties ...*PropertyDefinition) TypeDefinition {
specName := MakeTypeName(pkg, name+"_Spec")
return MakeTypeDefinition(
specName,
NewObjectType().WithProperties(properties...))
}

// createTestStatus makes a status for testing
func createTestStatus(name string) TypeDefinition {
statusProperty := NewPropertyDefinition("Status", "status", StringType)
statusName := MakeTypeName(pkg, name+"_Status")
return MakeTypeDefinition(
statusName,
NewObjectType().WithProperties(statusProperty))
}
8 changes: 3 additions & 5 deletions hack/generator/pkg/codegen/code_generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,7 @@ func createAllPipelineStages(idFactory astmodel.IdentifierFactory, configuration

// De-pluralize resource types
// (Must come after type aliases are resolved)
pipeline.ImproveResourcePluralization().
RequiresPrerequisiteStages("removeAliases"),
pipeline.ImproveResourcePluralization(),

pipeline.StripUnreferencedTypeDefinitions(),

Expand Down Expand Up @@ -175,16 +174,15 @@ func createAllPipelineStages(idFactory astmodel.IdentifierFactory, configuration
See https://github.com/kubernetes-sigs/controller-runtime/blob/master/pkg/webhook/conversion/conversion.go#L310
pipeline.InjectHubFunction(idFactory).UsedFor(pipeline.ARMTarget),
pipeline.ImplementConvertibleInterface(idFactory),
*/

// Safety checks at the end:
pipeline.EnsureDefinitionsDoNotUseAnyTypes(),
pipeline.EnsureARMTypeExistsForEveryResource().UsedFor(pipeline.ARMTarget),

pipeline.DeleteGeneratedCode(configuration.FullTypesOutputPath()),

pipeline.ExportPackages(configuration.FullTypesOutputPath()).
RequiresPrerequisiteStages("deleteGenerated"),
pipeline.ExportPackages(configuration.FullTypesOutputPath()),

pipeline.ExportControllerResourceRegistrations(configuration.FullTypesRegistrationOutputFilePath()).UsedFor(pipeline.ARMTarget),
}
Expand Down
Loading

0 comments on commit e92d643

Please sign in to comment.