Skip to content

Commit ead670c

Browse files
committed
PLT-1440: Latest version will set for data source pack
1 parent 82e3056 commit ead670c

File tree

4 files changed

+125
-7
lines changed

4 files changed

+125
-7
lines changed

docs/data-sources/pack.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ data "spectrocloud_pack" "cni" {
6161
- `name` (String) The name of the pack to search for.
6262
- `registry_uid` (String) The unique identifier (UID) of the registry where the pack is located. Specify `registry_uid` to search within a specific registry.
6363
- `type` (String) The type of pack to search for. Supported values are `helm`, `manifest`, `container`, `operator-instance`.
64-
- `version` (String) The version of the pack to search for.
64+
- `version` (String) Specifies the version of the pack to search for. If not set, the latest available version from the specified registry will be used.
6565

6666
### Read-Only
6767

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module github.com/spectrocloud/terraform-provider-spectrocloud
33
go 1.22.5
44

55
require (
6+
github.com/Masterminds/semver/v3 v3.1.1
67
github.com/go-openapi/strfmt v0.23.0
78
github.com/google/go-cmp v0.6.0
89
github.com/gorilla/mux v1.8.0
@@ -23,7 +24,6 @@ require (
2324

2425
require (
2526
github.com/Masterminds/goutils v1.1.1 // indirect
26-
github.com/Masterminds/semver/v3 v3.1.1 // indirect
2727
github.com/Masterminds/sprig/v3 v3.2.2 // indirect
2828
github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect
2929
github.com/agext/levenshtein v1.2.2 // indirect

spectrocloud/data_source_pack.go

Lines changed: 64 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ package spectrocloud
33
import (
44
"context"
55
"fmt"
6+
"github.com/Masterminds/semver/v3"
7+
"github.com/spectrocloud/gomi/pkg/ptr"
8+
"github.com/spectrocloud/palette-sdk-go/api/models"
9+
"github.com/spectrocloud/palette-sdk-go/client"
10+
"sort"
611
"strings"
712

813
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
@@ -46,7 +51,7 @@ func dataSourcePack() *schema.Resource {
4651
},
4752
"version": {
4853
Type: schema.TypeString,
49-
Description: "The version of the pack to search for.",
54+
Description: "Specifies the version of the pack to search for. If not set, the latest available version from the specified registry will be used.",
5055
Computed: true,
5156
Optional: true,
5257
},
@@ -73,6 +78,7 @@ func dataSourcePack() *schema.Resource {
7378

7479
func dataSourcePackRead(_ context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
7580
c := getV1ClientWithResourceContext(m, "")
81+
var packName = ""
7682

7783
// Warning or errors can be collected in a slice type
7884
var diags diag.Diagnostics
@@ -115,17 +121,24 @@ func dataSourcePackRead(_ context.Context, d *schema.ResourceData, m interface{}
115121
and first part would be any random name to make overall pack name unique and 2nd part is actual pack name.
116122
Thus, splitting pack name with '--' to get the correct pack name to find pack uuid
117123
*/
124+
118125
if strings.Contains(v.(string), "--") {
119126
v = strings.Split(v.(string), "--")[1]
120127
}
128+
packName = v.(string)
121129
filters = append(filters, fmt.Sprintf("spec.name=%s", v.(string)))
122130
}
123-
if v, ok := d.GetOk("version"); ok {
124-
filters = append(filters, fmt.Sprintf("spec.version=%s", v.(string)))
125-
}
126131
if v, ok := d.GetOk("registry_uid"); ok {
127132
registryUID = v.(string)
128133
}
134+
if v, ok := d.GetOk("version"); ok {
135+
filters = append(filters, fmt.Sprintf("spec.version=%s", v.(string)))
136+
} else {
137+
latestVersion := setLatestPackVersionToFilters(packName, registryUID, c)
138+
if latestVersion != "" {
139+
filters = append(filters, fmt.Sprintf("spec.version=%s", latestVersion))
140+
}
141+
}
129142
if v, ok := d.GetOk("cloud"); ok {
130143
clouds := expandStringList(v.(*schema.Set).List())
131144
if !stringContains(clouds, "all") {
@@ -140,7 +153,7 @@ func dataSourcePackRead(_ context.Context, d *schema.ResourceData, m interface{}
140153
return diag.FromErr(err)
141154
}
142155

143-
packName := "unknown"
156+
packName = "unknown"
144157
if v, ok := d.GetOk("name"); ok {
145158
packName = v.(string)
146159
}
@@ -196,3 +209,49 @@ func dataSourcePackRead(_ context.Context, d *schema.ResourceData, m interface{}
196209

197210
return diags
198211
}
212+
213+
func setLatestPackVersionToFilters(packName string, registryUID string, c *client.V1Client) string {
214+
var packLayers = []models.V1PackLayer{"addon", "csi", "cni", "os", "kernel"}
215+
var packTypes = []models.V1PackType{"spectro", "helm", "manifest", "oci"}
216+
var packAddOnTypes = []string{"load balancer", "ingress", "logging", "monitoring", "security", "authentication",
217+
"servicemesh", "system app", "app services", "registry", "csi", "cni", "integration", ""}
218+
219+
newFilter := &models.V1PackFilterSpec{
220+
Name: &models.V1FilterString{
221+
Eq: ptr.StringPtr(packName),
222+
},
223+
Type: packTypes,
224+
Layer: packLayers,
225+
Environment: []string{"all"},
226+
AddOnType: packAddOnTypes,
227+
}
228+
if registryUID != "" {
229+
newFilter.RegistryUID = []string{registryUID}
230+
}
231+
var newSort []*models.V1PackSortSpec
232+
latestVersion := ""
233+
packsResults, _ := c.SearchPacks(newFilter, newSort)
234+
if len(packsResults) == 1 {
235+
latestVersion, _ = getLatestVersion(packsResults[0].Spec.Registries)
236+
return latestVersion
237+
}
238+
return ""
239+
}
240+
241+
// getLatestVersion returns the latest version from a list of version strings.
242+
func getLatestVersion(versions []*models.V1RegistryPackMetadata) (string, error) {
243+
if len(versions) == 0 {
244+
return "", fmt.Errorf("no versions provided")
245+
}
246+
semverVersions := make([]*semver.Version, len(versions))
247+
for i, v := range versions {
248+
ver, err := semver.NewVersion(v.LatestVersion)
249+
if err != nil {
250+
return "", fmt.Errorf("invalid version %q: %w", v, err)
251+
}
252+
semverVersions[i] = ver
253+
}
254+
sort.Sort(semver.Collection(semverVersions))
255+
256+
return semverVersions[len(semverVersions)-1].String(), nil
257+
}

spectrocloud/data_source_pack_test.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package spectrocloud
33
import (
44
"context"
55
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
6+
"github.com/spectrocloud/palette-sdk-go/api/models"
67
"github.com/stretchr/testify/assert"
78
"testing"
89
)
@@ -48,3 +49,61 @@ func TestDataSourcePacksReadHelmMultiPacks(t *testing.T) {
4849
assertFirstDiagMessage(t, diags, "Multiple packs returned")
4950

5051
}
52+
53+
func TestGetLatestVersion(t *testing.T) {
54+
t.Run("valid versions", func(t *testing.T) {
55+
versions := []*models.V1RegistryPackMetadata{
56+
{LatestVersion: "v1.0.0"},
57+
{LatestVersion: "v1.2.0"},
58+
{LatestVersion: "v1.1.0"},
59+
}
60+
latest, err := getLatestVersion(versions)
61+
62+
assert.NoError(t, err, "Expected no error")
63+
assert.Equal(t, "1.2.0", latest, "The latest version should be returned")
64+
})
65+
66+
t.Run("empty versions list", func(t *testing.T) {
67+
versions := []*models.V1RegistryPackMetadata{}
68+
latest, err := getLatestVersion(versions)
69+
70+
assert.Error(t, err, "Expected an error for empty versions list")
71+
assert.Equal(t, "", latest, "No version should be returned")
72+
assert.Equal(t, "no versions provided", err.Error(), "Expected specific error message")
73+
})
74+
75+
t.Run("invalid version string", func(t *testing.T) {
76+
versions := []*models.V1RegistryPackMetadata{
77+
{LatestVersion: "1.0.0"},
78+
{LatestVersion: "invalid-version"},
79+
{LatestVersion: "1.1.0"},
80+
}
81+
latest, err := getLatestVersion(versions)
82+
83+
assert.Error(t, err, "Expected an error for invalid version string")
84+
assert.Equal(t, "", latest, "No version should be returned for invalid input")
85+
assert.Contains(t, err.Error(), "invalid version", "Error message should indicate invalid version")
86+
})
87+
88+
t.Run("single version", func(t *testing.T) {
89+
versions := []*models.V1RegistryPackMetadata{
90+
{LatestVersion: "2.0.0"},
91+
}
92+
latest, err := getLatestVersion(versions)
93+
94+
assert.NoError(t, err, "Expected no error")
95+
assert.Equal(t, "2.0.0", latest, "The single version should be returned")
96+
})
97+
98+
t.Run("pre-release versions", func(t *testing.T) {
99+
versions := []*models.V1RegistryPackMetadata{
100+
{LatestVersion: "1.0.0-alpha"},
101+
{LatestVersion: "1.0.0-beta"},
102+
{LatestVersion: "1.0.0"},
103+
}
104+
latest, err := getLatestVersion(versions)
105+
106+
assert.NoError(t, err, "Expected no error")
107+
assert.Equal(t, "1.0.0", latest, "The stable version should be returned as the latest")
108+
})
109+
}

0 commit comments

Comments
 (0)