Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
6 changes: 6 additions & 0 deletions pkg/config/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,12 @@ type UpdateOperationConfig struct {
// will leverage `delta.DifferentAt` to check whether a field have changed or not
// before including it in the update request.
OmitUnchangedFields bool `json:"omit_unchanged_fields"`
// OnlySetChangedFields instructs the code generator on how to generate logic for setting
// the value of Spec fields after a successful Update operation in the `sdkUpdate` function.
// If the boolean is true, the code generator uses `delta.DifferentAt` to check if a
// field had changed or not before setting it's value to that returned by the Update AWS API's
// response. Defaults to false.
OnlySetChangedFields bool `json:"only_set_unchanged_fields"`
}

// ReadOperationsConfig contains instructions for the code generator to handle
Expand Down
22 changes: 22 additions & 0 deletions pkg/generate/code/set_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,18 @@ func SetResource(
continue
}

onlySetChangedFieldsOnUpdate := op == r.Ops.Update && r.OnlySetChangedFieldsOnUpdate()
if onlySetChangedFieldsOnUpdate && inSpec {
fieldJSONPath := fmt.Sprintf("%s.%s", cfg.PrefixConfig.SpecField[1:], f.Names.Camel)
out += fmt.Sprintf(
"%sif delta.DifferentAt(%q) {\n", indent, fieldJSONPath,
)

// increase indentation level
indentLevel++
indent = "\t" + indent
}

sourceMemberShapeRef := outputShape.MemberRefs[memberName]
if sourceMemberShapeRef.Shape == nil {
// We may have some instructions to specially handle this field by
Expand Down Expand Up @@ -413,6 +425,16 @@ func SetResource(
} else {
indentLevel += 1
}

if onlySetChangedFieldsOnUpdate && inSpec {
// decrease indentation level
indentLevel--
indent = indent[1:]

out += fmt.Sprintf(
"%s}\n", indent,
)
}
}
return out
}
Expand Down
148 changes: 148 additions & 0 deletions pkg/generate/code/set_resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
package code_test

import (
"fmt"
"testing"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -5050,3 +5051,150 @@ func TestSetResource_WAFv2_RuleGroup_ReadOne(t *testing.T) {
code.SetResource(crd.Config(), crd, op, "resp", "ko", 1),
)
}

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

g := testutil.NewModelForServiceWithOptions(t, "mq", &testutil.TestingModelOptions{
GeneratorConfigFile: "generator-only-set-unchanged-fields.yaml",
})
op := model.OpTypeUpdate

crd := testutil.GetCRDByName(t, g, "Broker")
require.NotNil(crd)

