Skip to content

Commit

Permalink
update for default resource tags (#335)
Browse files Browse the repository at this point in the history
Issue #, if available: aws-controllers-k8s/community#1261

Description of changes:
* Removes `service.k8s.aws/managed=true` and `services.k8s.aws/created=%UTC_NOW%` tags and adds the new `services.k8s.aws/controller-version=%CONTROLLER_VERSION%` tag as resource-tags input for ACK controller.
* Updates ACK runtime to `v0.19.0`
* Adds new `TagConfig` field inside `ResourceConfig`
* Adds the code-generation for implementation of `AWSResourceManager.EnsureTags()`

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
  • Loading branch information
vijtrip2 authored Jun 10, 2022
1 parent 6acf40f commit 160b839
Show file tree
Hide file tree
Showing 18 changed files with 556 additions and 9 deletions.
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)
}
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

0 comments on commit 160b839

Please sign in to comment.