Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module github.com/aws-controllers-k8s/code-generator
go 1.17

require (
github.com/aws-controllers-k8s/runtime v0.18.4
github.com/aws-controllers-k8s/runtime v0.19.0
github.com/aws/aws-sdk-go v1.42.0
github.com/dlclark/regexp2 v1.4.0
// pin to v0.1.1 due to release problem with v0.1.2
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/aws-controllers-k8s/runtime v0.18.4 h1:iwLYNwhbuiWZrHPoulGj75oT+alE91wCNkF1FUELiAw=
github.com/aws-controllers-k8s/runtime v0.18.4/go.mod h1:oA8ML1/LL3chPn26P6SzBNu1CUI2nekB+PTqykNs0qU=
github.com/aws-controllers-k8s/runtime v0.19.0 h1:+O5a6jBSBAd8XTNMrVCIYu4G+ZUPZe/G5eopVFO18Dc=
github.com/aws-controllers-k8s/runtime v0.19.0/go.mod h1:oA8ML1/LL3chPn26P6SzBNu1CUI2nekB+PTqykNs0qU=
github.com/aws/aws-sdk-go v1.42.0 h1:BMZws0t8NAhHFsfnT3B40IwD13jVDG5KerlRksctVIw=
github.com/aws/aws-sdk-go v1.42.0/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q=
github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM=
Expand Down
33 changes: 33 additions & 0 deletions pkg/config/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,28 @@ type ResourceConfig struct {
// IsARNPrimaryKey determines whether the CRD uses the ARN as the primary
// identifier in the ReadOne operations.
IsARNPrimaryKey bool `json:"is_arn_primary_key"`
// TagConfig contains instructions for the code generator to generate
// custom code for ensuring tags
TagConfig *TagConfig `json:"tags,omitempty"`
}

// TagConfig instructs the code generator on how to generate functions that
// ensure that controller tags are added to the AWS Resource
type TagConfig struct {
// Ignore is a boolean that indicates whether ensuring controller tags
// should be ignored for a resource. For AWS resources which do not
// support tagging, this should be set to True
Ignore bool `json:"ignore,omitempty"`
// Path represents the field path for the member which contains the tags
Path *string `json:"path,omitempty"`
// KeyMemberName is the name of field which represents AWS tag key inside tag
// struct. This is only used for tag fields with shape as list of struct,
// where the struct represents a single tag.
KeyMemberName *string `json:"key_name,omitempty"`
// ValueMemberName is the name of field which represents AWS tag value inside
// tag struct. This is only used for tag fields with shape as list of struct,
// where the struct represents a single tag.
ValueMemberName *string `json:"value_name,omitempty"`
}

// SyncedConfig instructs the code generator on how to generate functions that checks
Expand Down Expand Up @@ -625,3 +647,14 @@ func (c *Config) GetListOpMatchFieldNames(
}
return rConfig.ListOperation.MatchFields
}

// TagsAreIgnored returns whether ensuring controller tags should be ignored
// for a resource or not.
func (c *Config) TagsAreIgnored(resName string) bool {
if rConfig, found := c.Resources[resName]; found {
if tagConfig := rConfig.TagConfig; tagConfig != nil {
return tagConfig.Ignore
}
}
return false
}
11 changes: 11 additions & 0 deletions pkg/generate/ack/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,12 @@ var (
"CheckNilReferencesPath": func(f *ackmodel.Field, sourceVarName string) string {
return code.CheckNilReferencesPath(f, sourceVarName)
},
"GoCodeInitializeNestedStructField": func(r *ackmodel.CRD,
sourceVarName string, f *ackmodel.Field, apiPkgImportName string,
indentLevel int) string {
return code.InitializeNestedStructField(r, sourceVarName, f,
apiPkgImportName, indentLevel)
},
}
)