expected := `
if delta.DifferentAt("Spec.AuthenticationStrategy") {
Copy link
Member

Choose a reason for hiding this comment

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

is this lambda comparing the latest after a ReadOne from AWS with desired from K8s? At first glance i don't think so

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is the delta passed to sdkUpdate. So, it should be the a comparison of the desired and latest.

if resp.AuthenticationStrategy != "" {
ko.Spec.AuthenticationStrategy = aws.String(string(resp.AuthenticationStrategy))
} else {
ko.Spec.AuthenticationStrategy = nil
}
}
if delta.DifferentAt("Spec.AutoMinorVersionUpgrade") {
if resp.AutoMinorVersionUpgrade != nil {
ko.Spec.AutoMinorVersionUpgrade = resp.AutoMinorVersionUpgrade
} else {
ko.Spec.AutoMinorVersionUpgrade = nil
}
}
if resp.BrokerId != nil {
ko.Status.BrokerID = resp.BrokerId
} else {
ko.Status.BrokerID = nil
}
if delta.DifferentAt("Spec.Configuration") {
if resp.Configuration != nil {
f3 := &svcapitypes.ConfigurationID{}
if resp.Configuration.Id != nil {
f3.ID = resp.Configuration.Id
}
if resp.Configuration.Revision != nil {
revisionCopy := int64(*resp.Configuration.Revision)
f3.Revision = &revisionCopy
}
ko.Spec.Configuration = f3
} else {
ko.Spec.Configuration = nil
}
}
if delta.DifferentAt("Spec.EngineVersion") {
if resp.EngineVersion != nil {
ko.Spec.EngineVersion = resp.EngineVersion
} else {
ko.Spec.EngineVersion = nil
}
}
if delta.DifferentAt("Spec.HostInstanceType") {
if resp.HostInstanceType != nil {
ko.Spec.HostInstanceType = resp.HostInstanceType
} else {
ko.Spec.HostInstanceType = nil
}
}
if delta.DifferentAt("Spec.LDAPServerMetadata") {
if resp.LdapServerMetadata != nil {
f8 := &svcapitypes.LDAPServerMetadataInput{}
if resp.LdapServerMetadata.Hosts != nil {
f8.Hosts = aws.StringSlice(resp.LdapServerMetadata.Hosts)
}
if resp.LdapServerMetadata.RoleBase != nil {
f8.RoleBase = resp.LdapServerMetadata.RoleBase
}
if resp.LdapServerMetadata.RoleName != nil {
f8.RoleName = resp.LdapServerMetadata.RoleName
}
if resp.LdapServerMetadata.RoleSearchMatching != nil {
f8.RoleSearchMatching = resp.LdapServerMetadata.RoleSearchMatching
}
if resp.LdapServerMetadata.RoleSearchSubtree != nil {
f8.RoleSearchSubtree = resp.LdapServerMetadata.RoleSearchSubtree
}
if resp.LdapServerMetadata.ServiceAccountUsername != nil {
f8.ServiceAccountUsername = resp.LdapServerMetadata.ServiceAccountUsername
}
if resp.LdapServerMetadata.UserBase != nil {
f8.UserBase = resp.LdapServerMetadata.UserBase
}
if resp.LdapServerMetadata.UserRoleName != nil {
f8.UserRoleName = resp.LdapServerMetadata.UserRoleName
}
if resp.LdapServerMetadata.UserSearchMatching != nil {
f8.UserSearchMatching = resp.LdapServerMetadata.UserSearchMatching
}
if resp.LdapServerMetadata.UserSearchSubtree != nil {
f8.UserSearchSubtree = resp.LdapServerMetadata.UserSearchSubtree
}
ko.Spec.LDAPServerMetadata = f8
} else {
ko.Spec.LDAPServerMetadata = nil
}
}
if delta.DifferentAt("Spec.Logs") {
if resp.Logs != nil {
f9 := &svcapitypes.Logs{}
if resp.Logs.Audit != nil {
f9.Audit = resp.Logs.Audit
}
if resp.Logs.General != nil {
f9.General = resp.Logs.General
}
ko.Spec.Logs = f9
} else {
ko.Spec.Logs = nil
}
}
if delta.DifferentAt("Spec.MaintenanceWindowStartTime") {
if resp.MaintenanceWindowStartTime != nil {
f10 := &svcapitypes.WeeklyStartTime{}
if resp.MaintenanceWindowStartTime.DayOfWeek != "" {
f10.DayOfWeek = aws.String(string(resp.MaintenanceWindowStartTime.DayOfWeek))
}
if resp.MaintenanceWindowStartTime.TimeOfDay != nil {
f10.TimeOfDay = resp.MaintenanceWindowStartTime.TimeOfDay
}
if resp.MaintenanceWindowStartTime.TimeZone != nil {
f10.TimeZone = resp.MaintenanceWindowStartTime.TimeZone
}
ko.Spec.MaintenanceWindowStartTime = f10
} else {
ko.Spec.MaintenanceWindowStartTime = nil
}
}
if delta.DifferentAt("Spec.SecurityGroups") {
if resp.SecurityGroups != nil {
ko.Spec.SecurityGroups = aws.StringSlice(resp.SecurityGroups)
} else {
ko.Spec.SecurityGroups = nil
}
}
`
actual := code.SetResource(crd.Config(), crd, op, "resp", "ko", 1)
fmt.Print(actual)

assert.Equal(
expected,
actual,
)
}
15 changes: 15 additions & 0 deletions pkg/model/crd.go
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,21 @@ func (r *CRD) OmitUnchangedFieldsOnUpdate() bool {
return false
}

// OnlySetChangedFieldsOnUpdate returns whether the controller needs to ensure that
// only values included in the delta are updated based on the Update operation's response.
func (r *CRD) OnlySetChangedFieldsOnUpdate() bool {
if r.Config() == nil {
return false
}
rConfig, found := r.Config().Resources[r.Names.Original]
if found {
if rConfig.UpdateOperation != nil {
return rConfig.UpdateOperation.OnlySetChangedFields
}
}
return false
}

// IsARNPrimaryKey returns true if the CRD uses its ARN as its primary key in
// ReadOne calls.
func (r *CRD) IsARNPrimaryKey() bool {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
ignore:
resources:
- Configuration
- User
field_paths:
- CreateBrokerInput.DataReplicationMode
- CreateBrokerInput.DataReplicationPrimaryBrokerArn
- User.ReplicationUser
resources:
Broker:
fields:
Users.Password:
is_secret: true
update_operation:
omit_unchanged_fields: true
only_set_unchanged_fields: true