зеркало из
1
0
Форкнуть 0
* Update comments for new go fmt rules

* Update DevContainer to Go 1.19

* Tweak comment
This commit is contained in:
Bevan Arps 2022-12-03 09:30:32 +13:00 коммит произвёл GitHub
Родитель 898ab11d9d
Коммит 4be29066fa
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
60 изменённых файлов: 562 добавлений и 558 удалений

Просмотреть файл

@ -1,7 +1,7 @@
# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.137.0/containers/go/.devcontainer/base.Dockerfile
# This is pinned to a particular version of go:
FROM mcr.microsoft.com/vscode/devcontainers/go:0-1.18
FROM mcr.microsoft.com/vscode/devcontainers/go:0-1.19
# APT dependencies
ENV DEBIAN_FRONTEND=noninteractive

Просмотреть файл

@ -88,7 +88,7 @@ fi
GOVER=$(go version)
write-info "Go version: ${GOVER[*]}"
GOVERREQUIRED="go1.18.*"
GOVERREQUIRED="go1.19.*"
GOVERACTUAL=$(go version | { read _ _ ver _; echo $ver; })
if ! [[ "$GOVERACTUAL" =~ $GOVERREQUIRED ]]; then
write-error "Go must be version $GOVERREQUIRED, not $GOVERACTUAL; see : https://golang.org/doc/install"

Просмотреть файл