Expand Down Expand Up @@ -220,9 +226,14 @@ func Controller(
"references.go.tpl",
"resource.go.tpl",
"sdk.go.tpl",
"tags.go.tpl",
}
for _, crd := range crds {
for _, target := range targets {
// skip adding "tags.go.tpl" file if tagging is ignored for a crd
if target == "tags.go.tpl" && crd.Config().TagsAreIgnored(crd.Names.Original) {
continue
}
outPath := filepath.Join("pkg/resource", crd.Names.Snake, strings.TrimSuffix(target, ".tpl"))
tplPath := filepath.Join("pkg/resource", target)
crdVars := &templateCRDVars{
Expand Down
24 changes: 24 additions & 0 deletions pkg/generate/ack/hook.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@ code paths:
* late_initialize_post_read_one
* references_pre_resolve
* references_post_resolve
* ensure_tags
* convert_tags
* convert_tags_pre_to_ack_tags
* convert_tags_post_to_ack_tags
* convert_tags_pre_from_ack_tags
* convert_tags_post_from_ack_tags

The "pre_build_request" hooks are called BEFORE the call to construct
the Input shape that is used in the API operation and therefore BEFORE
Expand Down Expand Up @@ -119,6 +125,24 @@ method
The "references_post_resolve" hooks are called AFTER resolving the
references for all Reference fields inside AWSResourceManager.ResolveReferences()
method

The "ensure_tags" hooks provide the complete custom implementation for
AWSResourceManager.EnsureTags() method

The "convert_tags" hooks provide the complete custom implementation for
"ToACKTags" and "FromACKTags" methods.

The "convert_tags_pre_to_ack_tags" are called before converting the K8s
resource tags into ACK tags

The "convert_tags_post_to_ack_tags" are called after converting the K8s
resource tags into ACK tags

The "convert_tags_pre_from_ack_tags" are called before converting the ACK
tags into K8s resource tags

The "convert_tags_post_from_ack_tags" are called after converting the ACK
tags into K8s resource tags
*/

// ResourceHookCode returns a string with custom callback code for a resource
Expand Down
8 changes: 8 additions & 0 deletions pkg/generate/ack/runtime_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,14 @@ func (frm *fakeRM) IsSynced(context.Context, acktypes.AWSResource) (bool, error)
return true, nil
}

func (frm *fakeRM) EnsureTags(
context.Context,
acktypes.AWSResource,
acktypes.ServiceControllerMetadata,
) error {
return nil
}

// This test is mostly just a hack to introduce a Go module dependency between
// the ACK runtime library and the code generator. The code generator doesn't
// actually depend on Go code in the ACK runtime, but it *produces* templated
Expand Down
107 changes: 107 additions & 0 deletions pkg/generate/code/initialize_field.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"). You may
// not use this file except in compliance with the License. A copy of the
// License is located at
//
// http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file is distributed
// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
// express or implied. See the License for the specific language governing
// permissions and limitations under the License.

package code

import (
"fmt"
"strings"

"github.com/aws-controllers-k8s/code-generator/pkg/fieldpath"

"github.com/aws-controllers-k8s/code-generator/pkg/model"
)

// InitializeNestedStructField returns the go code for initializing a nested
// struct field. Currently this method only supports the struct shape for
// nested elements.
//
// TODO(vijtrip2): Refactor the code out of set_resource.go for generating
// constructors and reuse here. This method is currently being used for handling
// nested Tagging fields.
//
// Example: generated code for "Logging.LoggingEnabled.TargetBucket" field
// inside "s3" "bucket" crd looks like:
//
// ```
// r.ko.Spec.Logging = &svcapitypes.BucketLoggingStatus{}
// r.ko.Spec.Logging.LoggingEnabled = &svcapitypes.LoggingEnabled{}
// ```
func InitializeNestedStructField(
r *model.CRD,
sourceVarName string,
field *model.Field,
// apiPkgAlias contains the imported package alias where the type definition
// for nested structs is present.
// ex: svcapitypes "github.com/aws-controllers-k8s/s3-controller/apis/v1alpha1"
apiPkgAlias string,
// Number of levels of indentation to use
indentLevel int,
) string {
out := ""
indent := strings.Repeat("\t", indentLevel)
fieldPath := field.Path
if fieldPath != "" {
fp := fieldpath.FromString(fieldPath)
if fp.Size() > 1 {
// replace the front field name with front field shape name inside
// the field path to construct the fieldShapePath
front := fp.Front()
frontField := r.Fields[front]
if frontField == nil {
panic(fmt.Sprintf("unable to find the field with name %s"+
" for fieldpath %s", front, fieldPath))
}
if frontField.ShapeRef == nil {
panic(fmt.Sprintf("nil ShapeRef for field %s", front))
}
fieldShapePath := strings.Replace(fieldPath, front,
frontField.ShapeRef.ShapeName, 1)
fsp := fieldpath.FromString(fieldShapePath)
var index int
// Build the prefix to access elements in field path.
// Use the front of fieldpath to determine whether the field is
// a spec field or status field.
elemAccessPrefix := sourceVarName
if _, found := r.SpecFields[front]; found {
elemAccessPrefix = fmt.Sprintf("%s%s", elemAccessPrefix,
r.Config().PrefixConfig.SpecField)
} else {
elemAccessPrefix = fmt.Sprintf("%s%s", elemAccessPrefix,
r.Config().PrefixConfig.StatusField)
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

👍

var importPath string
if apiPkgAlias != "" {
importPath = fmt.Sprintf("%s.", apiPkgAlias)
}
// traverse over the fieldShapePath and initialize every element
// except the last.
for index < fsp.Size()-1 {
elemName := fp.At(index)
elemShapeRef := fsp.ShapeRefAt(frontField.ShapeRef, index)
if elemShapeRef.Shape.Type != "structure" {
panic(fmt.Sprintf("only nested structures are supported."+
" Shape type for %s is %s inside fieldpath %s", elemName,
elemShapeRef.Shape.Type, fieldPath))
}
out += fmt.Sprintf("%s%s.%s = &%s%s{}\n",
indent, elemAccessPrefix, elemName, importPath,
elemShapeRef.GoTypeElem())
elemAccessPrefix = fmt.Sprintf("%s.%s", elemAccessPrefix,
elemName)
index++
}
}
}
return out
}
30 changes: 30 additions & 0 deletions pkg/generate/code/initialize_field_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package code_test

import (
"testing"

"github.com/aws-controllers-k8s/code-generator/pkg/generate/code"

"github.com/aws-controllers-k8s/code-generator/pkg/testutil"
"github.com/stretchr/testify/assert"
)

func TestInitializeNestedStructField(t *testing.T) {
assert := assert.New(t)

g := testutil.NewModelForServiceWithOptions(t, "s3",
&testutil.TestingModelOptions{GeneratorConfigFile: "generator-with-tags.yaml"})

crd := testutil.GetCRDByName(t, g, "Bucket")
assert.NotNil(crd)

f := crd.Fields["Logging.LoggingEnabled.TargetBucket"]

s := code.InitializeNestedStructField(crd, "r.ko", f,
"svcapitypes", 1)
expected :=
` r.ko.Spec.Logging = &svcapitypes.BucketLoggingStatus{}
r.ko.Spec.Logging.LoggingEnabled = &svcapitypes.LoggingEnabled{}
`
assert.Equal(expected, s)
}
21 changes: 21 additions & 0 deletions pkg/model/model_apigwv2_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,16 @@ func TestAPIGatewayV2_Api(t *testing.T) {
crd := getCRDByName("Api", crds)
require.NotNil(crd)

assert.False(crd.Config().TagsAreIgnored(crd.Names.Original))
tfName, err := crd.GetTagFieldName()
assert.Nil(err)
assert.Equal("Tags", tfName)

tf, err := crd.GetTagField()
assert.NotNil(tf)
assert.Nil(err)
assert.Equal("Tags", tf.Names.Original)

assert.Equal("API", crd.Names.Camel)
assert.Equal("api", crd.Names.CamelLower)
assert.Equal("api", crd.Names.Snake)
Expand Down Expand Up @@ -84,6 +94,17 @@ func TestAPIGatewayV2_Route(t *testing.T) {
crd := getCRDByName("Route", crds)
require.NotNil(crd)

assert.True(crd.Config().TagsAreIgnored(crd.Names.Original))
tfName, err := crd.GetTagFieldName()
assert.NotNil(err)
assert.Empty(tfName)

tf, err := crd.GetTagField()
assert.Nil(tf)

assert.Empty(crd.GetTagKeyMemberName())
assert.Empty(crd.GetTagValueMemberName())

assert.Equal("Route", crd.Names.Camel)
assert.Equal("route", crd.Names.CamelLower)
assert.Equal("route", crd.Names.Snake)
Expand Down
14 changes: 14 additions & 0 deletions pkg/model/model_ecr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,20 @@ func TestECRRepository(t *testing.T) {
crd := getCRDByName("Repository", crds)
require.NotNil(crd)

assert.False(crd.Config().TagsAreIgnored(crd.Names.Original))
tfName, err := crd.GetTagFieldName()
assert.Nil(err)
assert.Equal("Tags", tfName)

tf, err := crd.GetTagField()
assert.NotNil(tf)
assert.Nil(err)
assert.Equal("Tags", tf.Names.Original)

tagKeyMemberName := crd.GetTagKeyMemberName()
tagValueMemberName := crd.GetTagValueMemberName()
assert.Equal("Key", tagKeyMemberName)
assert.Equal("Value", tagValueMemberName)
// The ECR Repository API has just the C and R of the normal CRUD
// operations:
//
Expand Down
Loading