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

Allow subproduct to process a single major version #9

Merged
merged 4 commits into from
Dec 8, 2023
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
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.19.5
go-version: 1.21.4

- name: Checkout Code
uses: actions/checkout@v3
Expand Down
6 changes: 3 additions & 3 deletions sdk/eula_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ func TestFetchEulaLink(t *testing.T) {
require.Nil(t, err)

var eulaUrl string
eulaUrl, err = authenticatedClient.FetchEulaUrl("VMTOOLS1126", "1073")
eulaUrl, err = authenticatedClient.FetchEulaUrl("VMTOOLS1235", "1259")
assert.Nil(t, err)
assert.NotEmpty(t, eulaUrl, "Expected eulaUrl not be empty")
}
Expand All @@ -25,7 +25,7 @@ func TestFetchEulaLinkInvalidCode(t *testing.T) {
assert.Nil(t, err)

var eulaUrl string
eulaUrl, err = authenticatedClient.FetchEulaUrl("VMTOOLS1130", "9999")
eulaUrl, err = authenticatedClient.FetchEulaUrl("VMTOOLS1235", "9999")
assert.NotNil(t, err)
assert.ErrorIs(t, err, ErrorDlgDetailsInputs)
assert.Empty(t, eulaUrl, "Expected eulaUrl be empty")
Expand All @@ -35,6 +35,6 @@ func TestAcceptEula(t *testing.T) {
err = ensureLogin(t)
require.Nil(t, err)

err = authenticatedClient.AcceptEula("VMTOOLS1126", "1073")
err = authenticatedClient.AcceptEula("VMTOOLS1235", "1259")
assert.Nil(t, err)
}
63 changes: 36 additions & 27 deletions sdk/subproduct_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,29 @@ import (

func TestGetSubProductsSlice(t *testing.T) {
var subProducts []SubProductDetails
subProducts, err = basicClient.GetSubProductsSlice("vmware_horizon", "PRODUCT_BINARY")
subProducts, err = basicClient.GetSubProductsSlice("vmware_horizon", "PRODUCT_BINARY", "")
assert.Nil(t, err)
assert.GreaterOrEqual(t, len(subProducts), 20, "Expected response to contain at least 20 items")
}

func TestGetSubProductsSliceDrivers(t *testing.T) {
var subProducts []SubProductDetails
subProducts, err = basicClient.GetSubProductsSlice("vmware_vsphere", "DRIVERS_TOOLS")
subProducts, err = basicClient.GetSubProductsSlice("vmware_vsphere", "DRIVERS_TOOLS", "")
assert.Nil(t, err)
assert.GreaterOrEqual(t, len(subProducts), 20, "Expected response to contain at least 20 items")
assert.GreaterOrEqual(t, len(subProducts), 400, "Expected response to contain at least 200 items")

// Ensure less results are returned after specifying majpor version
subProducts, err = basicClient.GetSubProductsSlice("vmware_vsphere", "DRIVERS_TOOLS", "8_0")
assert.Nil(t, err)
assert.GreaterOrEqual(t, len(subProducts), 100, "Expected response to contain at least 100 items")
assert.LessOrEqual(t, len(subProducts), 400, "Expected response to contain less than 400 items")
}

func TestGetSubProductsSliceInvalidSlug(t *testing.T) {
var subProducts []SubProductDetails
subProducts, err = basicClient.GetSubProductsSlice("vsphere", "PRODUCT_BINARY", "")
assert.ErrorIs(t, err, ErrorInvalidSlug)
assert.Empty(t, subProducts, "Expected response to be empty")
}

func TestGetSubProductNsxLE(t *testing.T) {
Expand All @@ -32,37 +45,23 @@ func TestGetSubProductNsxLE(t *testing.T) {
assert.Greater(t, len(subProduct.DlgListByVersion), 0)
}

// func TestGetSubProduct(t *testing.T) {
// var subProduct SubProductDetails
// subProduct, err = basicClient.GetSubProduct("vmware_vsphere", "dem+standard", "PRODUCT_BINARY")
// assert.Nil(t, err)
// assert.NotEmpty(t, subProduct.ProductName)
// }

func TestGetSubProductDriver(t *testing.T) {
var subProduct SubProductDetails
subProduct, err = basicClient.GetSubProduct("vmware_horizon", "dem+standard", "PRODUCT_BINARY")
assert.Nil(t, err)
assert.NotEmpty(t, subProduct.ProductName)
}

func TestGetSubProductsSliceInvalidSlug(t *testing.T) {
var subProducts []SubProductDetails
subProducts, err = basicClient.GetSubProductsSlice("vsphere", "PRODUCT_BINARY")
assert.ErrorIs(t, err, ErrorInvalidSlug)
assert.Empty(t, subProducts, "Expected response to be empty")
}

func TestGetSubProductsMap(t *testing.T) {
var subProducts map[string]SubProductDetails
subProducts, err = basicClient.GetSubProductsMap("vmware_vsphere", "PRODUCT_BINARY")
subProducts, err = basicClient.GetSubProductsMap("vmware_vsphere", "PRODUCT_BINARY", "")
assert.Nil(t, err)
assert.Contains(t, subProducts, "vmtools")
}

func TestGetSubProductsMapHorizon(t *testing.T) {
var subProducts map[string]SubProductDetails
subProducts, err = basicClient.GetSubProductsMap("vmware_horizon_clients", "PRODUCT_BINARY")
subProducts, err = basicClient.GetSubProductsMap("vmware_horizon_clients", "PRODUCT_BINARY", "")
assert.Nil(t, err)
assert.Contains(t, subProducts, "cart+win")
assert.Contains(t, subProducts, "cart+andrd_x8632")
Expand All @@ -71,23 +70,23 @@ func TestGetSubProductsMapHorizon(t *testing.T) {

func TestGetSubProductsMapNsxLe(t *testing.T) {
var subProducts map[string]SubProductDetails
subProducts, err = basicClient.GetSubProductsMap("vmware_nsx", "PRODUCT_BINARY")
subProducts, err = basicClient.GetSubProductsMap("vmware_nsx", "PRODUCT_BINARY", "")
assert.Nil(t, err)
assert.Contains(t, subProducts, "nsx")
assert.Contains(t, subProducts, "nsx_le")
}

func TestGetSubProductsMapNsxTLe(t *testing.T) {
var subProducts map[string]SubProductDetails
subProducts, err = basicClient.GetSubProductsMap("vmware_nsx_t_data_center", "PRODUCT_BINARY")
subProducts, err = basicClient.GetSubProductsMap("vmware_nsx_t_data_center", "PRODUCT_BINARY", "")
assert.Nil(t, err)
assert.Contains(t, subProducts, "nsx-t")
assert.Contains(t, subProducts, "nsx-t_le")
}

func TestGetSubProductsMapInvalidSlug(t *testing.T) {
var subProductMap map[string]SubProductDetails
subProductMap, err = basicClient.GetSubProductsMap("vsphere", "PRODUCT_BINARY")
subProductMap, err = basicClient.GetSubProductsMap("vsphere", "PRODUCT_BINARY", "" )
assert.ErrorIs(t, err, ErrorInvalidSlug)
assert.Empty(t, subProductMap, "Expected response to be empty")
}
Expand Down Expand Up @@ -128,23 +127,33 @@ func TestModifyHorizonClientCode(t *testing.T) {
func TestGetProductName(t *testing.T) {
reEndVersion := regexp.MustCompile(`[0-9]+.*`)
productName := "VMware vSphere Hypervisor (ESXi) 8.0U2"
productName = getProductName(productName, "vmware_vsphere", "", reEndVersion)
productName = getProductName(productName, "vmware_vsphere", "PRODUCT_BINARY", reEndVersion)
assert.Equal(t, "VMware vSphere Hypervisor (ESXi)", productName)

// Ensure drivers are unmodified
productName = "VMware ESXi 8.0 native ixgben ENS 1.18.2.0 NIC Driver for Intel Ethernet Controllers 82599, x520, x540, x550, and x552 family"
productName = getProductName(productName, "vmware_vsphere", "Driver CDs", reEndVersion)
assert.Equal(t, productName, "Driver - native ixgben ENS")
productName = getProductName(productName, "vmware_vsphere", "DRIVERS_TOOLS", reEndVersion)
assert.Equal(t, productName, "VMware ESXi 8.0 native ixgben ENS 1.18.2.0 NIC Driver for Intel Ethernet Controllers 82599, x520, x540, x550, and x552 family")

// Ensure drivers are unmodified
productName = "HPE Custom Image for ESXi 7.0 U3 Install CD"
productName = getProductName(productName, "vmware_vsphere", "ADDONS", reEndVersion)
assert.Equal(t, productName, "HPE Custom Image for ESXi 7.0 U3 Install CD")
}

func TestGetProductCode(t *testing.T) {
reEndVersion := regexp.MustCompile(`[0-9]+.*`)
productCode := "ESXI80U2"
productCode = getProductCode(productCode, "vmware_vsphere", "", reEndVersion)
productCode = getProductCode(productCode, "vmware_vsphere", "PRODUCT_BINARY", reEndVersion)
assert.Equal(t, "esxi", productCode)

// Ensure drivers are unmodified
productCode = "DT-ESXI80-INTEL-I40EN-2650-1OEM"
productCode = getProductCode(productCode, "vmware_vsphere", "Driver CDs", reEndVersion)
productCode = getProductCode(productCode, "vmware_vsphere", "DRIVERS_TOOLS", reEndVersion)
assert.Equal(t, "dt-esxi80-intel-i40en-2650-1oem", productCode)

// Ensure custom isos are unmodified
productCode = "OEM-ESXI70U3-HPE"
productCode = getProductCode(productCode, "vmware_vsphere", "ADDONS", reEndVersion)
assert.Equal(t, "oem-esxi70u3-hpe", productCode)
}
85 changes: 49 additions & 36 deletions sdk/subproducts.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ package sdk

import (
"errors"
"fmt"
"regexp"
"slices"
"sort"
"strings"
)
Expand All @@ -25,35 +25,53 @@ type SubProductSliceElement struct {
var ErrorInvalidSubProduct = errors.New("subproduct: invalid subproduct requested")
var ErrorInvalidSubProductMajorVersion = errors.New("subproduct: invalid major version requested")

func (c *Client) GetSubProductsMap(slug, dlgType string) (subProductMap map[string]SubProductDetails, err error) {
func (c *Client) GetSubProductsMap(slug, dlgType, requestedMajorVersion string) (subProductMap map[string]SubProductDetails, err error) {
c.EnsureProductDetailMap()
if err != nil {
return
}

if _, ok := ProductDetailMap[slug]; !ok {
err = ErrorInvalidSlug
return
}

subProductMap = make(map[string]SubProductDetails)

var majorVersions []string
majorVersions, err = c.GetMajorVersionsSlice(slug)
if err != nil {
return
}

// Iterate major product versions and extract all unique products
// All version information is stripped
for _, majorVersion := range majorVersions {
var dlgEditionsList []DlgEditionsLists
subProductMap = make(map[string]SubProductDetails)

// Only process requested major version, otherwise process all for slug
if requestedMajorVersion != "" {
if !slices.Contains(majorVersions, requestedMajorVersion) {
err = ErrorInvalidSubProductMajorVersion
return
}
err = c.processMajorVersion(slug, requestedMajorVersion, dlgType, subProductMap)
if err != nil {
return
}
} else {
// Iterate major product versions and extract all unique products
// All version information is stripped
for _, majorVersion := range majorVersions {
c.processMajorVersion(slug, majorVersion, dlgType, subProductMap)
// Invalid version errors need to be ignored, as they come from deprecated products
if err == ErrorInvalidVersion {
err = nil
} else if err != nil {
return
}
}
}
return
}

func (c *Client) processMajorVersion (slug, majorVersion, dlgType string, subProductMap map[string]SubProductDetails) (err error) {
var dlgEditionsList []DlgEditionsLists
dlgEditionsList, err = c.GetDlgEditionsList(slug, majorVersion, dlgType)
// Invalid version errors need to be ignored, as they come from deprecated products
if err == ErrorInvalidVersion {
err = nil
continue
} else if err != nil {
if err != nil {
return
}

Expand All @@ -62,8 +80,8 @@ func (c *Client) GetSubProductsMap(slug, dlgType string) (subProductMap map[stri
// Regex captures numbers and all text after
reEndVersion := regexp.MustCompile(`[0-9]+.*`)

productName := getProductName(dlgList.Name, slug, dlgEdition.Name, reEndVersion)
productCode := getProductCode(strings.ToLower(dlgList.Code), slug, dlgEdition.Name, reEndVersion)
productName := getProductName(dlgList.Name, slug, dlgType, reEndVersion)
productCode := getProductCode(strings.ToLower(dlgList.Code), slug, dlgType, reEndVersion)

// Initalize the struct for a product code for the first time
if _, ok := subProductMap[productCode]; !ok {
Expand All @@ -81,20 +99,19 @@ func (c *Client) GetSubProductsMap(slug, dlgType string) (subProductMap map[stri
}
}
}
}
return
}

func getProductCode(productCode, slug, dlgEditionName string, reEndVersion *regexp.Regexp) (string) {
func getProductCode(productCode, slug, dlgType string, reEndVersion *regexp.Regexp) (string) {
productCode = strings.ToLower(productCode)
if dlgType != "PRODUCT_BINARY" {
return productCode
}
reMidVersion := regexp.MustCompile(`(-|_)([0-9.]+)(-|_)`)
// Horizon clients don't follow a common pattern for API naming. This block aligns the pattern
if strings.HasPrefix(productCode, "cart") {
return modifyHorizonClientCode(productCode)
}
if slug == "vmware_vsphere" && dlgEditionName == "Driver CDs" {
return productCode
}
}
// Horizon clients don't follow a common pattern for API naming. This block aligns the pattern
// Check if product code has text after the version section
if ok := reMidVersion.MatchString(productCode); ok{
// replace version with + to allow for string to be split when searching
Expand All @@ -110,22 +127,19 @@ func getProductCode(productCode, slug, dlgEditionName string, reEndVersion *rege
return productCode
}

func getProductName(productName, slug, dlgEditionName string, reEndVersion *regexp.Regexp) (string) {
func getProductName(productName, slug, dlgType string, reEndVersion *regexp.Regexp) (string) {
if dlgType != "PRODUCT_BINARY" {
return productName
}
// Special case for Horizon due to inconsistent naming
if slug == "vmware_horizon" {
reNumbers := regexp.MustCompile(`[0-9.,]+`)
reSpace := regexp.MustCompile(`\s+`)
productName := strings.TrimSpace(reNumbers.ReplaceAllString(productName, ""))
return reSpace.ReplaceAllString(productName, " ")
// Special case for ESXi drivers to make human readable
} else if slug == "vmware_vsphere" && dlgEditionName == "Driver CDs" {
stripEsx := regexp.MustCompile(`VMware ESXi [0-9]+.[0-9]+ `)
productName = fmt.Sprintf("Driver - %s", stripEsx.ReplaceAllString(productName, ""))
return strings.TrimSpace(reEndVersion.ReplaceAllString(productName, ""))
} else {
return strings.TrimSpace(reEndVersion.ReplaceAllString(productName, ""))
}

}

func modifyHorizonClientCode(productCode string) (string) {
Expand Down Expand Up @@ -164,9 +178,8 @@ func duplicateNsxToNsxLe(subProductMap map[string]SubProductDetails, productCode
subProductMap[productCode + "_le"].DlgListByVersion[majorVersion] = dlgList
}


func (c *Client) GetSubProductsSlice(slug, dlgType string) (data []SubProductDetails, err error) {
subProductMap, err := c.GetSubProductsMap(slug, dlgType)
func (c *Client) GetSubProductsSlice(slug, dlgType, majorVersion string) (data []SubProductDetails, err error) {
subProductMap, err := c.GetSubProductsMap(slug, dlgType, majorVersion)
if err != nil {
return
}
Expand All @@ -190,7 +203,7 @@ func (c *Client) GetSubProductsSlice(slug, dlgType string) (data []SubProductDet

func (c *Client) GetSubProduct(slug, subProduct, dlgType string) (data SubProductDetails, err error) {
var subProductMap map[string]SubProductDetails
subProductMap, err = c.GetSubProductsMap(slug, dlgType)
subProductMap, err = c.GetSubProductsMap(slug, dlgType, "")
if err != nil {
return
}
Expand All @@ -206,7 +219,7 @@ func (c *Client) GetSubProduct(slug, subProduct, dlgType string) (data SubProduc

func (c *Client) GetSubProductDetails(slug, subProduct, majorVersion, dlgType string) (data DlgList, err error) {
var subProducts map[string]SubProductDetails
subProducts, err = c.GetSubProductsMap(slug, dlgType)
subProducts, err = c.GetSubProductsMap(slug, dlgType, "")
if err != nil {
return
}
Expand Down