@ -22,7 +22,6 @@ import (
// However, some services put the code & message at the top level instead.
// This is common enough that the Azure Python SDK has specific handling to promote a nested error to the top level.
// See https://github.com/Azure/azure-sdk-for-python/blob/9791fb5bc4cb6001768e6e1fb986b8d8f8326c43/sdk/core/azure-core/azure/core/exceptions.py#L153
//
type CloudError struct {
error error

Просмотреть файл

@ -175,12 +175,12 @@ func (h ResourceHierarchy) fullyQualifiedARMIDImpl(subscriptionID string, origin
// rootKind returns the ResourceHierarchyRoot type of the hierarchy.
// There are 5 cases here:
// 1. The hierarchy is comprised solely of a resource group. This is subscription rooted.
// 2. The hierarchy has multiple entries and roots up to a resource group. This is Resource Group rooted.
// 3. The hierarchy has multiple entries and doesn't root up to a resource group. This is subscription rooted.
// 4. The hierarchy roots up to a tenant scope resource. This is tenant rooted.
// 5. The hierarchy contains a resource that sets genruntime.ChildResourceIDOverrideAnnotation. This is
// "Override" rooted.
// 1. The hierarchy is comprised solely of a resource group. This is subscription rooted.
// 2. The hierarchy has multiple entries and roots up to a resource group. This is Resource Group rooted.
// 3. The hierarchy has multiple entries and doesn't root up to a resource group. This is subscription rooted.
// 4. The hierarchy roots up to a tenant scope resource. This is tenant rooted.
// 5. The hierarchy contains a resource that sets genruntime.ChildResourceIDOverrideAnnotation. This is
// "Override" rooted.
func (h ResourceHierarchy) rootKind(originalHierarchy ResourceHierarchy) ResourceHierarchyRoot {
if len(h) == 0 {
panic("resource hierarchy cannot be len 0")

Просмотреть файл

@ -29,9 +29,9 @@ import (
//
// Ideally we would panic on this error but we don't have a good way to deal with the following
// problem at the moment:
// - during record the controller does GET (404), PUT, … GET (OK)
// - during playback the controller does GET (which now returns OK), DELETE, PUT, …
// and fails due to a missing DELETE recording
// - during record the controller does GET (404), PUT, … GET (OK)
// - during playback the controller does GET (which now returns OK), DELETE, PUT, …
// and fails due to a missing DELETE recording
func translateErrors(r *recorder.Recorder, cassetteName string, t *testing.T) http.RoundTripper {
return errorTranslation{r, cassetteName, nil, t}
}

Просмотреть файл

@ -241,11 +241,11 @@ func (tc *KubePerTestContext) CreateResourceGroup(rg *resources.ResourceGroup) (
// registerCleanup registers the resource for cleanup at the end of the test. We must do this for every resource
// for two reasons:
// 1. Because OwnerReferences based deletion doesn't even run in EnvTest, see:
// https://book.kubebuilder.io/reference/envtest.html#testing-considerations
// 2. Even if it did run, it happens in the background which means that there's no guarantee that all the resources
// are deleted before the test ends. When the resources aren't deleted, they attempt to log to a closed logger
// which panics.
// 1. Because OwnerReferences based deletion doesn't even run in EnvTest, see:
// https://book.kubebuilder.io/reference/envtest.html#testing-considerations
// 2. Even if it did run, it happens in the background which means that there's no guarantee that all the resources
// are deleted before the test ends. When the resources aren't deleted, they attempt to log to a closed logger
// which panics.
func (tc *KubePerTestContext) registerCleanup(obj client.Object) {
tc.tracker.Track(obj)
}
@ -279,10 +279,10 @@ var OperationTimeoutReplaying = 2 * time.Minute
// OperationTimeoutRecording is the default timeout for a single operation when recording.
// This is so high because the following operations are slow:
// * Deleting an AKS cluster.
// * Creating a Redis Enterprise Database.
// * Deleting a CosmosDB MongoDB.
// * Creating a Virtual Network Gateway Controller.
// - Deleting an AKS cluster.
// - Creating a Redis Enterprise Database.
// - Deleting a CosmosDB MongoDB.
// - Creating a Virtual Network Gateway Controller.
var OperationTimeoutRecording = 30 * time.Minute
func (tc *KubePerTestContext) DefaultOperationTimeout() time.Duration {
@ -521,13 +521,14 @@ func (tc *KubePerTestContext) deleteResourcesAndWait(objs ...client.Object) {
// Note that this protects against deleting resources that have a parent-child relationship in the same request. This is perfectly
// fine in the real world, but in many of our recording envtests we can get into a situation where there's a race
// during deletion that causes HTTP replay issues. The sequence of events is:
// 1. Delete parent resource and child resource at the same time.
// 2. During recording, child deletion never sees a successful (finished) DELETE request because parent is deleted so soon
// after the child that we just record a successful DELETE for the parent and don't bother sending the final child
// DELETE that would return success.
// 3. During replay, the race is how quickly the parent deletes and how many requests the child has a chance to send
// in that time. If the parent deletes slowly the child might try to send more requests than we actually have recorded
// (because none of them represent a terminal "actually deleted" state), which will cause a test failure.
// 1. Delete parent resource and child resource at the same time.
// 2. During recording, child deletion never sees a successful (finished) DELETE request because parent is deleted so soon
// after the child that we just record a successful DELETE for the parent and don't bother sending the final child
// DELETE that would return success.
// 3. During replay, the race is how quickly the parent deletes and how many requests the child has a chance to send
// in that time. If the parent deletes slowly the child might try to send more requests than we actually have recorded
// (because none of them represent a terminal "actually deleted" state), which will cause a test failure.
//
// In envtest it's still critical to delete everything, because ownership based deletion isn't enabled in envtest and we can't
// leave resources around or they will continue to attempt to log to a closed test logger. To avoid this we
// carefully delete resources starting with the root and working our way down one rank at a time. This shouldn't be much

Просмотреть файл

@ -71,9 +71,9 @@ var _ Calculator = &calculator{}
// 2. If syncPeriod is set, success results that would normally be terminal are instead configured to try again in syncPeriod.
// 3. If requeueDelayOverride is set, all happy-path requests have requeueDelayOverride set.
// The scenarios that this handler doesn't target:
// 1. Any error other than ReadyConditionImpacting error.
// 2. Happy-path requests when requeueDelayOverride is not set. These are scenarios where the operator is working
// as expected and we're just doing something like polling an async operation.
// 1. Any error other than ReadyConditionImpacting error.
// 2. Happy-path requests when requeueDelayOverride is not set. These are scenarios where the operator is working
// as expected and we're just doing something like polling an async operation.
func (i *calculator) NextInterval(req ctrl.Request, result ctrl.Result, err error) (ctrl.Result, error) {
i.failuresLock.Lock()
defer i.failuresLock.Unlock()

Просмотреть файл

@ -56,7 +56,7 @@ func Test_Success_WithSyncPeriod_ReturnsSyncPeriod(t *testing.T) {
t.Parallel()
g := NewGomegaWithT(t)
syncPeriod := 10 * time.Second
syncPeriod := 12 * time.Second
calc := newCalculator(
CalculatorParameters{
ErrorBaseDelay: 1 * time.Second,
@ -69,7 +69,10 @@ func Test_Success_WithSyncPeriod_ReturnsSyncPeriod(t *testing.T) {
result, err := calc.NextInterval(req, ctrl.Result{}, nil)
g.Expect(err).ToNot(HaveOccurred())
g.Expect(result.RequeueAfter > 8*time.Second).To(BeTrue())
// Threshold here needs to be at or below the lower-bound of the RequeuePeriod, taking Jitter into account
// Currently jitter is 0.25, so the 12s above becomes 9s to 15s
g.Expect(result.RequeueAfter >= 9*time.Second).To(BeTrue())
g.Expect(calc.(*calculator).failures).To(HaveLen(0))
}

Просмотреть файл

@ -154,10 +154,11 @@ func (c Condition) ShouldOverwrite(other Condition) bool {
// priority of the condition for overwrite purposes if Condition ObservedGeneration's are the same. Higher is more important.
// The result of this is the following:
// 1. Status == True conditions, and Status == False conditions with Severity == Warning or Error are all the highest priority.
// This means that the most recent update with any of those states will overwrite anything.
// 2. Status == False conditions with Severity == Info will only overwrite other Status == False conditions with Severity == Info.
// 3. Status == Unknown conditions will not overwrite anything.
// 1. Status == True conditions, and Status == False conditions with Severity == Warning or Error are all the highest priority.
// This means that the most recent update with any of those states will overwrite anything.
// 2. Status == False conditions with Severity == Info will only overwrite other Status == False conditions with Severity == Info.
// 3. Status == Unknown conditions will not overwrite anything.
//
// Keep in mind that this priority is specifically for comparing Conditions with the same ObservedGeneration. If the ObservedGeneration
// is different, the newer one always wins.
func (c Condition) priority() int {

Просмотреть файл

@ -19,24 +19,23 @@ import (
// referencing types from other packages. If we tried to use an interface with a single method, we'd inevitably end up
// with circular package references:
//
// +----------------+ +----------------+
// | v1 | | v2 |
// | PersonSpec | --- import v2 ---> | PersonSpec |
// | | | |
// | ConvertTo() | <--- import v1 --- | ConvertTo() |
// +----------------+ +----------------+
// +----------------+ +----------------+
// | v1 | | v2 |
// | PersonSpec | --- import v2 ---> | PersonSpec |
// | | | |
// | ConvertTo() | <--- import v1 --- | ConvertTo() |
// +----------------+ +----------------+
//
// Instead, we have to have support for both directions, so that we can always operate from one side of the package
// reference chain:
//
// +----------------+ +----------------+
// | v1 | | v2 |
// | PersonSpec | | PersonSpec |
// | | | |
// | ConvertTo() | --- import v2 ---> | |
// | ConvertFrom() | | |
// +----------------+ +----------------+
//
// +----------------+ +----------------+
// | v1 | | v2 |
// | PersonSpec | | PersonSpec |
// | | | |
// | ConvertTo() | --- import v2 ---> | |
// | ConvertFrom() | | |
// +----------------+ +----------------+
type ConvertibleSpec interface {
// ConvertSpecTo will populate the passed Spec by copying over all available information from this one
ConvertSpecTo(destination ConvertibleSpec) error

Просмотреть файл

@ -14,7 +14,6 @@ import (
// ConvertibleStatus is implemented by status types to allow conversion among the different versions of a given status
//
// Why do we need both directions of conversion? See ConvertibleSpec for details.
//
type ConvertibleStatus interface {
// ConvertStatusTo will populate the passed Status by copying over all available information from this one
ConvertStatusTo(destination ConvertibleStatus) error

Просмотреть файл

@ -309,8 +309,8 @@ func (builder *convertFromARMBuilder) operatorSpecPropertyHandler(
//
// If 'X' is a property that was flattened:
//
// k8sObj.Y1 = armObj.X.Y1;
// k8sObj.Y2 = armObj.X.Y2;
// k8sObj.Y1 = armObj.X.Y1;
// k8sObj.Y2 = armObj.X.Y2;
//
// in reality each assignment is likely to be another conversion that is specific
// to the type being converted.
@ -464,6 +464,7 @@ func (builder *convertFromARMBuilder) propertiesWithSameNameHandler(
// convertComplexTypeNameProperty handles conversion of complex TypeName properties.
// This function generates code that looks like this:
//
// <nameHint>Converted := <destinationType>{}
// err = <nameHint>Converted.FromARM(owner, <source>)
// if err != nil {

Просмотреть файл

@ -294,8 +294,8 @@ func (builder *convertToARMBuilder) referencePropertyHandler(
//
// If 'X' is a property that was flattened:
//
// armObj.X.Y1 = k8sObj.Y1;
// armObj.X.Y2 = k8sObj.Y2;
// armObj.X.Y1 = k8sObj.Y1;
// armObj.X.Y2 = k8sObj.Y2;
//
// in reality each assignment is likely to be another conversion that is specific
// to the type being converted.
@ -399,9 +399,10 @@ func (builder *convertToARMBuilder) flattenedPropertyHandler(
// that assigns it a value if any of the “from” properties are not nil.
//
// Resultant code looks like:
// if (from1 != nil) || (from2 != nil) || … {
// <resultIdent>.<toProp> = &<toPropTypeName>{}
// }
//
// if (from1 != nil) || (from2 != nil) || … {
// <resultIdent>.<toProp> = &<toPropTypeName>{}
// }
func (builder *convertToARMBuilder) buildToPropInitializer(
fromProps []*astmodel.PropertyDefinition,
toPropTypeName astmodel.TypeName,
@ -458,6 +459,7 @@ func (builder *convertToARMBuilder) propertiesWithSameNameHandler(
// convertReferenceProperty handles conversion of reference properties.
// This function generates code that looks like this:
//
// <namehint>ARMID, err := resolved.ResolvedReferences.Lookup(<source>)
// if err != nil {
// return nil, err
@ -493,6 +495,7 @@ func (builder *convertToARMBuilder) convertReferenceProperty(_ *astmodel.Convers
// convertSecretProperty handles conversion of secret properties.
// This function generates code that looks like this:
//
// <namehint>Secret, err := resolved.ResolvedSecrets.Lookup(<source>)
// if err != nil {
// return nil, errors.Wrap(err, "looking up secret for <source>")
@ -533,12 +536,12 @@ func (builder *convertToARMBuilder) convertSecretProperty(_ *astmodel.Conversion
// convertConfigMapProperty handles conversion of configMap properties.
// This function generates code that looks like this:
//
// <namehint>Value, err := resolved.ResolvedConfigMaps.Lookup(<source>)
// if err != nil {
// return nil, errors.Wrap(err, "looking up config map value for <source>")
// }
// <destination> = <namehint>Value
//
func (builder *convertToARMBuilder) convertConfigMapProperty(_ *astmodel.ConversionFunctionBuilder, params astmodel.ConversionParameters) []dst.Stmt {
isString := astmodel.TypeEquals(params.DestinationType, astmodel.StringType)
if !isString {
@ -574,7 +577,8 @@ func (builder *convertToARMBuilder) convertConfigMapProperty(_ *astmodel.Convers
// convertComplexTypeNameProperty handles conversion of complex TypeName properties.
// This function generates code that looks like this:
// <nameHint>, err := <source>.ToARM(name)
//
// <nameHint>, err := <source>.ToARM(name)
// if err != nil {
// return nil, err
// }

Просмотреть файл

@ -14,7 +14,7 @@ import (
// SimpleAssignment performs a simple assignment like:
//
// <lhs> = <rhs>
// <lhs> = <rhs>
//
// See also ShortDeclaration
func SimpleAssignment(lhs dst.Expr, rhs dst.Expr) *dst.AssignStmt {
@ -31,7 +31,7 @@ func SimpleAssignment(lhs dst.Expr, rhs dst.Expr) *dst.AssignStmt {
// ShortDeclaration performs a simple assignment like:
//
// <id> := <rhs>
// <id> := <rhs>
//
// Method naming inspired by https://tour.golang.org/basics/10
func ShortDeclaration(id string, rhs dst.Expr) *dst.AssignStmt {
@ -62,7 +62,9 @@ func AssignmentStatement(lhs dst.Expr, tok token.Token, rhs dst.Expr) *dst.Assig
}
// QualifiedAssignment performs a simple assignment like:
// <lhs>.<lhsSel> := <rhs> // tok = token.DEFINE
//
// <lhs>.<lhsSel> := <rhs> // tok = token.DEFINE
//
// or <lhs>.<lhsSel> = <rhs> // tok = token.ASSIGN
func QualifiedAssignment(lhs dst.Expr, lhsSel string, tok token.Token, rhs dst.Expr) *dst.AssignStmt {
return &dst.AssignStmt{
@ -73,8 +75,9 @@ func QualifiedAssignment(lhs dst.Expr, lhsSel string, tok token.Token, rhs dst.E
}
// SimpleAssignmentWithErr performs a simple assignment like:
// <lhs>, err := <rhs> // tok = token.DEFINE
// or <lhs>, err = <rhs> // tok = token.ASSIGN
//
// <lhs>, err := <rhs> // tok = token.DEFINE
// or <lhs>, err = <rhs> // tok = token.ASSIGN
func SimpleAssignmentWithErr(lhs dst.Expr, tok token.Token, rhs dst.Expr) *dst.AssignStmt {
errId := dst.NewIdent("err")
return &dst.AssignStmt{
@ -92,6 +95,7 @@ func SimpleAssignmentWithErr(lhs dst.Expr, tok token.Token, rhs dst.Expr) *dst.A
// AssignToInterface performs an assignment of a well-typed variable to an interface{}. This is usually used to
// perform a type assertion on a concrete type in a subsequent statement (which Go doesn't allow, it only allows type
// assertions on interface types).
//
// var <lhsVar> interface{} = <rhs>
func AssignToInterface(lhsVar string, rhs dst.Expr) *dst.DeclStmt {
return &dst.DeclStmt{

Просмотреть файл

@ -14,8 +14,8 @@ import (
// CheckErrorAndReturn checks if the err is non-nil, and if it is returns.
//
// if err != nil {
// return <otherReturns...>, err
// if err != nil {
// return <otherReturns...>, err
// }
func CheckErrorAndReturn(otherReturns ...dst.Expr) dst.Stmt {
retStmt := &dst.ReturnStmt{
@ -28,16 +28,15 @@ func CheckErrorAndReturn(otherReturns ...dst.Expr) dst.Stmt {
// CheckErrorAndWrap checks if the err is non-nil, and if it is returns it, wrapped with additional information
// If no arguments are provided, will generate
//
// if err != nil {
// return errors.Wrap(err, <message>)
// }
// if err != nil {
// return errors.Wrap(err, <message>)
// }
//
// otherwise will generate
//
// if err != nil {
// return errors.Wrapf(err, <message>, <args>)
// }
//
// if err != nil {
// return errors.Wrapf(err, <message>, <args>)
// }
func CheckErrorAndWrap(errorsPackage string, message string, args ...dst.Expr) dst.Stmt {
wrap := WrapError(errorsPackage, "err", message, args...)
return CheckErrorAndSingleStatement(Returns(wrap))
@ -45,8 +44,8 @@ func CheckErrorAndWrap(errorsPackage string, message string, args ...dst.Expr) d
// CheckErrorAndSingleStatement checks if the err is non-nil, and if it is executes the provided statement.
//
// if err != nil {
// <stmt>
// if err != nil {
// <stmt>
// }
func CheckErrorAndSingleStatement(stmt dst.Stmt) dst.Stmt {
return &dst.IfStmt{
@ -82,10 +81,12 @@ func WrapError(errorsPackage string, err string, message string, args ...dst.Exp
// with its default value.
//
// For example:
// var <varName> <packageRef>.<structName>
//
// var <varName> <packageRef>.<structName>
//
// Note that it does *not* do:
// <varName> := <packageRef>.<structName>{}
//
// <varName> := <packageRef>.<structName>{}
//
// …as that does not work for enum types.
func NewVariableQualified(varName string, qualifier string, structName string) dst.Stmt {
@ -109,10 +110,12 @@ func NewVariableQualified(varName string, qualifier string, structName string) d
// with its default value.
//
// For example:
// var <varName> <structName>
//
// var <varName> <structName>
//
// Note that it does *not* do:
// <varName> := <structName>{}
//
// <varName> := <structName>{}
//
// …as that does not work for enum types.
func NewVariable(varName string, structName string) dst.Stmt {
@ -135,8 +138,7 @@ func NewVariableWithType(varName string, varType dst.Expr) dst.Stmt {
// LocalVariableDeclaration performs a local variable declaration for use within a method
//
// var <ident> <typ>
//
// var <ident> <typ>
func LocalVariableDeclaration(ident string, typ dst.Expr, comment string) *dst.DeclStmt {
return &dst.DeclStmt{
Decl: VariableDeclaration(ident, typ, comment),
@ -145,8 +147,8 @@ func LocalVariableDeclaration(ident string, typ dst.Expr, comment string) *dst.D
// VariableDeclaration performs a global variable declaration
//
// // <comment>
// var <ident> <typ>
// // <comment>
// var <ident> <typ>
//
// For a LocalVariable within a method, use LocalVariableDeclaration() to create an ast.Stmt instead
func VariableDeclaration(ident string, typ dst.Expr, comment string) *dst.GenDecl {
@ -169,8 +171,7 @@ func VariableDeclaration(ident string, typ dst.Expr, comment string) *dst.GenDec
// TypeAssert returns an assignment statement with a type assertion
//
// <lhs>, ok := <rhs>.(<type>)
//
// <lhs>, ok := <rhs>.(<type>)
func TypeAssert(lhs dst.Expr, rhs dst.Expr, typ dst.Expr) *dst.AssignStmt {
return &dst.AssignStmt{
Lhs: []dst.Expr{
@ -192,7 +193,6 @@ func TypeAssert(lhs dst.Expr, rhs dst.Expr, typ dst.Expr) *dst.AssignStmt {
// if ok {
// return <returns>
// }
//
func ReturnIfOk(returns ...dst.Expr) *dst.IfStmt {
return ReturnIfExpr(dst.NewIdent("ok"), returns...)
}
@ -202,7 +202,6 @@ func ReturnIfOk(returns ...dst.Expr) *dst.IfStmt {
// if !ok {
// return <returns>
// }
//
func ReturnIfNotOk(returns ...dst.Expr) *dst.IfStmt {
return ReturnIfExpr(
&dst.UnaryExpr{
@ -214,10 +213,9 @@ func ReturnIfNotOk(returns ...dst.Expr) *dst.IfStmt {
// ReturnIfNil checks if a variable is nil and if it is returns
//
// if <toCheck> == nil {
// return <returns...>
// if <toCheck> == nil {
// return <returns...>
// }
//
func ReturnIfNil(toCheck dst.Expr, returns ...dst.Expr) dst.Stmt {
return ReturnIfExpr(
AreEqual(toCheck, Nil()),
@ -226,10 +224,9 @@ func ReturnIfNil(toCheck dst.Expr, returns ...dst.Expr) dst.Stmt {
// ReturnIfNotNil checks if a variable is not nil and if it is returns
//
// if <toCheck> != nil {
// return <returns...>
// if <toCheck> != nil {
// return <returns...>
// }
//
func ReturnIfNotNil(toCheck dst.Expr, returns ...dst.Expr) dst.Stmt {
return ReturnIfExpr(
AreNotEqual(toCheck, Nil()),
@ -239,9 +236,8 @@ func ReturnIfNotNil(toCheck dst.Expr, returns ...dst.Expr) dst.Stmt {
// ReturnIfExpr returns if the expression evaluates as true
//
// if <cond> {
// return <returns...>
// return <returns...>
// }
//
func ReturnIfExpr(cond dst.Expr, returns ...dst.Expr) *dst.IfStmt {
if len(returns) == 0 {
panic("Expected at least 1 return for ReturnIfOk")
@ -262,7 +258,6 @@ func ReturnIfExpr(cond dst.Expr, returns ...dst.Expr) *dst.IfStmt {
// FormatError produces a call to fmt.Errorf with the given format string and args
//
// fmt.Errorf(<formatString>, <args>)
//
func FormatError(fmtPackage string, formatString string, args ...dst.Expr) dst.Expr {
var callArgs []dst.Expr
callArgs = append(
@ -275,7 +270,6 @@ func FormatError(fmtPackage string, formatString string, args ...dst.Expr) dst.E
// AddrOf returns a statement that gets the address of the provided expression.
//
// &<expr>
//
func AddrOf(expr dst.Expr) *dst.UnaryExpr {
return &dst.UnaryExpr{
Op: token.AND,
@ -299,7 +293,6 @@ func AsReference(expr dst.Expr) dst.Expr {
// Dereference returns a statement that dereferences the pointer returned by the provided expression
//
// *<expr>
//
func Dereference(expr dst.Expr) dst.Expr {
return &dst.StarExpr{
X: dst.Clone(expr).(dst.Expr),
@ -308,9 +301,9 @@ func Dereference(expr dst.Expr) dst.Expr {
// Returns creates a return statement with one or more expressions, of the form
//
// return <expr>
// or return <expr>, <expr>, ...
// return <expr>
//
// or return <expr>, <expr>, ...
func Returns(returns ...dst.Expr) dst.Stmt {
return &dst.ReturnStmt{
Decs: dst.ReturnStmtDecorations{
@ -324,9 +317,8 @@ func Returns(returns ...dst.Expr) dst.Stmt {
// ReturnNoError creates a return nil statement for when no error occurs
//
// // No error
// return nil
//
// // No error
// return nil
func ReturnNoError() dst.Stmt {
result := Returns(Nil())
result.Decorations().Before = dst.EmptyLine
@ -350,7 +342,6 @@ func WrappedErrorf(errorsPackage string, template string, args ...interface{}) d
// QualifiedTypeName generates a reference to a type within an imported package
//
// <pkg>.<name>
//
func QualifiedTypeName(pkg string, name string) *dst.SelectorExpr {
return &dst.SelectorExpr{
X: dst.NewIdent(pkg),
@ -361,7 +352,6 @@ func QualifiedTypeName(pkg string, name string) *dst.SelectorExpr {
// Selector generates a field reference into an existing expression
//
// <expr>.<name0>.(<name1>.<name2>…)
//
func Selector(expr dst.Expr, names ...string) *dst.SelectorExpr {
exprs := []dst.Expr{dst.Clone(expr).(dst.Expr)}
for _, name := range names {
@ -379,7 +369,6 @@ func Selector(expr dst.Expr, names ...string) *dst.SelectorExpr {
// AreEqual generates a == comparison between the two expressions
//
// <lhs> == <rhs>
//
func AreEqual(lhs dst.Expr, rhs dst.Expr) *dst.BinaryExpr {
return BinaryExpr(lhs, token.EQL, rhs)
}
@ -387,7 +376,6 @@ func AreEqual(lhs dst.Expr, rhs dst.Expr) *dst.BinaryExpr {
// AreNotEqual generates a != comparison between the two expressions
//
// <lhs> != <rhs>
//
func AreNotEqual(lhs dst.Expr, rhs dst.Expr) *dst.BinaryExpr {
return BinaryExpr(lhs, token.NEQ, rhs)
}

Просмотреть файл

@ -12,7 +12,6 @@ import (
// CallFunc creates an expression to call a function with specified arguments
//
// <funcName>(<arguments>...)
//
func CallFunc(funcName string, arguments ...dst.Expr) *dst.CallExpr {
return createCallExpr(dst.NewIdent(funcName), arguments...)
}
@ -21,7 +20,6 @@ func CallFunc(funcName string, arguments ...dst.Expr) *dst.CallExpr {
// arguments
//
// <qualifier>.<funcName>(arguments...)
//
func CallQualifiedFunc(qualifier string, funcName string, arguments ...dst.Expr) *dst.CallExpr {
return createCallExpr(
&dst.SelectorExpr{
@ -34,7 +32,6 @@ func CallQualifiedFunc(qualifier string, funcName string, arguments ...dst.Expr)
// CallExpr creates an expression to call the named function with the specified arguments
//
// <expr>.<funcName>(arguments...)
//
func CallExpr(expr dst.Expr, funcName string, arguments ...dst.Expr) *dst.CallExpr {
var receiver dst.Expr = expr
if star, ok := expr.(*dst.StarExpr); ok {

Просмотреть файл

@ -13,10 +13,9 @@ import (
// SimpleIf creates a simple if statement with multiple statements
//
// if <condition> {
// <trueBranch>
// }
//
// if <condition> {
// <trueBranch>
// }
func SimpleIf(condition dst.Expr, statements ...dst.Stmt) *dst.IfStmt {
return &dst.IfStmt{
Cond: condition,
@ -26,12 +25,12 @@ func SimpleIf(condition dst.Expr, statements ...dst.Stmt) *dst.IfStmt {
// SimpleIfElse creates a simple if else statement. Each branch may contain multiple statements.
//
// if <condition> {
// <trueBranch>
// } else {
// <falseBranch>
// }
// if <condition> {
// <trueBranch>
// } else {
//
// <falseBranch>
// }
func SimpleIfElse(condition dst.Expr, trueBranch []dst.Stmt, falseBranch []dst.Stmt) *dst.IfStmt {
result := &dst.IfStmt{
Cond: condition,
@ -44,10 +43,9 @@ func SimpleIfElse(condition dst.Expr, trueBranch []dst.Stmt, falseBranch []dst.S
// IfEqual executes a series of statements if the supplied expressions are
//
// if <left> == <right> {
// <statements>
// }
//
// if <left> == <right> {
// <statements>
// }
func IfEqual(left dst.Expr, right dst.Expr, statements ...dst.Stmt) *dst.IfStmt {
return &dst.IfStmt{
Cond: AreEqual(left, right),
@ -57,10 +55,9 @@ func IfEqual(left dst.Expr, right dst.Expr, statements ...dst.Stmt) *dst.IfStmt
// IfNotNil executes a series of statements if the supplied expression is not nil
//
// if <source> != nil {
// <statements>
// }
//
// if <source> != nil {
// <statements>
// }
func IfNotNil(toCheck dst.Expr, statements ...dst.Stmt) *dst.IfStmt {
return &dst.IfStmt{
Cond: NotNil(toCheck),
@ -70,10 +67,9 @@ func IfNotNil(toCheck dst.Expr, statements ...dst.Stmt) *dst.IfStmt {
// IfNil executes a series of statements if the supplied expression is nil
//
// if <source> != nil {
// <statements>
// }
//
// if <source> != nil {
// <statements>
// }
func IfNil(toCheck dst.Expr, statements ...dst.Stmt) *dst.IfStmt {
return &dst.IfStmt{
Cond: AreEqual(toCheck, Nil()),
@ -86,7 +82,6 @@ func IfNil(toCheck dst.Expr, statements ...dst.Stmt) *dst.IfStmt {
// if ok {
// <statements>
// }
//
func IfOk(statements ...dst.Stmt) *dst.IfStmt {
return &dst.IfStmt{
Cond: dst.NewIdent("ok"),
@ -99,7 +94,6 @@ func IfOk(statements ...dst.Stmt) *dst.IfStmt {
// if !ok {
// <statements>
// }
//
func IfNotOk(statements ...dst.Stmt) *dst.IfStmt {
return &dst.IfStmt{
Cond: &dst.UnaryExpr{
@ -116,10 +110,9 @@ func IfNotOk(statements ...dst.Stmt) *dst.IfStmt {
// local is the name of the local variable to initialize
// statements form the body of the if statement
//
// if <local>, ok := <expr>.(<typeExpr>); ok {
// <statements>
// }
//
// if <local>, ok := <expr>.(<typeExpr>); ok {
// <statements>
// }
func IfType(expr dst.Expr, typeExpr dst.Expr, local string, statements ...dst.Stmt) *dst.IfStmt {
return &dst.IfStmt{
Init: TypeAssert(dst.NewIdent(local), expr, typeExpr),

Просмотреть файл

@ -51,9 +51,10 @@ func NewTestFuncDetails(testingPackage string, testName string, body ...dst.Stmt
}
// DefineFunc defines a function (header, body, etc), like:
// <comment>
//
// <comment>
// func (<receiverIdent> <receiverType>) <name>(<params...>) (<returns...>) {
// <body...>
// <body...>
// }
func (fn *FuncDetails) DefineFunc() *dst.FuncDecl {

Просмотреть файл

@ -13,8 +13,7 @@ import (
// MakeMap returns the call expression for making a map
//
// make(map[<key>]<value>)
//
// make(map[<key>]<value>)
func MakeMap(key dst.Expr, value dst.Expr) *dst.CallExpr {
return &dst.CallExpr{
Fun: dst.NewIdent("make"),
@ -29,8 +28,7 @@ func MakeMap(key dst.Expr, value dst.Expr) *dst.CallExpr {
// MakeMapWithCapacity returns the call expression for making a map with a predefined capacity
//
// make(map[<key>]<value>, <capacity>)
//
// make(map[<key>]<value>, <capacity>)
func MakeMapWithCapacity(key dst.Expr, value dst.Expr, capacity dst.Expr) *dst.CallExpr {
return &dst.CallExpr{
Fun: dst.NewIdent("make"),
@ -46,8 +44,7 @@ func MakeMapWithCapacity(key dst.Expr, value dst.Expr, capacity dst.Expr) *dst.C
// InsertMap returns an assignment statement for inserting an item into a map
//
// <mapExpr>[<key>] = <rhs>
//
// <mapExpr>[<key>] = <rhs>
func InsertMap(mapExpr dst.Expr, key dst.Expr, rhs dst.Expr) *dst.AssignStmt {
return SimpleAssignment(
&dst.IndexExpr{
@ -60,10 +57,9 @@ func InsertMap(mapExpr dst.Expr, key dst.Expr, rhs dst.Expr) *dst.AssignStmt {
// IterateOverMapWithValue creates a statement to iterate over the content of a map using the
// specified identifiers for each key and value found.
//
// for <key>, <item> := range <mapExpr> {
// <statements>
// }
//
// for <key>, <item> := range <mapExpr> {
// <statements>
// }
func IterateOverMapWithValue(key string, item string, mapExpr dst.Expr, statements ...dst.Stmt) *dst.RangeStmt {
return &dst.RangeStmt{
Key: dst.NewIdent(key),

Просмотреть файл

@ -14,7 +14,6 @@ import (
// MakeSlice returns the call expression for making a slice
//
// make([]<value>)
//
func MakeSlice(listType dst.Expr, len dst.Expr) *dst.CallExpr {
return &dst.CallExpr{
Fun: dst.NewIdent("make"),
@ -28,7 +27,6 @@ func MakeSlice(listType dst.Expr, len dst.Expr) *dst.CallExpr {
// AppendItemToSlice returns a statement to append a single item to a slice
//
// <lhs> = append(<lhs>, <rhs>)
//
func AppendItemToSlice(lhs dst.Expr, rhs dst.Expr) dst.Stmt {
return SimpleAssignment(
dst.Clone(lhs).(dst.Expr),
@ -38,7 +36,6 @@ func AppendItemToSlice(lhs dst.Expr, rhs dst.Expr) dst.Stmt {
// AppendItemsToSlice returns a statement to append many individual items to a slice
//
// <lhs> = append(<lhs>, <rhs>, <rhs>, <rhs>, ...)
//
func AppendItemsToSlice(lhs dst.Expr, rhs ...dst.Expr) dst.Stmt {
args := make([]dst.Expr, 0, len(rhs)+1)
args = append(args, lhs)
@ -54,7 +51,6 @@ func AppendItemsToSlice(lhs dst.Expr, rhs ...dst.Expr) dst.Stmt {
// AppendSliceToSlice returns a statement to append a slice to another slice
//
// <lhs> = append(<lhs>, <rhs>...)
//
func AppendSliceToSlice(lhs dst.Expr, rhs dst.Expr) dst.Stmt {
f := CallFunc("append", dst.Clone(lhs).(dst.Expr), dst.Clone(rhs).(dst.Expr))
f.Ellipsis = true
@ -66,10 +62,9 @@ func AppendSliceToSlice(lhs dst.Expr, rhs dst.Expr) dst.Stmt {
// IterateOverSlice creates a statement to iterate over the content of a list using the specified
// identifier for each element in the list
//
// for _, <item> := range <list> {
// <statements>
// }
//
// for _, <item> := range <list> {
// <statements>
// }
func IterateOverSlice(item string, list dst.Expr, statements ...dst.Stmt) *dst.RangeStmt {
return &dst.RangeStmt{
Key: dst.NewIdent("_"),
@ -83,10 +78,9 @@ func IterateOverSlice(item string, list dst.Expr, statements ...dst.Stmt) *dst.R
// IterateOverSliceWithIndex creates a statement to iterate over the content of a list using the specified
// identifiers for each index and element in the list
//
// for <index>, <item> := range <list> {
// <statements>
// }
//
// for <index>, <item> := range <list> {
// <statements>
// }
func IterateOverSliceWithIndex(index string, item string, list dst.Expr, statements ...dst.Stmt) *dst.RangeStmt {
return &dst.RangeStmt{
Key: dst.NewIdent(index),

Просмотреть файл

@ -176,7 +176,8 @@ func (builder *ConversionFunctionBuilder) BuildConversion(params ConversionParam
// IdentityConvertComplexOptionalProperty handles conversion for optional properties with complex elements
// This function generates code that looks like this:
// if <source> != nil {
//
// if <source> != nil {
// <code for producing result from destinationType.Element()>
// <destination> = &<result>
// }
@ -222,7 +223,8 @@ func IdentityConvertComplexOptionalProperty(builder *ConversionFunctionBuilder,
// IdentityConvertComplexArrayProperty handles conversion for array properties with complex elements
// This function generates code that looks like this:
// for _, item := range <source> {
//
// for _, item := range <source> {
// <code for producing result from destinationType.Element()>
// <destination> = append(<destination>, <result>)
// }
@ -293,10 +295,11 @@ func IdentityConvertComplexArrayProperty(builder *ConversionFunctionBuilder, par
// IdentityConvertComplexMapProperty handles conversion for map properties with complex values.
// This function panics if the map keys are not primitive types.
// This function generates code that looks like this:
// if <source> != nil {
//
// if <source> != nil {
// <destination> = make(map[<destinationType.KeyType()]<destinationType.ValueType()>, len(<source>))
// for key, value := range <source> {
// <code for producing result from destinationType.ValueType()>
// <code for producing result from destinationType.ValueType()>
// <destination>[key] = <result>
// }
// }
@ -388,6 +391,7 @@ func IdentityConvertComplexMapProperty(builder *ConversionFunctionBuilder, param
// Note that because this handler is dealing with TypeName's and not Optional<TypeName>, it is safe to
// perform a simple assignment rather than a copy.
// This function generates code that looks like this:
//
// <destination> <assignmentHandler> <source>
func IdentityAssignTypeName(_ *ConversionFunctionBuilder, params ConversionParameters) []dst.Stmt {
destinationType, ok := params.DestinationType.(TypeName)
@ -474,15 +478,17 @@ func AssignToOptional(builder *ConversionFunctionBuilder, params ConversionParam
// AssignFromOptional assigns address of source to destination.
// This function generates code that looks like this, for simple conversions:
// if (<source> != nil) {
// <destination> <assignmentHandler> *<source>
// }
//
// if (<source> != nil) {
// <destination> <assignmentHandler> *<source>
// }
//
// or:
// if (<source> != nil) {
// <destination>Temp := convert(*<source>)
// <destination> <assignmentHandler> <destination>Temp
// }
//
// if (<source> != nil) {
// <destination>Temp := convert(*<source>)
// <destination> <assignmentHandler> <destination>Temp
// }
func AssignFromOptional(builder *ConversionFunctionBuilder, params ConversionParameters) []dst.Stmt {
optSrc, ok := params.SourceType.(*OptionalType)
if !ok {
@ -556,7 +562,8 @@ func IdentityAssignValidatedTypeSource(builder *ConversionFunctionBuilder, param
// IdentityDeepCopyJSON special cases copying JSON-type fields to call the DeepCopy method.
// It generates code that looks like:
// <destination> = *<source>.DeepCopy()
//
// <destination> = *<source>.DeepCopy()
func IdentityDeepCopyJSON(_ *ConversionFunctionBuilder, params ConversionParameters) []dst.Stmt {
if !TypeEquals(params.DestinationType, JSONType) {
return nil
@ -585,8 +592,9 @@ func AssignmentHandlerAssign(lhs dst.Expr, rhs dst.Expr) dst.Stmt {
// CreateLocal creates an unused local variable name.
// Names are chosen according to the following rules:
// 1. If there is no local variable with the <suffix> name, use that.
// 2. If there is a local variable with the <suffix> name, create a variable name <nameHint><suffix>.
// 1. If there is no local variable with the <suffix> name, use that.
// 2. If there is a local variable with the <suffix> name, create a variable name <nameHint><suffix>.
//
// In the case that <nameHint><suffix> is also taken append numbers to the end in standard KnownLocalsSet fashion.
// Note that this function trims numbers on the right hand side of nameHint, so a nameHint of "item1" will get a local
// variable named item<suffix>.

Просмотреть файл

@ -329,11 +329,11 @@ func simplifyName(context string, name string) string {
// sliceIntoWords splits the provided identifier into a slice of individual words.
// A word is defined by one of the following:
// 1. A space ("a test" becomes "a" and "test")
// 2. A transition between lowercase and uppercase ("aWord" becomes "a" and "Word")
// 3. A transition between multiple uppercase letters and a lowercase letter ("XMLDocument" becomes "XML" and "Document")
// 4. A transition between a letter and a digit ("book12" becomes "book" and "12")
// 5. A transition between a digit and a letter ("12monkeys" becomes "12" and "monkeys")
// 1. A space ("a test" becomes "a" and "test")
// 2. A transition between lowercase and uppercase ("aWord" becomes "a" and "Word")
// 3. A transition between multiple uppercase letters and a lowercase letter ("XMLDocument" becomes "XML" and "Document")
// 4. A transition between a letter and a digit ("book12" becomes "book" and "12")
// 5. A transition between a digit and a letter ("12monkeys" becomes "12" and "monkeys")
func sliceIntoWords(identifier string) []string {
// Trim any leading and trailing spaces to make our life easier later
identifier = strings.Trim(identifier, " ")

Просмотреть файл

@ -15,7 +15,7 @@ import (
//
// Conceptually it takes (via Add) a list of functions of the form:
//
// func ([ctx interface{},] left {some Type}, right {some Type}) (Type, error)
// func ([ctx interface{},] left {some Type}, right {some Type}) (Type, error)
//
// where `left` and `right` can be concrete types that implement the `Type` interface.
//

Просмотреть файл

@ -76,7 +76,7 @@ func TestWriteDebugDescription(t *testing.T) {
}
// TestWriteDebugDescriptionNils ensures that WriteDebugDescription() doesn't panic even if called on a nil reference,
//(this is a diagnostic method that should pretty much always do something useful)
// (this is a diagnostic method that should pretty much always do something useful)
func TestWriteDebugDescriptionNils(t *testing.T) {
t.Parallel()
g := NewGomegaWithT(t)

Просмотреть файл

@ -28,7 +28,6 @@ import "fmt"
// VisitObjectType = func(this TypeVisitor, it *ObjectType, ctx interface{}) (Type, error) // Works
// VisitObjectType = func(it TypeName) Type // Fails - parameter is not an *ObjectType
// VisitObjectType = func(this TypeVisitor, it TypeName, ctx interface{}) (ObjectType, error) // Fails -return is not Type
//
type TypeVisitorBuilder struct {
VisitTypeName interface{}
VisitOneOfType interface{}

Просмотреть файл

@ -32,26 +32,27 @@ func (e resourceRemovalVisitorContext) WithMoreDepth() resourceRemovalVisitorCon
// EmbeddedResourceRemover uses a variety of heuristics to remove resources that are embedded inside other resources.
// There are a number of different kinds of embeddings:
// 1. A "Properties" embedding. When we process the Azure JSON schema/Swagger we manufacture a "Spec"
// type that doesn't exist in the JSON schema/Swagger. In the JSON schema the resource itself must comply with ARM
// resource requirements, meaning that all of the RP specific properties are stored in the "Properties"
// property which for the sake of example we will say has type "R1Properties".
// Other resources which have a property somewhere in their type hierarchy with that same "R1Properties"
// type are actually embedding the R1 resource entirely inside themselves. Since the R1 resource is its own
// resource it doesn't make sense to have it embedded inside another resource in Kubernetes. These embeddings
// should really just be cross resource references. This pipeline finds such embeddings and removes them. A concrete
// example of one such embedding is
// v20181001 Microsoft.Networking Connection.Spec.Properties.LocalNetworkGateway2.Properties.
// The LocalNetworkGateway2 property is of type "LocalNetworkGateway" which is itself a resource.
// The ideal shape of Connection.Spec.Properties.LocalNetworkGate2 would just be a reference to a
// LocalNetworkGateway resource.
// 2. A subresource embedding. For the same reasons above, embedded subresources don't make sense in Kubernetes.
// In the case of embedded subresources, the ideal shape would be a complete removal of the reference. We forbid
// parent resources directly referencing child resources as it complicates the Watches scenario for each resource
// reconciler. It's also not a common pattern in Kubernetes - usually you can identify children for a
// given parent via a label. An example of this type of embedding is
// v20180601 Microsoft.Networking RouteTable.Spec.Properties.Routes. The Routes property is of type RouteTableRoutes
// which is a child resource of RouteTable.
// 1. A "Properties" embedding. When we process the Azure JSON schema/Swagger we manufacture a "Spec"
// type that doesn't exist in the JSON schema/Swagger. In the JSON schema the resource itself must comply with ARM
// resource requirements, meaning that all of the RP specific properties are stored in the "Properties"
// property which for the sake of example we will say has type "R1Properties".
// Other resources which have a property somewhere in their type hierarchy with that same "R1Properties"
// type are actually embedding the R1 resource entirely inside themselves. Since the R1 resource is its own
// resource it doesn't make sense to have it embedded inside another resource in Kubernetes. These embeddings
// should really just be cross resource references. This pipeline finds such embeddings and removes them. A concrete
// example of one such embedding is
// v20181001 Microsoft.Networking Connection.Spec.Properties.LocalNetworkGateway2.Properties.
// The LocalNetworkGateway2 property is of type "LocalNetworkGateway" which is itself a resource.
// The ideal shape of Connection.Spec.Properties.LocalNetworkGate2 would just be a reference to a
// LocalNetworkGateway resource.
// 2. A subresource embedding. For the same reasons above, embedded subresources don't make sense in Kubernetes.
// In the case of embedded subresources, the ideal shape would be a complete removal of the reference. We forbid
// parent resources directly referencing child resources as it complicates the Watches scenario for each resource
// reconciler. It's also not a common pattern in Kubernetes - usually you can identify children for a
// given parent via a label. An example of this type of embedding is
// v20180601 Microsoft.Networking RouteTable.Spec.Properties.Routes. The Routes property is of type RouteTableRoutes
// which is a child resource of RouteTable.
//
// Note that even though the above examples do not include Status definitions, the same rules apply to Status definitions, with
// the only difference being that for Status definitions the resource reference in Swagger (the source of the Status definitions)
// is to the Status type (as opposed to the "Properties" type for Spec).

Просмотреть файл

@ -196,20 +196,21 @@ func validateConfigMapDestinations(k *functions.ResourceFunction, codeGeneration
}
// validateOperatorSpecSliceBody helps generate the body of the validateResourceReferences function:
// func (account *DatabaseAccount) validateConfigMapDestinations() error {
// if <receiver>.Spec.OperatorSpec == nil {
// return nil
// }
// if <receiver>.Spec.OperatorSpec.<operatorSpecProperty> == nil {
// return nil
// }
// toValidate := []*<validateType>{
// account.Spec.OperatorSpec.ConfigMaps.ClientId,
// account.Spec.OperatorSpec.ConfigMaps.PrincipalId,
// ...
// }
// return genruntime.<validateFunctionName>(toValidate)
// }
//
// func (account *DatabaseAccount) validateConfigMapDestinations() error {
// if <receiver>.Spec.OperatorSpec == nil {
// return nil
// }
// if <receiver>.Spec.OperatorSpec.<operatorSpecProperty> == nil {
// return nil
// }
// toValidate := []*<validateType>{
// account.Spec.OperatorSpec.ConfigMaps.ClientId,
// account.Spec.OperatorSpec.ConfigMaps.PrincipalId,
// ...
// }
// return genruntime.<validateFunctionName>(toValidate)
// }
func validateOperatorSpecSliceBody(
codeGenerationContext *astmodel.CodeGenerationContext,
resource *astmodel.ResourceType,

Просмотреть файл

@ -70,9 +70,9 @@ func nestSpecIntoForProvider(
// nestType nests the contents of the provided outerType into a property with the given nestedPropertyName whose
// type is the given nestedTypeName. The result is a type that looks something like the following:
//
// type <outerTypeName> struct {
// <nestedPropertyName> <nestedTypeName> `yaml:"<nestedPropertyName>"`
// }
// type <outerTypeName> struct {
// <nestedPropertyName> <nestedTypeName> `yaml:"<nestedPropertyName>"`
// }
func nestType(
idFactory astmodel.IdentifierFactory,
definitions astmodel.TypeDefinitionSet,

Просмотреть файл

@ -46,7 +46,6 @@ const DetectSkippingPropertiesStageID = "detectSkippingProperties"
// Additional complexities:
// - We need to handle type renaming between versions.
// - When introduced, we will also need to handle property renaming between versions
//
func DetectSkippingProperties() *Stage {
return NewStage(
DetectSkippingPropertiesStageID,

Просмотреть файл

@ -101,7 +101,6 @@ func removeFlatten(t astmodel.Type) astmodel.Type {
// We will flatten it and rename the second property, resulting in:
// - `Type`
// - `PropertiesType`
//
func fixCollisions(props []*astmodel.PropertyDefinition) []*astmodel.PropertyDefinition {
names := make(map[astmodel.PropertyName]int)

Просмотреть файл

@ -22,13 +22,14 @@ const RemoveStatusPropertyValidationsStageID = "removeStatusPropertyValidation"
// This is required because Status is retrieved directly from the ARM API, and there are
// cases where ARM might return something that isn't actually "valid" according to the validation,
// but makes sense in context. Some examples:
// 1. Status has a modelAsString enum with 2 values, but in a future API version, a 3rd value is added.
// The fact that the enum is modelAsString allows the service to return the new 3rd value even in old API
// versions.
// 2. Status has an int that must be between 10 and 20. In a future API version, that restriction is relaxed and
// the same int can now be between 0 and 50.
// 3. A bug in the services Swagger specification causes the service to accept enums with any case, but always
// return the enum all uppercase
// 1. Status has a modelAsString enum with 2 values, but in a future API version, a 3rd value is added.
// The fact that the enum is modelAsString allows the service to return the new 3rd value even in old API
// versions.
// 2. Status has an int that must be between 10 and 20. In a future API version, that restriction is relaxed and
// the same int can now be between 0 and 50.
// 3. A bug in the services Swagger specification causes the service to accept enums with any case, but always
// return the enum all uppercase
//
// In the above cases, if we left validation on the Status types, we would be unable to persist the content
// returned by the service (apiserver will reject it as not matching the OpenAPI schema). This could be a problem
// in cases where the resource was created via some other means and then imported into

Просмотреть файл

@ -175,14 +175,15 @@ func orderByFunctionName(functions []*functions.IndexRegistrationFunction) func(
}
// createGetKnownTypesFunc creates a getKnownTypes function that returns all known types:
// func getKnownTypes() []client.Object {
// var result []client.Object
// result = append(result, new(<package>.<resource>))
// result = append(result, new(<package>.<resource>))
// result = append(result, new(<package>.<resource>))
// ...
// return result
// }
//
// func getKnownTypes() []client.Object {
// var result []client.Object
// result = append(result, new(<package>.<resource>))
// result = append(result, new(<package>.<resource>))
// result = append(result, new(<package>.<resource>))
// ...
// return result
// }
func createGetKnownTypesFunc(codeGenerationContext *astmodel.CodeGenerationContext, resources []astmodel.TypeName) (dst.Decl, error) {
funcName := "getKnownTypes"
funcComment := "returns the list of all types."
@ -236,27 +237,28 @@ func createGetKnownTypesFunc(codeGenerationContext *astmodel.CodeGenerationConte
}
// createGetKnownStorageTypesFunc creates a getKnownStorageTypes function that returns all storage types:
// func getKnownStorageTypes() []registration.StorageType {
// var result []*registration.StorageType
// result = append(result, &registration.StorageType{
// Obj: new(<package>.<resource>),
// Indexes: []registration.Index{
// {
// Key: <key>,
// Func: <func>,
// },
//
// func getKnownStorageTypes() []registration.StorageType {
// var result []*registration.StorageType
// result = append(result, &registration.StorageType{
// Obj: new(<package>.<resource>),
// Indexes: []registration.Index{
// {
// Key: <key>,
// Func: <func>,
// },
// Watches: []registration.Watch{
// {
// Src: <source> (usually corev1.Secret{}),
// },
// },
// Watches: []registration.Watch{
// {
// Src: <source> (usually corev1.Secret{}),
// },
// })
// result = append(result, registration.StorageType{Obj: new(<package>.<resource>)})
// result = append(result, registration.StorageType{Obj: new(<package>.<resource>)})
// ...
// return result
// }
// },
// })
// result = append(result, registration.StorageType{Obj: new(<package>.<resource>)})
// result = append(result, registration.StorageType{Obj: new(<package>.<resource>)})
// ...
// return result
// }
func (r *ResourceRegistrationFile) createGetKnownStorageTypesFunc(
codeGenerationContext *astmodel.CodeGenerationContext,
) dst.Decl {
@ -375,14 +377,15 @@ func (r *ResourceRegistrationFile) createGetResourceExtensions(context *astmodel
}
// createCreateSchemeFunc creates a createScheme() function like:
// func createScheme() *runtime.Scheme {
// scheme := runtime.NewScheme()
// _ = clientgoscheme.AddToScheme(scheme)
// _ = batchv20170901.AddToScheme(scheme)
// _ = documentdbv20150408.AddToScheme(scheme)
// _ = storagev20190401.AddToScheme(scheme)
// return scheme
// }
//
// func createScheme() *runtime.Scheme {
// scheme := runtime.NewScheme()
// _ = clientgoscheme.AddToScheme(scheme)
// _ = batchv20170901.AddToScheme(scheme)
// _ = documentdbv20150408.AddToScheme(scheme)
// _ = storagev20190401.AddToScheme(scheme)
// return scheme
// }
func (r *ResourceRegistrationFile) createCreateSchemeFunc(codeGenerationContext *astmodel.CodeGenerationContext) (dst.Decl, error) {
runtime, err := codeGenerationContext.GetImportedPackageName(astmodel.APIMachineryRuntimeReference)
if err != nil {
@ -500,6 +503,7 @@ func (r *ResourceRegistrationFile) makeWatchesExpr(typeName astmodel.TypeName, c
}
// makeSimpleWatchesExpr generates code for a Watches expression:
//
// {
// Src: &source.Kind{Type: &<fieldType>{}},
// MakeEventHandler: <watchHelperFuncName>([]string{<typeNameKeys[typeName]>}, &<typeName>{}),

Просмотреть файл

@ -146,7 +146,6 @@ func (stage *Stage) RequiresPrerequisiteStages(prerequisites ...string) {
// Post-requisites are thus not completely isomorphic with RequiresPrerequisiteStages as there may be supporting stages that are
// sometimes omitted from execution when targeting different outcomes. Having both pre- and post-requisites allows the
// dependencies to drop out cleanly when different stages are present.
//
func (stage *Stage) RequiresPostrequisiteStages(postrequisites ...string) {
if len(stage.postrequisites) > 0 {
panic(fmt.Sprintf(

Просмотреть файл

@ -28,7 +28,8 @@ import (
"github.com/Azure/azure-service-operator/v2/tools/generator/internal/jsonast"
)
/* addStatusFromSwagger creates a PipelineStage to add status information into the generated resources.
/*
addStatusFromSwagger creates a PipelineStage to add status information into the generated resources.
This information is derived from the Azure Swagger specifications. We parse the Swagger specs and look for
any actions that appear to be ARM resources (have PUT methods with types we can use and appropriate names in the
@ -39,7 +40,6 @@ Next, we walk over all the resources we are currently generating CRDs for and at
a match for the resource in the status information we have parsed. If we locate a match, it is
added to the Status field of the Resource type, after we have renamed all the status types to
avoid any conflicts with existing Spec types that have already been defined.
*/
func AddStatusFromSwagger(idFactory astmodel.IdentifierFactory, config *config.Configuration) *Stage {
return NewLegacyStage(

Просмотреть файл

@ -18,22 +18,28 @@ import (
const UnrollRecursiveTypesStageID = "unrollRecursiveTypes"
// UnrollRecursiveTypes finds types that reference themselves and "unrolls" the reference. So a type that looks like:
// type Error struct {
// code string
// message string
// errors []Error
// }
//
// type Error struct {
// code string
// message string
// errors []Error
// }
//
// gets unrolled to look like:
// type Error struct {
// code string
// message string
// errors []Error_Unrolled
// }
//
// type Error struct {
// code string
// message string
// errors []Error_Unrolled
// }
//
// where Error_Unrolled looks like:
// type Error_Unrolled struct {
// code string
// message string
// }
//
// type Error_Unrolled struct {
// code string
// message string
// }
//
// The recursive references must be removed because
// controller-tools doesn't support generating "references" (JSON $ref) so it can't support recursive types today.
// See https://github.com/kubernetes-sigs/controller-tools/issues/489 for more information.

Просмотреть файл

@ -116,7 +116,6 @@ func (graph *ConversionGraph) TransitionCount() int {
// declaringType is the type containing the property.
// property is the name of the property.
// definitions is a set of known definitions.
//
func (graph *ConversionGraph) FindNextProperty(
ref astmodel.PropertyReference,
definitions astmodel.TypeDefinitionSet) (astmodel.PropertyReference, error) {

Просмотреть файл

@ -80,11 +80,12 @@ func (p *PropertyConverter) useBaseTypeForEnumerations(
}
// shortCircuitNamesOfSimpleTypes redirects or replaces TypeNames
// o If a TypeName points into an API package, it is redirected into the appropriate storage package
// o If a TypeName references an enumeration, it is replaced with the underlying type of the enumeration as our
// storage definitions don't use enumerations, they use primitive definitions
// o If a TypeName references an alias for a primitive type (these are used to specify validations), it is replace
// with the primitive type
//
// o If a TypeName points into an API package, it is redirected into the appropriate storage package
// o If a TypeName references an enumeration, it is replaced with the underlying type of the enumeration as our
// storage definitions don't use enumerations, they use primitive definitions
// o If a TypeName references an alias for a primitive type (these are used to specify validations), it is replace
// with the primitive type
func (p *PropertyConverter) shortCircuitNamesOfSimpleTypes(
tv *astmodel.TypeVisitor, tn astmodel.TypeName, ctx interface{}) (astmodel.Type, error) {

Просмотреть файл

@ -26,7 +26,6 @@ import (
// │ ObjectModelConfiguration │───────║ GroupConfiguration ║───────│ VersionConfiguration │───────│ TypeConfiguration │───────│ PropertyConfiguration │
// │ │1 1..n║ ║1 1..n│ │1 1..n│ │1 1..n│ │
// └──────────────────────────┘ ╚════════════════════╝ └──────────────────────┘ └───────────────────┘ └───────────────────────┘
//
type GroupConfiguration struct {
name string
versions map[string]*VersionConfiguration

Просмотреть файл

@ -24,7 +24,6 @@ import (
// ║ ObjectModelConfiguration ║───────│ GroupConfiguration │───────│ VersionConfiguration │───────│ TypeConfiguration │───────│ PropertyConfiguration │
// ║ ║1 1..n│ │1 1..n│ │1 1..n│ │1 1..n│ │
// ╚══════════════════════════╝ └────────────────────┘ └──────────────────────┘ └───────────────────┘ └───────────────────────┘
//
type ObjectModelConfiguration struct {
groups map[string]*GroupConfiguration // nested configuration for individual groups
typoAdvisor *TypoAdvisor

Просмотреть файл

@ -21,7 +21,6 @@ import (
// │ ObjectModelConfiguration │───────│ GroupConfiguration │───────│ VersionConfiguration │───────│ TypeConfiguration │───────║ PropertyConfiguration ║
// │ │1 1..n│ │1 1..n│ │1 1..n│ │1 1..n║ ║
// └──────────────────────────┘ └────────────────────┘ └──────────────────────┘ └───────────────────┘ ╚═══════════════════════╝
//
type PropertyConfiguration struct {
name string
nameInNextVersion configurable[string] // Name this property has in the next version

Просмотреть файл

@ -24,7 +24,6 @@ import (
// │ ObjectModelConfiguration │───────│ GroupConfiguration │───────│ VersionConfiguration │───────║ TypeConfiguration ║───────│ PropertyConfiguration │
// │ │1 1..n│ │1 1..n│ │1 1..n║ ║1 1..n│ │
// └──────────────────────────┘ └────────────────────┘ └──────────────────────┘ ╚═══════════════════╝ └───────────────────────┘
//
type TypeConfiguration struct {
name string
properties map[string]*PropertyConfiguration

Просмотреть файл

@ -22,7 +22,6 @@ import (
// │ ObjectModelConfiguration │───────│ GroupConfiguration │───────║ VersionConfiguration ║───────│ TypeConfiguration │───────│ PropertyConfiguration │
// │ │1 1..n│ │1 1..n║ ║1 1..n│ │1 1..n│ │
// └──────────────────────────┘ └────────────────────┘ ╚══════════════════════╝ └───────────────────┘ └───────────────────────┘
//
type VersionConfiguration struct {
name string
types map[string]*TypeConfiguration

Просмотреть файл

@ -35,10 +35,10 @@ type PropertyConversion func(
// ctx contains additional information that may be needed when creating the property conversion
//
// The factory should return one of three result sets:
// * For a fatal error, one that guarantees no conversion can be generated, return (nil, error)
// This will abort the conversion process and return an error for logging.
// * For a valid conversion, return (conversion, nil)
// * When no conversion could be generated by this factory, return (nil, nil) to delegate to another factory
// - For a fatal error, one that guarantees no conversion can be generated, return (nil, error)
// This will abort the conversion process and return an error for logging.
// - For a valid conversion, return (conversion, nil)
// - When no conversion could be generated by this factory, return (nil, nil) to delegate to another factory
//
// Each conversion should be written with lead predicates to make sure that it only fires in the correct circumstances.
// This requires, in particular, that most conversions check for optionality and bag items and exit early when those are
@ -47,7 +47,6 @@ type PropertyConversion func(
// generate the correct code; any conversion that relies on being "protected" from particular situations by having other
// conversions earlier in the list held by propertyConversionFactories is brittle and likely to generate the incorrect
// code if the order of items in the list is modified.
//
type PropertyConversionFactory func(
source *TypedConversionEndpoint,
destination *TypedConversionEndpoint,
@ -110,47 +109,48 @@ func init() {
// source *string => destination *Sku
//
// assuming
// type Sku string
//
// type Sku string
//
// assignFromOptional can handle the optionality of sourceEndpoint and makes a recursive call
// to CreateTypeConversion() with the simpler target:
//
// source string => destination *Sku
//
// assignToOptional can handle the optionality of destinationEndpoint and makes a recursive
// call to CreateTypeConversion() with a simpler target:
// assignToOptional can handle the optionality of destinationEndpoint and makes a recursive
// call to CreateTypeConversion() with a simpler target:
//
// source string => destination Sku
// source string => destination Sku
//
// assignToAliasedPrimitive can handle the type conversion of string to Sku, and makes
// a recursive call to CreateTypeConversion() with a simpler target:
// assignToAliasedPrimitive can handle the type conversion of string to Sku, and makes
// a recursive call to CreateTypeConversion() with a simpler target:
//
// source string => destination string
// source string => destination string
//
// assignPrimitiveFromPrimitive can handle primitive values, and generates a
// conversion that does a simple assignment:
// assignPrimitiveFromPrimitive can handle primitive values, and generates a
// conversion that does a simple assignment:
//
// destination = source
// destination = source
//
// assignToAliasedPrimitive injects the necessary type conversion:
// assignToAliasedPrimitive injects the necessary type conversion:
//
// destination = Sku(source)
// destination = Sku(source)
//
// assignToOptional injects a local variable and takes it's address
// assignToOptional injects a local variable and takes it's address
//
// sku := Sku(source)
// destination = &sku
// sku := Sku(source)
// destination = &sku
//
// finally, assignFromOptional injects the check to see if we have a value to assign in the
// first place, assigning a suitable zero value if we don't:
//
// if source != nil {
// sku := Sku(source)
// destination := &sku
// } else {
// destination := ""
// }
// if source != nil {
// sku := Sku(source)
// destination := &sku
// } else {
//
// destination := ""
// }
func CreateTypeConversion(
sourceEndpoint *TypedConversionEndpoint,
destinationEndpoint *TypedConversionEndpoint,
@ -193,26 +193,27 @@ func NameOfPropertyAssignmentFunction(
// writeToBagItem will generate a conversion where the destination is in our property bag
//
// For non-optional sources, the value is directly added
// # For non-optional sources, the value is directly added
//
// <propertyBag>.Add(<propertyName>, <source>)
//
// For optional sources, the value is only added if non-nil; if nil, we remove any existing item
//
// if <source> != nil {
// <propertyBag>.Add(<propertyName>, *<source>)
// } else {
// <propertyBag>.Remove(<propertyName>)
// }
// if <source> != nil {
// <propertyBag>.Add(<propertyName>, *<source>)
// } else {
//
// <propertyBag>.Remove(<propertyName>)
// }
//
// For slice and slice sources, the value is only added if it is non-empty; if empty we remove any existing item
//
// if len(<source>) > 0 {
// <propertyBag>.Add(<propertyName>, <source>)
// } else {
// <propertyBag>.Remove(<propertyName>)
// }
// if len(<source>) > 0 {
// <propertyBag>.Add(<propertyName>, <source>)
// } else {
//
// <propertyBag>.Remove(<propertyName>)
// }
func writeToBagItem(
sourceEndpoint *TypedConversionEndpoint,
destinationEndpoint *TypedConversionEndpoint,
@ -288,7 +289,6 @@ func writeToBagItem(
// underlying type of the destination is compatible with the source.
//
// <destination> = &<source>
//
func assignToOptional(
sourceEndpoint *TypedConversionEndpoint,
destinationEndpoint *TypedConversionEndpoint,
@ -348,18 +348,18 @@ func assignToOptional(
// pullFromBagItem will populate a property from a property bag
//
// if <propertyBag>.Contains(<sourceName>) {
// var <value> <destinationType>
// err := <propertyBag>.Pull(<sourceName>, &<value>)
// if err != nil {
// return errors.Wrapf(err, ...)
// }
// if <propertyBag>.Contains(<sourceName>) {
// var <value> <destinationType>
// err := <propertyBag>.Pull(<sourceName>, &<value>)
// if err != nil {
// return errors.Wrapf(err, ...)
// }
//
// <destination> = <value>
// } else {
// <destination> = <zero>
// }
// <destination> = <value>
// } else {
//
// <destination> = <zero>
// }
func pullFromBagItem(
sourceEndpoint *TypedConversionEndpoint,
destinationEndpoint *TypedConversionEndpoint,
@ -444,15 +444,16 @@ func pullFromBagItem(
// assignFromOptional will handle the case where the source type may be missing (nil)
//
// <original> := <source>
// if <original> != nil {
// <destination> = *<original>
// } else {
// <destination> = <zero>
// }
//
// if <original> != nil {
// <destination> = *<original>
// } else {
//
// <destination> = <zero>
// }
//
// Must trigger before assignToOptional so we generate the right zero values; to enforce this, assignToOptional includes
// a predicate check that the source is NOT optional, allowing this conversion to trigger first.
//
func assignFromOptional(
sourceEndpoint *TypedConversionEndpoint,
destinationEndpoint *TypedConversionEndpoint,
@ -528,7 +529,6 @@ func assignFromOptional(
// the source is type compatible with the base type of the enumeration
//
// <destination> = <enumeration-cast>(<source>)
//
func assignToEnumeration(
sourceEndpoint *TypedConversionEndpoint,
destinationEndpoint *TypedConversionEndpoint,
@ -585,7 +585,6 @@ func assignToEnumeration(
// same primitive type and are not optional
//
// <destination> = <source>
//
func assignPrimitiveFromPrimitive(
sourceEndpoint *TypedConversionEndpoint,
destinationEndpoint *TypedConversionEndpoint,
@ -627,7 +626,6 @@ func assignPrimitiveFromPrimitive(
// definitions have the same underlying primitive type and are not optional
//
// <destination> = <cast>(<source>)
//
func assignAliasedPrimitiveFromAliasedPrimitive(
sourceEndpoint *TypedConversionEndpoint,
destinationEndpoint *TypedConversionEndpoint,
@ -727,7 +725,6 @@ func assignFromAliasedPrimitive(
// is not optional and we can find a conversion to give us the primitive type.
//
// <destination> = <cast>(<source>)
//
func assignToAliasedPrimitive(
sourceEndpoint *TypedConversionEndpoint,
destinationEndpoint *TypedConversionEndpoint,
@ -859,13 +856,14 @@ func assignHandcraftedImplementations(
// underlying definitions of the two arrays are compatible
//
// <arr> := make([]<type>, len(<reader>))
// for <index>, <value> := range <reader> {
// // Shadow the loop variable to avoid aliasing
// <value> := <value>
// <arr>[<index>] := <value> // Or other conversion as required
// }
// <writer> = <arr>
//
// for <index>, <value> := range <reader> {
// // Shadow the loop variable to avoid aliasing
// <value> := <value>
// <arr>[<index>] := <value> // Or other conversion as required
// }
//
// <writer> = <arr>
func assignArrayFromArray(
sourceEndpoint *TypedConversionEndpoint,
destinationEndpoint *TypedConversionEndpoint,
@ -978,16 +976,16 @@ func assignArrayFromArray(
// assignMapFromMap will generate a code fragment to populate an array, assuming the
// underlying definitions of the two arrays are compatible
//
// if <reader> != nil {
// <map> := make(map[<key>]<type>)
// for key, <item> := range <reader> {
// <map>[<key>] := <item>
// }
// <writer> = <map>
// } else {
// <writer> = <zero>
// }
// if <reader> != nil {
// <map> := make(map[<key>]<type>)
// for key, <item> := range <reader> {
// <map>[<key>] := <item>
// }
// <writer> = <map>
// } else {
//
// <writer> = <zero>
// }
func assignMapFromMap(
sourceEndpoint *TypedConversionEndpoint,
destinationEndpoint *TypedConversionEndpoint,
@ -1176,7 +1174,6 @@ func assignEnumFromEnum(
//
// <local> = <baseType>(<source>)
// <destination> = <enum>(<local>)
//
func assignPrimitiveFromEnum(
sourceEndpoint *TypedConversionEndpoint,
destinationEndpoint *TypedConversionEndpoint,
@ -1223,11 +1220,12 @@ func assignPrimitiveFromEnum(
//
// var <local> <destinationType>
// err := <local>.AssignPropertiesFrom(<source>)
// if err != nil {
// return errors.Wrap(err, "while calling <local>.AssignPropertiesFrom(<source>)")
// }
// <destination> = <local>
//
// if err != nil {
// return errors.Wrap(err, "while calling <local>.AssignPropertiesFrom(<source>)")
// }
//
// <destination> = <local>
func assignObjectDirectlyFromObject(
sourceEndpoint *TypedConversionEndpoint,
destinationEndpoint *TypedConversionEndpoint,
@ -1335,11 +1333,12 @@ func assignObjectDirectlyFromObject(
//
// var <local> <destinationType>
// err := <source>.AssignPropertiesTo(&<local>)
// if err != nil {
// return errors.Wrap(err, "while calling <local>.AssignPropertiesTo(<source>)")
// }
// <destination> = <local>
//
// if err != nil {
// return errors.Wrap(err, "while calling <local>.AssignPropertiesTo(<source>)")
// }
//
// <destination> = <local>
func assignObjectDirectlyToObject(
sourceEndpoint *TypedConversionEndpoint,
destinationEndpoint *TypedConversionEndpoint,
@ -1450,19 +1449,20 @@ func assignObjectDirectlyToObject(
//
// var <local> <intermediateType>
// err := <local>.AssignPropertiesFrom(<source>)
// if err != nil {
// return errors.Wrap(err, "while calling <local>.AssignPropertiesFrom(<source>)")
// }
//
// if err != nil {
// return errors.Wrap(err, "while calling <local>.AssignPropertiesFrom(<source>)")
// }
//
// var <otherlocal> <destinationType>
// err := <otherlocal>.AssignPropertiesFrom(<local>)
// if err != nil {
// return errors.Wrap(err, "while calling <otherlocal>.AssignPropertiesFrom(<local>)")
// }
//
// if err != nil {
// return errors.Wrap(err, "while calling <otherlocal>.AssignPropertiesFrom(<local>)")
// }
//
// Note the actual steps are generated by nested conversions; this handler works by finding the two conversions needed
// given our intermediate type and chaining them together.
//
func assignObjectsViaIntermediateObject(
sourceEndpoint *TypedConversionEndpoint,
destinationEndpoint *TypedConversionEndpoint,
@ -1619,7 +1619,6 @@ const (
// copyKnownType will generate an assignment with the results of a call on the specified TypeName
//
// <destination> = <source>.<methodName>()
//
func copyKnownType(name astmodel.TypeName, methodName string, returnKind knownTypeMethodReturn) func(*TypedConversionEndpoint, *TypedConversionEndpoint, *PropertyConversionContext) (PropertyConversion, error) {
return func(sourceEndpoint *TypedConversionEndpoint, destinationEndpoint *TypedConversionEndpoint, _ *PropertyConversionContext) (PropertyConversion, error) {
// Require both source and destination to not be bag items

Просмотреть файл

@ -42,7 +42,6 @@ func (set ReadableConversionEndpointSet) CreateValueFunctionEndpoints(sourceType
// Background: When our source instance has a property bag, that bag might contain values we can write into properties
// on our destination instance. We therefore iterate through each property on the *destination* type and create a
// ReadableConversionEndpoint for each one that looks in the property bag for a value.
//
func (set ReadableConversionEndpointSet) CreatePropertyBagMemberEndpoints(destinationType astmodel.Type) int {
// Add a property bag item endpoint for each property we don't already support
return set.addForEachProperty(destinationType, func(prop *astmodel.PropertyDefinition) *ReadableConversionEndpoint {

Просмотреть файл

@ -32,7 +32,6 @@ func (set WritableConversionEndpointSet) CreatePropertyEndpoints(destinationType
// Background: When our destination instance has a property bag, that bag can be used to stash properties from the
// source where there is no matching destination property. We therefore iterate through each property on the *source*
// type and create a WritableConversionEndpoint for each one so the value is stashed in the property bag.
//
func (set WritableConversionEndpointSet) CreatePropertyBagMemberEndpoints(sourceType astmodel.Type) int {
// Add a property bag member endpoint for each property we don't already support
return set.addForEachProperty(sourceType, func(prop *astmodel.PropertyDefinition) *WritableConversionEndpoint {

Просмотреть файл

@ -26,17 +26,16 @@ import (
// preexisting AssignProperties*() method. Otherwise, we chain to that type to do the conversion to an intermediate
// instance and then convert using that.
//
// func (r <receiver>) ConvertFrom(instance <interfaceType>) error {
// source, ok := instance.(*<otherType>)
// if !ok {
// // Need indirect conversion
// source = &<otherType>{}
// source.ConvertFrom(instance)
// }
//
// return r.AssignPropertiesFrom(source)
// }
// func (r <receiver>) ConvertFrom(instance <interfaceType>) error {
// source, ok := instance.(*<otherType>)
// if !ok {
// // Need indirect conversion
// source = &<otherType>{}
// source.ConvertFrom(instance)
// }
//
// return r.AssignPropertiesFrom(source)
// }
type ChainedConversionFunction struct {
// name is the unique name for this function
name string
@ -149,24 +148,23 @@ func (fn *ChainedConversionFunction) AsFunc(
//
// For ConvertFrom, we generate
//
// src, ok := source.(*<intermediateType>)
// if ok {
// // Populate our instance from source
// return s.AssignPropertiesFrom(source)
// }
// src, ok := source.(*<intermediateType>)
// if ok {
// // Populate our instance from source
// return s.AssignPropertiesFrom(source)
// }
//
// // Convert to an intermediate form
// src = &<intermediateType>{}
// err := src.ConvertFrom(source)
// if err != nil {
// return errors.Wrapf(err, "...elided...")
// }
// // Convert to an intermediate form
// src = &<intermediateType>{}
// err := src.ConvertFrom(source)
// if err != nil {
// return errors.Wrapf(err, "...elided...")
// }
//
// // Update our instance from src
// return s.AssignPropertiesFrom(src)
// // Update our instance from src
// return s.AssignPropertiesFrom(src)
//
// For ConvertTo, we have essentially the same structure, but two-step conversion is done in the other order.
//
func (fn *ChainedConversionFunction) bodyForConvert(
receiverName string, parameterName string, generationContext *astmodel.CodeGenerationContext) []dst.Stmt {

Просмотреть файл

@ -16,9 +16,9 @@ import (
// GetConditionsFunction returns a function declaration containing the implementation of the GetConditions() function.
//
// func (r *<receiver>) GetConditions() genruntime.Conditions {
// return r.Status.Conditions
// }
// func (r *<receiver>) GetConditions() genruntime.Conditions {
// return r.Status.Conditions
// }
func GetConditionsFunction(k *ResourceFunction, codeGenerationContext *astmodel.CodeGenerationContext, receiver astmodel.TypeName, methodName string) *dst.FuncDecl {
receiverIdent := k.IdFactory().CreateReceiver(receiver.Name())
receiverType := receiver.AsType(codeGenerationContext)
@ -44,9 +44,9 @@ func GetConditionsFunction(k *ResourceFunction, codeGenerationContext *astmodel.
// SetConditionsFunction returns a function declaration containing the implementation of the SetConditions() function.
//
// func (r *<receiver>) SetConditions(conditions genruntime.Conditions) {
// r.Status.Conditions = conditions
// }
// func (r *<receiver>) SetConditions(conditions genruntime.Conditions) {
// r.Status.Conditions = conditions
// }
func SetConditionsFunction(k *ResourceFunction, codeGenerationContext *astmodel.CodeGenerationContext, receiver astmodel.TypeName, methodName string) *dst.FuncDecl {
conditionsParameterName := k.IdFactory().CreateIdentifier(astmodel.ConditionsProperty, astmodel.NotExported)

Просмотреть файл

@ -64,7 +64,8 @@ func validateResourceReferences(k *ResourceFunction, codeGenerationContext *astm
}
// validateResourceReferencesBody helps generate the body of the validateResourceReferences function:
// refs, err := reflecthelpers.FindResourceReferences(&<resource>.Spec)
//
// refs, err := reflecthelpers.FindResourceReferences(&<resource>.Spec)
// if err != nil {
// return err
// }
@ -123,10 +124,10 @@ func validateWriteOncePropertiesFunction(resourceFn *ResourceFunction, codeGener
// validateWriteOncePropertiesFunctionBody helps generate the body of the validateWriteOncePropertiesFunctionBody function:
//
// oldObj, ok := old.(*Receiver)
// if !ok {
// return nil
// }
// oldObj, ok := old.(*Receiver)
// if !ok {
// return nil
// }
//
// return genruntime.ValidateWriteOnceProperties(oldObj, <receiverIndent>)
func validateWriteOncePropertiesFunctionBody(receiver astmodel.TypeName, codeGenerationContext *astmodel.CodeGenerationContext, receiverIdent string) []dst.Stmt {
@ -175,7 +176,8 @@ func validateOptionalConfigMapReferences(k *ResourceFunction, codeGenerationCont
}
// validateOptionalConfigMapReferencesBody helps generate the body of the validateResourceReferences function:
// refs, err := reflecthelpers.FindOptionalConfigMapReferences(&<resource>.Spec)
//
// refs, err := reflecthelpers.FindOptionalConfigMapReferences(&<resource>.Spec)
// if err != nil {
// return err
// }

Просмотреть файл

@ -214,6 +214,7 @@ func (v *ValidatorBuilder) validateDelete(k *ResourceFunction, codeGenerationCon
// validateBody returns the body for the generic validation function which invokes all local (code generated) validations
// as well as checking if there are any handcrafted validations and invoking them too:
// For example:
//
// validations := <receiverIdent>.createValidations()
// var temp interface{} = <receiverIdent>
// if runtimeValidator, ok := temp.(genruntime.Validator); ok {
@ -326,12 +327,15 @@ func (v *ValidatorBuilder) makeLocalValidationFuncDetails(kind ValidationKind, c
}
// localValidationFuncBody returns the body of the local (code generated) validation functions:
//
// return []func() error{
// <receiver>.<validationFunc1>,
// <receiver>.<validationFunc2>,
// ...
// }
//
// or in the case of update functions (that may not need the old parameter):
//
// return []func(old runtime.Object) error{
// func(old runtime.Object) error {
// return <receiver>.<validationFunc1>
@ -361,8 +365,11 @@ func (v *ValidatorBuilder) localValidationFuncBody(kind ValidationKind, codeGene
// makeLocalValidationElement creates a validation expression, automatically removing the old parameter for update
// validations if it's not needed. These elements are used to build the list of validation functions.
// If validation != ValidationKindUpdate or validation == ValidationKindUpdate that DOES use the old parameter:
//
// <receiver>.<validationFunc>
//
// If validate == ValidationKindUpdate that doesn't use the old parameter:
//
// func(old runtime.Object) error {
// return <receiver>.<validationFunc>
// }

Просмотреть файл

@ -63,20 +63,21 @@ func (d *KubernetesExporterBuilder) ToInterfaceImplementation() *astmodel.Interf
// exportKubernetesResource returns the body of the ExportKubernetesResources function, which implements
// the genruntime.KubernetesExporter interface.
// Generates code like:
// func (<receiver> *<receiverType>) ExportKubernetesResources(_ context.Context, _ genruntime.MetaObject, _ *genericarmclient.GenericClient, _ logr.Logger) ([]client.Object, error) {
// collector := configmaps.NewCollector(<receiver>.Namespace)
// if <receiver>.Spec.OperatorSpec != nil && <receiver>.Spec.OperatorSpec.ConfigMaps != nil {
// if <receiver>.<propertyPath> != nil {
// collector.AddValue(<receiver>.Spec.OperatorSpec.ConfigMaps.<configMapProperty>, *<receiver>.<propertyPath>)
//
// func (<receiver> *<receiverType>) ExportKubernetesResources(_ context.Context, _ genruntime.MetaObject, _ *genericarmclient.GenericClient, _ logr.Logger) ([]client.Object, error) {
// collector := configmaps.NewCollector(<receiver>.Namespace)
// if <receiver>.Spec.OperatorSpec != nil && <receiver>.Spec.OperatorSpec.ConfigMaps != nil {
// if <receiver>.<propertyPath> != nil {
// collector.AddValue(<receiver>.Spec.OperatorSpec.ConfigMaps.<configMapProperty>, *<receiver>.<propertyPath>)
// }
// }
// ...
// result, err := collector.Values()
// if err != nil {
// return nil, err
// }
// return configmaps.SliceToClientObjectSlice(result), nil
// }
// ...
// result, err := collector.Values()
// if err != nil {
// return nil, err
// }
// return configmaps.SliceToClientObjectSlice(result), nil
//}
func (d *KubernetesExporterBuilder) exportKubernetesResources(k *ResourceFunction, codeGenerationContext *astmodel.CodeGenerationContext, receiver astmodel.TypeName, methodName string) *dst.FuncDecl {
receiverIdent := k.IdFactory().CreateReceiver(receiver.Name())
receiverType := receiver.AsType(codeGenerationContext)

Просмотреть файл

@ -16,14 +16,13 @@ import (
// We put these on our resource types, giving us a way to obtain the right type of instance when the reconciler is
// working with ARM. The code differs slightly depending on whether we're injecting into an API or storage variant.
//
// func (resource *SomeResource) OriginalGVK() scheme.GroupVersionKind {
// return scheme.GroupVersionKind{
// Group: GroupVersion.Group,
// Version: resource.Spec.OriginalVersion,
// Kind: "SomeResource",
// }
// }
//
// func (resource *SomeResource) OriginalGVK() scheme.GroupVersionKind {
// return scheme.GroupVersionKind{
// Group: GroupVersion.Group,
// Version: resource.Spec.OriginalVersion,
// Kind: "SomeResource",
// }
// }
type OriginalGVKFunction struct {
idFactory astmodel.IdentifierFactory
hasOriginalVersionFunction bool

Просмотреть файл

@ -16,10 +16,9 @@ import (
// We put these on the *API* versions of our spec types, giving us a way to obtain the right type of instance when the
// reconciler is working with ARM.
//
// func (spec *SomeSpec) OriginalVersion() string {
// return GroupVersion.Version
// }
//
// func (spec *SomeSpec) OriginalVersion() string {
// return GroupVersion.Version
// }
type OriginalVersionFunction struct {
idFactory astmodel.IdentifierFactory
}

Просмотреть файл

@ -24,24 +24,24 @@ import (
// If the two instances involved in conversion are not in the same "spoke" leading to this "hub" version, we'll pivot to
// the reverse conversion, starting at the far end of that "spoke":
//
// func (s <hubtype>) ConvertFrom(instance <interfaceType>>) error {
// return instance.ConvertTo(s)
// }
// func (s <hubtype>) ConvertFrom(instance <interfaceType>>) error {
// return instance.ConvertTo(s)
// }
//
// The pivot is needed when following the package references from resource version to resource version won't lead us to
// encounter the other type that's involved in the conversion, as we can see if we're trying to convert between v1 and
// v2:
//
// ################## ################# +---------------+
// # v1 # # v2 # | v3 |
// # Person # # Person # | Person |
// ################## ################# +---------------+
// | | |
// v v v
// +----------------+ +---------------+ +---------------+
// | v1storage | | v2storage | | v3storage |
// | Person |----------------->| Person |----------------->| Person |
// +----------------+ +---------------+ +---------------+
// ################## ################# +---------------+
// # v1 # # v2 # | v3 |
// # Person # # Person # | Person |
// ################## ################# +---------------+
// | | |
// v v v
// +----------------+ +---------------+ +---------------+
// | v1storage | | v2storage | | v3storage |
// | Person |----------------->| Person |----------------->| Person |
// +----------------+ +---------------+ +---------------+
//
// Following package references from v1 leads us, in turn, to v1storage, v2storage, and finally v3storage (our hub
// version), none of is the version we need to terminate the conversion path.
@ -52,13 +52,13 @@ import (
//
// v1.Person.ConvertTo(v2.Person)
// --> v1storage.Person.ConvertTo(v2.Person)
// --> v2storage.Person.ConvertTo(v2.Person)
// --> v3storage.Person.ConvertTo(v2.Person) // Pivot!
// --> v2.Person.ConvertFrom(v3storage.Person) // Change of direction
// --> v2storage.Person.ConvertFrom(v3storage.Person)
//
// --> v2storage.Person.ConvertTo(v2.Person)
// --> v3storage.Person.ConvertTo(v2.Person) // Pivot!
// --> v2.Person.ConvertFrom(v3storage.Person) // Change of direction
// --> v2storage.Person.ConvertFrom(v3storage.Person)
//
// Note that conversions like this always pivot through the hub version.
//
type PivotConversionFunction struct {
// nameFrom is the name for this function when converting FROM a provided instance
nameFrom string
@ -162,7 +162,6 @@ func (fn *PivotConversionFunction) AsFunc(
//
// Note that the method called is in the *other* *direction*; we restart the conversion at the extreme of the second
// spoke, invoking conversions back towards the hub again.
//
func (fn *PivotConversionFunction) bodyForPivot(
receiverName string,
parameterName string,

Просмотреть файл

@ -233,9 +233,11 @@ func (fn *PropertyAssignmentFunction) generateBody(
// createPropertyBagPrologue creates any introductory statements needed to set up our property bag before we start doing
// assignments. We need to handle three cases:
// o If our source has a property bag, we clone it.
// o If our destination has a property bag (and our source does not), we create a new one.
// o If neither source nor destination has a property bag, we don't need to do anything.
//
// o If our source has a property bag, we clone it.
// o If our destination has a property bag (and our source does not), we create a new one.
// o If neither source nor destination has a property bag, we don't need to do anything.
//
// source is the name of the source to read the property bag from
func (fn *PropertyAssignmentFunction) createPropertyBagPrologue(
source string,
@ -289,10 +291,11 @@ func (fn *PropertyAssignmentFunction) createPropertyBagPrologue(
// propertyBagEpilogue creates any concluding statements required to handle our property bag after assignments are
// complete.
// o If the destination has a property bag
// > If our bag is empty, we set the destination to nil
// > Otherwise we need to store our current property bag there
// o Otherwise we do nothing
//
// o If the destination has a property bag
// > If our bag is empty, we set the destination to nil
// > Otherwise we need to store our current property bag there
// o Otherwise we do nothing
func (fn *PropertyAssignmentFunction) propertyBagEpilogue(
destination string,
) []dst.Stmt {

Просмотреть файл

@ -20,31 +20,30 @@ import (
//
// Direct conversion from the hub type:
//
// func (<receiver> <receiverType>) Convert<From|To>(hub conversion.Hub) error {
// source, ok := hub.(*<hub.Type>)
// if !ok {
// return fmt.Errorf("expected <hub.Type> but received %T instead", hub)
// }
// return <receiver>.AssignProperties<From|To><Type>(source)
// }
// func (<receiver> <receiverType>) Convert<From|To>(hub conversion.Hub) error {
// source, ok := hub.(*<hub.Type>)
// if !ok {
// return fmt.Errorf("expected <hub.Type> but received %T instead", hub)
// }
// return <receiver>.AssignProperties<From|To><Type>(source)
// }
//
// Indirect conversion, multiple steps via an intermediate instance
//
// func (r <receiver>) Convert<From|To>(hub conversion.Hub) error {
// var source <vNext>
// err := source.Convert<From|To>(hub)
// if err != nil {
// return errors.Wrap(err, "converting from hub to source")
// }
// func (r <receiver>) Convert<From|To>(hub conversion.Hub) error {
// var source <vNext>
// err := source.Convert<From|To>(hub)
// if err != nil {
// return errors.Wrap(err, "converting from hub to source")
// }
//
// err = <receiver>.AssignProperties<From|To><Type>(&source)
// if err != nil {
// return errors.Wrap(err, "converting from source to <type>")
// }
//
// return nil
// }
// err = <receiver>.AssignProperties<From|To><Type>(&source)
// if err != nil {
// return errors.Wrap(err, "converting from source to <type>")
// }
//
// return nil
// }
type ResourceConversionFunction struct {
// hub is the TypeName of the canonical hub type, the final target or original source for conversion
hub astmodel.TypeName
@ -141,12 +140,12 @@ func (fn *ResourceConversionFunction) Hub() astmodel.TypeName {
// directConversion creates a simple direct conversion between the two types
//
// <local>, ok := <hubAsInterface>.(<actualHubType>)
// if !ok {
// return errors.Errorf("expected <actualHubType> but received %T instead", <hubAsInterface>)
// }
//
// if !ok {
// return errors.Errorf("expected <actualHubType> but received %T instead", <hubAsInterface>)
// }
//
// return <receiver>.AssignProperties(To|From)<type>(<local>)
//
func (fn *ResourceConversionFunction) directConversion(
receiverName string, generationContext *astmodel.CodeGenerationContext) []dst.Stmt {
fmtPackage := generationContext.MustGetImportedPackageName(astmodel.FmtReference)
@ -182,17 +181,18 @@ func (fn *ResourceConversionFunction) directConversion(
//
// var source <intermediateType>
// err := source.ConvertFrom(<hub>)
// if err != nil {
// return errors.Wrap(err, "converting from hub to source")
// }
//
// if err != nil {
// return errors.Wrap(err, "converting from hub to source")
// }
//
// err = <receiver>.AssignPropertiesFrom<type>(&source)
// if err != nil {
// return errors.Wrap(err, "converting from source to <type>")
// }
//
// if err != nil {
// return errors.Wrap(err, "converting from source to <type>")
// }
//
// return nil
//
func (fn *ResourceConversionFunction) indirectConversionFromHub(
receiverName string, generationContext *astmodel.CodeGenerationContext) []dst.Stmt {
errorsPackage := generationContext.MustGetImportedPackageName(astmodel.GitHubErrorsReference)
@ -240,17 +240,18 @@ func (fn *ResourceConversionFunction) indirectConversionFromHub(
//
// var destination <intermediateType>
// err = <receiver>.AssignPropertiesTo<type>(&destination)
// if err != nil {
// return errors.Wrap(err, "converting to destination from <type>")
// }
//
// if err != nil {
// return errors.Wrap(err, "converting to destination from <type>")
// }
//
// err := destination.ConvertTo(<hub>)
// if err != nil {
// return errors.Wrap(err, "converting from destination to hub")
// }
//
// if err != nil {
// return errors.Wrap(err, "converting from destination to hub")
// }
//
// return nil
//
func (fn *ResourceConversionFunction) indirectConversionToHub(
receiverName string, generationContext *astmodel.CodeGenerationContext) []dst.Stmt {
errorsPackage := generationContext.MustGetImportedPackageName(astmodel.GitHubErrorsReference)

Просмотреть файл

@ -270,11 +270,14 @@ func fixedValueGetAzureNameFunction(fixedValue string) functions.ObjectFunctionH
// newOwnerFunction creates the Owner function declaration. This has two possible formats.
// For normal resources:
//
// func (<receiver> *<receiver>) Owner() *genruntime.ResourceReference {
// group, kind := genruntime.LookupOwnerGroupKind(<receiver>.Spec)
// return &genruntime.ResourceReference{Group: group, Kind: kind, Namespace: <receiver>.Namespace, Name: <receiver>.Spec.Owner.Name}
// }
//
// For extension resources:
//
// func (<receiver> *<receiver>) Owner() *genruntime.ResourceReference {
// return &genruntime.ResourceReference{Group: <receiver>.Spec.Owner.Group, Kind: <receiver>.Spec.Owner.Kind, name: <receiver>.Spec.Owner.Name}
// }
@ -329,6 +332,7 @@ func newOwnerFunction(r *astmodel.ResourceType) func(k *functions.ObjectFunction
}
// newGetResourceScopeFunction creates a function that returns the scope of the resource.
//
// func (<receiver> *<receiver>) GetResourceScope() genruntime.ResourceScope {
// return genruntime.ResourceScopeResourceGroup
// }

Просмотреть файл

@ -115,29 +115,30 @@ func (scanner *SchemaScanner) RunHandlerForSchema(ctx context.Context, schema Sc
}
// GenerateDefinitionsFromDeploymentTemplate takes in the resources section of the Azure deployment template schema and returns golang AST Packages
// containing the types described in the schema which match the {resource_type}/{version} filters provided.
//
// The schema we are working with is something like the following (in yaml for brevity):
// containing the types described in the schema which match the {resource_type}/{version} filters provided.
//
// resources:
// items:
// oneOf:
// allOf:
// $ref: {{ base resource schema for ARM }}
// oneOf:
// - ARM resources
// oneOf:
// allOf:
// $ref: {{ base resource for external resources, think SendGrid }}
// oneOf:
// - External ARM resources
// oneOf:
// allOf:
// $ref: {{ base resource for ARM specific stuff like locks, deployments, etc }}
// oneOf:
// - ARM specific resources. I'm not 100% sure why...
// The schema we are working with is something like the following (in yaml for brevity):
//
// allOf acts like composition which composites each schema from the child oneOf with the base reference from allOf.
// resources:
// items:
// oneOf:
// allOf:
// $ref: {{ base resource schema for ARM }}
// oneOf:
// - ARM resources
// oneOf:
// allOf:
// $ref: {{ base resource for external resources, think SendGrid }}
// oneOf:
// - External ARM resources
// oneOf:
// allOf:
// $ref: {{ base resource for ARM specific stuff like locks, deployments, etc }}
// oneOf:
// - ARM specific resources. I'm not 100% sure why...
//
// allOf acts like composition which composites each schema from the child oneOf with the base reference from allOf.
func (scanner *SchemaScanner) GenerateDefinitionsFromDeploymentTemplate(ctx context.Context, root Schema) (astmodel.TypeDefinitionSet, error) {
ctx, span := tab.StartSpan(ctx, "GenerateDefinitionsFromDeploymentTemplate")
defer span.End()

Просмотреть файл

@ -19,23 +19,24 @@ import (
// apiVersion: apiextensions.k8s.io/v1
// kind: CustomResourceDefinition
// metadata:
// name: roleassignments.microsoft.authorization.azure.com
// annotations:
// cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)
//
// name: roleassignments.microsoft.authorization.azure.com
// annotations:
// cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)
//
// spec:
// preserveUnknownFields: false
// conversion:
// strategy: Webhook
// webhook:
// conversionReviewVersions:
// - v1beta1
// clientConfig:
// service:
// namespace: system
// name: webhook-service
// path: /convert
//
//
// preserveUnknownFields: false
// conversion:
// strategy: Webhook
// webhook:
// conversionReviewVersions:
// - v1beta1
// clientConfig:
// service:
// namespace: system
// name: webhook-service
// path: /convert
type ConversionPatchFile struct {
ApiVersion string `yaml:"apiVersion"`
Kind string `yaml:"kind"`

Просмотреть файл

@ -25,7 +25,6 @@ import (
// - patches/webhook-conversion-microsoft.authorization.azure.com_roleassignments.yaml
// - patches/webhook-conversion-microsoft.batch.azure.com_batchaccounts.yaml
// ...
//
type CRDKustomizeFile struct {
Resources []string `yaml:"resources"` // File paths to resource CRDs
Patches []string `yaml:"patches"` // File paths to patch files used to enable conversions

Просмотреть файл

@ -147,7 +147,6 @@ func (tc *ResourceConversionTestCase) Equals(other astmodel.TestCase, override a
// properties := gopter.NewProperties(parameters)
// properties.Property("...", prop.ForAll(RunTestForX, XGenerator())
// properties.TestingRun(t, gopter.NewFormatedReporter(true, 240, os.Stdout))
//
func (tc *ResourceConversionTestCase) createTestRunner(codegenContext *astmodel.CodeGenerationContext) dst.Decl {
const (
parametersLocal = "parameters"
@ -237,24 +236,26 @@ func (tc *ResourceConversionTestCase) createTestRunner(codegenContext *astmodel.
//
// var hub OtherType
// err := subject.ConvertTo(&hub)
// if err != nil {
// return err.Error()
// }
//
// if err != nil {
// return err.Error()
// }
//
// var result OurType
// err = result.ConvertFrom(&hub)
// if err != nil {
// return err.Error()
// }
//
// if err != nil {
// return err.Error()
// }
//
// match := cmp.Equal(subject, actual, cmpopts.EquateEmpty())
// if !match {
// result := diff.Diff(subject, actual);
// return result
// }
//
// if !match {
// result := diff.Diff(subject, actual);
// return result
// }
//
// return ""
//
func (tc *ResourceConversionTestCase) createTestMethod(
subject astmodel.TypeName,
codegenContext *astmodel.CodeGenerationContext) dst.Decl {