Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

resource/aws_elasticache_global_replication_group: Support upgrading version #25077

Merged
merged 14 commits into from
May 27, 2022
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
3 changes: 3 additions & 0 deletions .changelog/25077.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
resource/aws_elasticache_global_replication_group: Add support for upgrading `engine_version`.
```
30 changes: 6 additions & 24 deletions internal/service/elasticache/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/elasticache"
"github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr"
gversion "github.com/hashicorp/go-version"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
Expand Down Expand Up @@ -535,8 +534,12 @@ func setFromCacheCluster(d *schema.ResourceData, c *elasticache.CacheCluster) er
d.Set("node_type", c.CacheNodeType)

d.Set("engine", c.Engine)
if err := setEngineVersionFromCacheCluster(d, c); err != nil {
return err
if aws.StringValue(c.Engine) == engineRedis {
if err := setEngineVersionRedis(d, c.EngineVersion); err != nil {
return err
}
} else {
setEngineVersionMemcached(d, c.EngineVersion)
}
d.Set("auto_minor_version_upgrade", strconv.FormatBool(aws.BoolValue(c.AutoMinorVersionUpgrade)))

Expand All @@ -557,27 +560,6 @@ func setFromCacheCluster(d *schema.ResourceData, c *elasticache.CacheCluster) er
return nil
}

func setEngineVersionFromCacheCluster(d *schema.ResourceData, c *elasticache.CacheCluster) error {
engineVersion, err := gversion.NewVersion(aws.StringValue(c.EngineVersion))
if err != nil {
return fmt.Errorf("error reading ElastiCache Cache Cluster (%s) engine version: %w", d.Id(), err)
}
if engineVersion.Segments()[0] < 6 {
d.Set("engine_version", engineVersion.String())
} else {
// Handle major-only version number
configVersion := d.Get("engine_version").(string)
if t, _ := regexp.MatchString(`[6-9]\.x`, configVersion); t {
d.Set("engine_version", fmt.Sprintf("%d.x", engineVersion.Segments()[0]))
} else {
d.Set("engine_version", fmt.Sprintf("%d.%d", engineVersion.Segments()[0], engineVersion.Segments()[1]))
}
}
d.Set("engine_version_actual", engineVersion.String())

return nil
}

func resourceClusterUpdate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*conns.AWSClient).ElastiCacheConn

Expand Down
88 changes: 76 additions & 12 deletions internal/service/elasticache/engine_version.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"math"
"regexp"

"github.com/aws/aws-sdk-go/aws"
multierror "github.com/hashicorp/go-multierror"
gversion "github.com/hashicorp/go-version"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
Expand Down Expand Up @@ -88,29 +89,39 @@ func customizeDiffEngineVersionForceNewOnDowngrade(_ context.Context, diff *sche
return engineVersionForceNewOnDowngrade(diff)
}

type forceNewDiffer interface {
Id() string
HasChange(key string) bool
type getChangeDiffer interface {
GetChange(key string) (interface{}, interface{})
ForceNew(key string) error
}

func engineVersionForceNewOnDowngrade(diff forceNewDiffer) error {
if diff.Id() == "" || !diff.HasChange("engine_version") {
return nil
}

func engineVersionIsDowngrade(diff getChangeDiffer) (bool, error) {
o, n := diff.GetChange("engine_version")
oVersion, err := normalizeEngineVersion(o.(string))
if err != nil {
return fmt.Errorf("error parsing old engine_version: %w", err)
return false, fmt.Errorf("error parsing old engine_version: %w", err)
}
nVersion, err := normalizeEngineVersion(n.(string))
if err != nil {
return fmt.Errorf("error parsing new engine_version: %w", err)
return false, fmt.Errorf("error parsing new engine_version: %w", err)
}

return nVersion.LessThan(oVersion), nil
}

type forceNewDiffer interface {
Id() string
GetChange(key string) (interface{}, interface{})
HasChange(key string) bool
ForceNew(key string) error
}

func engineVersionForceNewOnDowngrade(diff forceNewDiffer) error {
if diff.Id() == "" || !diff.HasChange("engine_version") {
return nil
}

if nVersion.GreaterThan(oVersion) || nVersion.Equal(oVersion) {
if downgrade, err := engineVersionIsDowngrade(diff); err != nil {
return err
} else if !downgrade {
return nil
}

Expand All @@ -130,3 +141,56 @@ func normalizeEngineVersion(version string) (*gversion.Version, error) {
}
return gversion.NewVersion(version)
}

func setEngineVersionMemcached(d *schema.ResourceData, version *string) {
d.Set("engine_version", version)
d.Set("engine_version_actual", version)
}

func setEngineVersionRedis(d *schema.ResourceData, version *string) error {
engineVersion, err := gversion.NewVersion(aws.StringValue(version))
if err != nil {
return fmt.Errorf("error reading engine version: %w", err)
}
if engineVersion.Segments()[0] < 6 {
d.Set("engine_version", engineVersion.String())
} else {
// Handle major-only version number
configVersion := d.Get("engine_version").(string)
if t, _ := regexp.MatchString(`[6-9]\.x`, configVersion); t {
d.Set("engine_version", fmt.Sprintf("%d.x", engineVersion.Segments()[0]))
} else {
d.Set("engine_version", fmt.Sprintf("%d.%d", engineVersion.Segments()[0], engineVersion.Segments()[1]))
}
}
d.Set("engine_version_actual", engineVersion.String())

return nil
}

type versionDiff [3]int

// diffVersion returns a diff of the versions, component by component.
// Only reports the first diff, since subsequent segments are unimportant for us.
func diffVersion(n, o *gversion.Version) (result versionDiff) {
if n.String() == o.String() {
return
}

segmentsNew := n.Segments64()
segmentsOld := o.Segments64()

for i := 0; i < 3; i++ {
lhs := segmentsNew[i]
rhs := segmentsOld[i]
if lhs < rhs {
result[i] = -1
break
} else if lhs > rhs {
result[i] = 1
break
}
}

return
}
Loading