From a9dd4c8a7b3375af0b08c4f9cd7f21cc0c5b6899 Mon Sep 17 00:00:00 2001 From: abarreiro Date: Wed, 25 Jan 2023 10:13:10 +0100 Subject: [PATCH 01/30] Change PR to only Defined Interfaces contents Signed-off-by: abarreiro --- .changes/v2.19.0/527-features.md | 3 + govcd/api_vcd_test.go | 4 +- govcd/catalog.go | 6 +- govcd/defined_interface.go | 211 +++++++++++++++++++++++++++++++ govcd/defined_interface_test.go | 75 +++++++++++ govcd/openapi_endpoints.go | 1 + types/v56/constants.go | 1 + types/v56/openapi.go | 10 ++ 8 files changed, 308 insertions(+), 3 deletions(-) create mode 100644 .changes/v2.19.0/527-features.md create mode 100644 govcd/defined_interface.go create mode 100644 govcd/defined_interface_test.go diff --git a/.changes/v2.19.0/527-features.md b/.changes/v2.19.0/527-features.md new file mode 100644 index 000000000..b9cd0c303 --- /dev/null +++ b/.changes/v2.19.0/527-features.md @@ -0,0 +1,3 @@ +* Added support for Defined Interfaces with client methods `VCDClient.CreateDefinedInterface`, `VCDClient.GetAllDefinedInterfaces`, + `VCDClient.GetDefinedInterface`, `VCDClient.GetDefinedInterfaceById` and methods to manipulate them `DefinedInterface.Update`, + `DefinedInterface.Delete` [GH-527] diff --git a/govcd/api_vcd_test.go b/govcd/api_vcd_test.go index 4da761617..32dd1dc07 100644 --- a/govcd/api_vcd_test.go +++ b/govcd/api_vcd_test.go @@ -1,5 +1,5 @@ -//go:build api || openapi || functional || catalog || vapp || gateway || network || org || query || extnetwork || task || vm || vdc || system || disk || lb || lbAppRule || lbAppProfile || lbServerPool || lbServiceMonitor || lbVirtualServer || user || search || nsxv || nsxt || auth || affinity || role || alb || certificate || vdcGroup || metadata || providervdc || ALL -// +build api openapi functional catalog vapp gateway network org query extnetwork task vm vdc system disk lb lbAppRule lbAppProfile lbServerPool lbServiceMonitor lbVirtualServer user search nsxv nsxt auth affinity role alb certificate vdcGroup metadata providervdc ALL +//go:build api || openapi || functional || catalog || vapp || gateway || network || org || query || extnetwork || task || vm || vdc || system || disk || lb || lbAppRule || lbAppProfile || lbServerPool || lbServiceMonitor || lbVirtualServer || user || search || nsxv || nsxt || auth || affinity || role || alb || certificate || vdcGroup || metadata || providervdc || rde || ALL +// +build api openapi functional catalog vapp gateway network org query extnetwork task vm vdc system disk lb lbAppRule lbAppProfile lbServerPool lbServiceMonitor lbVirtualServer user search nsxv nsxt auth affinity role alb certificate vdcGroup metadata providervdc rde ALL /* * Copyright 2022 VMware, Inc. All rights reserved. Licensed under the Apache v2 License. diff --git a/govcd/catalog.go b/govcd/catalog.go index 1d7ac0043..5f9d350e4 100644 --- a/govcd/catalog.go +++ b/govcd/catalog.go @@ -503,6 +503,10 @@ func queryVappTemplateAndVerifyTask(client *Client, vappTemplateUrl *url.URL, ne return nil, err } + if vappTemplateParsed.Tasks == nil { + return vappTemplateParsed, fmt.Errorf("the vApp Template %s does not contain tasks, an error happened during upload", vappTemplateUrl) + } + for _, task := range vappTemplateParsed.Tasks.Task { if task.Status == "error" && newItemName == task.Owner.Name { util.Logger.Printf("[Error] %#v", task.Error) @@ -787,7 +791,7 @@ func removeCatalogItemOnError(client *Client, vappTemplateLink *url.URL, itemNam if err != nil { util.Logger.Printf("[Error] Error deleting Catalog item %s: %s", vappTemplateLink, err) } - if len(vAppTemplate.Tasks.Task) > 0 { + if vAppTemplate.Tasks != nil && len(vAppTemplate.Tasks.Task) > 0 { util.Logger.Printf("[TRACE] Task found. Will try to cancel.\n") break } diff --git a/govcd/defined_interface.go b/govcd/defined_interface.go new file mode 100644 index 000000000..d3f98745e --- /dev/null +++ b/govcd/defined_interface.go @@ -0,0 +1,211 @@ +/* + * Copyright 2022 VMware, Inc. All rights reserved. Licensed under the Apache v2 License. + */ + +package govcd + +import ( + "fmt" + "github.com/vmware/go-vcloud-director/v2/types/v56" + "net/url" +) + +// DefinedInterface is a type for handling Defined Interfaces in VCD that allow to define new RDEs (DefinedEntityType). +type DefinedInterface struct { + DefinedInterface *types.DefinedInterface + client *Client +} + +// CreateDefinedInterface creates a Defined Interface. +// Only System administrator can create Defined Interfaces. +func (vcdClient *VCDClient) CreateDefinedInterface(definedInterface *types.DefinedInterface) (*DefinedInterface, error) { + client := vcdClient.Client + if !client.IsSysAdmin { + return nil, fmt.Errorf("creating Defined Interfaces requires System user") + } + + endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointInterfaces + apiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint) + if err != nil { + return nil, err + } + + urlRef, err := client.OpenApiBuildEndpoint(endpoint) + if err != nil { + return nil, err + } + + result := &DefinedInterface{ + DefinedInterface: &types.DefinedInterface{}, + client: &vcdClient.Client, + } + + err = client.OpenApiPostItem(apiVersion, urlRef, nil, definedInterface, result.DefinedInterface, nil) + if err != nil { + return nil, err + } + + return result, nil +} + +// GetAllDefinedInterfaces retrieves all Defined Interfaces. Query parameters can be supplied to perform additional filtering. +// Only System administrator can retrieve Defined Interfaces. +func (vcdClient *VCDClient) GetAllDefinedInterfaces(queryParameters url.Values) ([]*DefinedInterface, error) { + client := vcdClient.Client + if !client.IsSysAdmin { + return nil, fmt.Errorf("getting Defined Interfaces requires System user") + } + + endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointInterfaces + apiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint) + if err != nil { + return nil, err + } + + urlRef, err := client.OpenApiBuildEndpoint(endpoint) + if err != nil { + return nil, err + } + + typeResponses := []*types.DefinedInterface{{}} + err = client.OpenApiGetAllItems(apiVersion, urlRef, queryParameters, &typeResponses, nil) + if err != nil { + return nil, err + } + + // Wrap all typeResponses into DefinedEntityType types with client + returnRDEs := make([]*DefinedInterface, len(typeResponses)) + for sliceIndex := range typeResponses { + returnRDEs[sliceIndex] = &DefinedInterface{ + DefinedInterface: typeResponses[sliceIndex], + client: &vcdClient.Client, + } + } + + return returnRDEs, nil +} + +// GetDefinedInterface retrieves a single Defined Interface defined by its unique combination of vendor, namespace and version. +// Only System administrator can retrieve Defined Interfaces. +func (vcdClient *VCDClient) GetDefinedInterface(vendor, namespace, version string) (*DefinedInterface, error) { + client := vcdClient.Client + if !client.IsSysAdmin { + return nil, fmt.Errorf("getting Defined Interfaces requires System user") + } + + queryParameters := url.Values{} + queryParameters.Add("filter", fmt.Sprintf("vendor==%s;nss==%s;version==%s", vendor, namespace, version)) + interfaces, err := vcdClient.GetAllDefinedInterfaces(queryParameters) + if err != nil { + return nil, err + } + + if len(interfaces) == 0 { + return nil, fmt.Errorf("%s could not find the Defined Interface with vendor %s, namespace %s and version %s", ErrorEntityNotFound, vendor, namespace, version) + } + + if len(interfaces) > 1 { + return nil, fmt.Errorf("found more than 1 Defined Interface with vendor %s, namespace %s and version %s", vendor, namespace, version) + } + + return interfaces[0], nil +} + +// GetDefinedInterfaceById gets a Defined Interface identified by its unique URN. +// Only System administrator can retrieve Defined Interfaces. +func (vcdClient *VCDClient) GetDefinedInterfaceById(id string) (*DefinedInterface, error) { + client := vcdClient.Client + if !client.IsSysAdmin { + return nil, fmt.Errorf("getting Defined Interfaces requires System user") + } + + endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointInterfaces + apiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint) + if err != nil { + return nil, err + } + + urlRef, err := client.OpenApiBuildEndpoint(endpoint, id) + if err != nil { + return nil, err + } + + result := &DefinedInterface{ + DefinedInterface: &types.DefinedInterface{}, + client: &vcdClient.Client, + } + + err = client.OpenApiGetItem(apiVersion, urlRef, nil, result.DefinedInterface, nil) + if err != nil { + return nil, err + } + + return result, nil +} + +// Update updates the receiver Defined Interface with the values given by the input. +// Only System administrator can update Defined Interfaces. +func (di *DefinedInterface) Update(definedInterface types.DefinedInterface) error { + client := di.client + if !client.IsSysAdmin { + return fmt.Errorf("updating Defined Interfaces requires System user") + } + + if di.DefinedInterface.ID == "" { + return fmt.Errorf("ID of the receiver Defined Interface is empty") + } + + if definedInterface.ID != "" && definedInterface.ID != di.DefinedInterface.ID { + return fmt.Errorf("ID of the receiver Defined Interface and the input ID don't match") + } + + endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointInterfaces + apiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint) + if err != nil { + return err + } + + urlRef, err := client.OpenApiBuildEndpoint(endpoint, di.DefinedInterface.ID) + if err != nil { + return err + } + + err = client.OpenApiPutItem(apiVersion, urlRef, nil, definedInterface, di.DefinedInterface, nil) + if err != nil { + return err + } + + return nil +} + +// Delete deletes the receiver Defined Interface. +// Only System administrator can delete Defined Interfaces. +func (di *DefinedInterface) Delete() error { + client := di.client + if !client.IsSysAdmin { + return fmt.Errorf("deleting Defined Interfaces requires System user") + } + + if di.DefinedInterface.ID == "" { + return fmt.Errorf("ID of the receiver Defined Interface is empty") + } + + endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointInterfaces + apiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint) + if err != nil { + return err + } + + urlRef, err := client.OpenApiBuildEndpoint(endpoint, di.DefinedInterface.ID) + if err != nil { + return err + } + + err = client.OpenApiDeleteItem(apiVersion, urlRef, nil, nil) + if err != nil { + return err + } + + di.DefinedInterface = &types.DefinedInterface{} + return nil +} diff --git a/govcd/defined_interface_test.go b/govcd/defined_interface_test.go new file mode 100644 index 000000000..97d5deb40 --- /dev/null +++ b/govcd/defined_interface_test.go @@ -0,0 +1,75 @@ +//go:build functional || openapi || rde || ALL +// +build functional openapi rde ALL + +/* + * Copyright 2022 VMware, Inc. All rights reserved. Licensed under the Apache v2 License. + */ + +package govcd + +import ( + "fmt" + "github.com/vmware/go-vcloud-director/v2/types/v56" + . "gopkg.in/check.v1" + "strings" +) + +// Test_DefinedInterface tests the CRUD behavior of Defined Interfaces. First, it checks how many exist already, +// as VCD contains some pre-defined ones. Then we create a new one, so the number of existing ones should be increased by 1. +// We try to get this new created interface with the available getter methods and then perform an update. +// As a final step, we delete it and check that the deletion is correct (the receiver object is empty and doesn't exist in VCD). +func (vcd *TestVCD) Test_DefinedInterface(check *C) { + if vcd.skipAdminTests { + check.Skip(fmt.Sprintf(TestRequiresSysAdminPrivileges, check.TestName())) + } + endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointInterfaces + skipOpenApiEndpointTest(vcd, check, endpoint) + + allDefinedInterfaces, err := vcd.client.GetAllDefinedInterfaces(nil) + check.Assert(err, IsNil) + alreadyPresentRDEs := len(allDefinedInterfaces) + + dummyRde := &types.DefinedInterface{ + Name: check.TestName() + "_name", + Namespace: check.TestName() + "_nss", + Version: "1.2.3", + Vendor: "vmware", + } + newDefinedInterface, err := vcd.client.CreateDefinedInterface(dummyRde) + check.Assert(err, IsNil) + check.Assert(newDefinedInterface, NotNil) + check.Assert(newDefinedInterface.DefinedInterface.Name, Equals, dummyRde.Name) + check.Assert(newDefinedInterface.DefinedInterface.Namespace, Equals, dummyRde.Namespace) + check.Assert(newDefinedInterface.DefinedInterface.Version, Equals, dummyRde.Version) + check.Assert(newDefinedInterface.DefinedInterface.Vendor, Equals, dummyRde.Vendor) + check.Assert(newDefinedInterface.DefinedInterface.IsReadOnly, Equals, dummyRde.IsReadOnly) + + AddToCleanupListOpenApi(newDefinedInterface.DefinedInterface.ID, check.TestName(), types.OpenApiPathVersion1_0_0+types.OpenApiEndpointInterfaces+newDefinedInterface.DefinedInterface.ID) + + allDefinedInterfaces, err = vcd.client.GetAllDefinedInterfaces(nil) + check.Assert(err, IsNil) + check.Assert(len(allDefinedInterfaces), Equals, alreadyPresentRDEs+1) + + obtainedDefinedInterface, err := vcd.client.GetDefinedInterfaceById(newDefinedInterface.DefinedInterface.ID) + check.Assert(err, IsNil) + check.Assert(*obtainedDefinedInterface.DefinedInterface, DeepEquals, *newDefinedInterface.DefinedInterface) + + obtainedDefinedInterface2, err := vcd.client.GetDefinedInterface(obtainedDefinedInterface.DefinedInterface.Vendor, obtainedDefinedInterface.DefinedInterface.Namespace, obtainedDefinedInterface.DefinedInterface.Version) + check.Assert(err, IsNil) + check.Assert(*obtainedDefinedInterface2.DefinedInterface, DeepEquals, *obtainedDefinedInterface.DefinedInterface) + + err = newDefinedInterface.Update(types.DefinedInterface{ + Name: dummyRde.Name + "2", // Only name can be updated + }) + check.Assert(err, IsNil) + check.Assert(newDefinedInterface.DefinedInterface.Name, Equals, dummyRde.Name+"2") + + deletedId := newDefinedInterface.DefinedInterface.ID + err = newDefinedInterface.Delete() + check.Assert(err, IsNil) + check.Assert(*newDefinedInterface.DefinedInterface, DeepEquals, types.DefinedInterface{}) + + _, err = vcd.client.GetDefinedInterfaceById(deletedId) + check.Assert(err, NotNil) + check.Assert(strings.Contains(err.Error(), ErrorEntityNotFound.Error()), Equals, true) +} diff --git a/govcd/openapi_endpoints.go b/govcd/openapi_endpoints.go index be84235ce..5bb47b70f 100644 --- a/govcd/openapi_endpoints.go +++ b/govcd/openapi_endpoints.go @@ -54,6 +54,7 @@ var endpointMinApiVersions = map[string]string{ types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointSecurityTags: "36.0", // VCD 10.3+ types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointNsxtRouteAdvertisement: "34.0", // VCD 10.1+ types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointLogicalVmGroups: "35.0", // VCD 10.2+ + types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointInterfaces: "35.0", // VCD 10.2+ // NSX-T ALB (Advanced/AVI Load Balancer) support was introduced in 10.2 types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointAlbController: "35.0", // VCD 10.2+ diff --git a/types/v56/constants.go b/types/v56/constants.go index 1af088cdd..23c2efa5e 100644 --- a/types/v56/constants.go +++ b/types/v56/constants.go @@ -389,6 +389,7 @@ const ( OpenApiEndpointEdgeBgpNeighbor = "edgeGateways/%s/routing/bgp/neighbors/" // '%s' is NSX-T Edge Gateway ID OpenApiEndpointEdgeBgpConfigPrefixLists = "edgeGateways/%s/routing/bgp/prefixLists/" // '%s' is NSX-T Edge Gateway ID OpenApiEndpointEdgeBgpConfig = "edgeGateways/%s/routing/bgp" // '%s' is NSX-T Edge Gateway ID + OpenApiEndpointInterfaces = "interfaces/" // NSX-T ALB related endpoints diff --git a/types/v56/openapi.go b/types/v56/openapi.go index 2ac05c705..4fab73f98 100644 --- a/types/v56/openapi.go +++ b/types/v56/openapi.go @@ -426,3 +426,13 @@ type LogicalVmGroup struct { NamedVmGroupReferences OpenApiReferences `json:"namedVmGroupReferences,omitempty"` // List of named VM Groups associated with LogicalVmGroup. PvdcID string `json:"pvdcId,omitempty"` // URN for Provider VDC } + +// DefinedInterface defines a interface for a defined entity. The combination of nss+version+vendor should be unique +type DefinedInterface struct { + ID string `json:"id,omitempty"` // The id of the defined interface type in URN format + Name string `json:"name,omitempty"` // The name of the defined interface + Namespace string `json:"nss,omitempty"` // A unique namespace associated with the interface + Version string `json:"version,omitempty"` // The interface's version. The version should follow semantic versioning rules + Vendor string `json:"vendor,omitempty"` // The vendor name + IsReadOnly bool `json:"readonly,omitempty"` // True if the entity type cannot be modified +} From cfdd924a07aaa99d5c74da61c39ccfb8fba2131b Mon Sep 17 00:00:00 2001 From: abarreiro Date: Wed, 25 Jan 2023 10:22:53 +0100 Subject: [PATCH 02/30] # Signed-off-by: abarreiro --- govcd/catalog.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/govcd/catalog.go b/govcd/catalog.go index 5f9d350e4..7479f4916 100644 --- a/govcd/catalog.go +++ b/govcd/catalog.go @@ -791,6 +791,10 @@ func removeCatalogItemOnError(client *Client, vappTemplateLink *url.URL, itemNam if err != nil { util.Logger.Printf("[Error] Error deleting Catalog item %s: %s", vappTemplateLink, err) } + if vAppTemplate.Tasks == nil { + util.Logger.Printf("[Error] Error deleting Catalog item %s: it doesn't contain any task", vappTemplateLink) + return + } if vAppTemplate.Tasks != nil && len(vAppTemplate.Tasks.Task) > 0 { util.Logger.Printf("[TRACE] Task found. Will try to cancel.\n") break From 18d17c16b8ed3391f13b284af15ee185e5cdd8c0 Mon Sep 17 00:00:00 2001 From: abarreiro Date: Wed, 25 Jan 2023 10:31:29 +0100 Subject: [PATCH 03/30] Fix tests Signed-off-by: abarreiro --- govcd/common_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/govcd/common_test.go b/govcd/common_test.go index 81a99a15a..af03f70a5 100644 --- a/govcd/common_test.go +++ b/govcd/common_test.go @@ -1,5 +1,5 @@ -//go:build api || auth || functional || catalog || vapp || gateway || network || org || query || extnetwork || task || vm || vdc || system || disk || lb || lbAppRule || lbAppProfile || lbServerPool || lbServiceMonitor || lbVirtualServer || user || role || nsxv || nsxt || openapi || affinity || search || alb || certificate || vdcGroup || metadata || providervdc || ALL -// +build api auth functional catalog vapp gateway network org query extnetwork task vm vdc system disk lb lbAppRule lbAppProfile lbServerPool lbServiceMonitor lbVirtualServer user role nsxv nsxt openapi affinity search alb certificate vdcGroup metadata providervdc ALL +//go:build api || auth || functional || catalog || vapp || gateway || network || org || query || extnetwork || task || vm || vdc || system || disk || lb || lbAppRule || lbAppProfile || lbServerPool || lbServiceMonitor || lbVirtualServer || user || role || nsxv || nsxt || openapi || affinity || search || alb || certificate || vdcGroup || metadata || providervdc || rde || ALL +// +build api auth functional catalog vapp gateway network org query extnetwork task vm vdc system disk lb lbAppRule lbAppProfile lbServerPool lbServiceMonitor lbVirtualServer user role nsxv nsxt openapi affinity search alb certificate vdcGroup metadata providervdc rde ALL /* * Copyright 2021 VMware, Inc. All rights reserved. Licensed under the Apache v2 License. From e67558aafc0cb2e112673e39030738db6f1003f9 Mon Sep 17 00:00:00 2001 From: abarreiro Date: Wed, 25 Jan 2023 10:50:40 +0100 Subject: [PATCH 04/30] Changelog Signed-off-by: abarreiro --- .changes/{v2.19.0 => v2.20.0}/527-features.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .changes/{v2.19.0 => v2.20.0}/527-features.md (100%) diff --git a/.changes/v2.19.0/527-features.md b/.changes/v2.20.0/527-features.md similarity index 100% rename from .changes/v2.19.0/527-features.md rename to .changes/v2.20.0/527-features.md From 900eb866bb700d9a9c38458ad425952f848c94ee Mon Sep 17 00:00:00 2001 From: abarreiro Date: Wed, 25 Jan 2023 10:59:52 +0100 Subject: [PATCH 05/30] Init Signed-off-by: abarreiro --- .changes/v2.20.0/xxx-features.md | 3 + govcd/defined_entity.go | 220 +++++++++++++++++++++++++++++++ govcd/defined_entity_test.go | 118 +++++++++++++++++ govcd/openapi_endpoints.go | 1 + test-resources/rde_type.json | 41 ++++++ types/v56/constants.go | 1 + types/v56/openapi.go | 16 +++ 7 files changed, 400 insertions(+) create mode 100644 .changes/v2.20.0/xxx-features.md create mode 100644 govcd/defined_entity.go create mode 100644 govcd/defined_entity_test.go create mode 100644 test-resources/rde_type.json diff --git a/.changes/v2.20.0/xxx-features.md b/.changes/v2.20.0/xxx-features.md new file mode 100644 index 000000000..26f853ca0 --- /dev/null +++ b/.changes/v2.20.0/xxx-features.md @@ -0,0 +1,3 @@ +* Added support for Runtime Defined Entity types with client methods `VCDClient.CreateRdeType`, `VCDClient.GetAllRdeTypes`, + `VCDClient.GetRdeType`, `VCDClient.GetRdeTypeById` and methods to manipulate them `DefinedEntityType.Update`, + `DefinedEntityType.Delete` [GH-xxx] diff --git a/govcd/defined_entity.go b/govcd/defined_entity.go new file mode 100644 index 000000000..e56b85385 --- /dev/null +++ b/govcd/defined_entity.go @@ -0,0 +1,220 @@ +/* + * Copyright 2022 VMware, Inc. All rights reserved. Licensed under the Apache v2 License. + */ + +package govcd + +import ( + "fmt" + "github.com/vmware/go-vcloud-director/v2/types/v56" + "net/url" +) + +// DefinedEntityType is a type for handling Runtime Defined Entity (RDE) type definitions +type DefinedEntityType struct { + DefinedEntityType *types.DefinedEntityType + client *Client +} + +// CreateRdeType creates a Runtime Defined Entity type. +// Only System administrator can create RDE types. +func (vcdClient *VCDClient) CreateRdeType(rde *types.DefinedEntityType) (*DefinedEntityType, error) { + client := vcdClient.Client + if !client.IsSysAdmin { + return nil, fmt.Errorf("creating Runtime Defined Entity types requires System user") + } + + endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEntityTypes + apiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint) + if err != nil { + return nil, err + } + + urlRef, err := client.OpenApiBuildEndpoint(endpoint) + if err != nil { + return nil, err + } + + result := &DefinedEntityType{ + DefinedEntityType: &types.DefinedEntityType{}, + client: &vcdClient.Client, + } + + err = client.OpenApiPostItem(apiVersion, urlRef, nil, rde, result.DefinedEntityType, nil) + if err != nil { + return nil, err + } + + return result, nil +} + +// GetAllRdeTypes retrieves all Runtime Defined Entity types. Query parameters can be supplied to perform additional filtering. +// Only System administrator can retrieve RDE types. +func (vcdClient *VCDClient) GetAllRdeTypes(queryParameters url.Values) ([]*DefinedEntityType, error) { + client := vcdClient.Client + if !client.IsSysAdmin { + return nil, fmt.Errorf("getting Runtime Defined Entity types requires System user") + } + + endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEntityTypes + apiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint) + if err != nil { + return nil, err + } + + urlRef, err := client.OpenApiBuildEndpoint(endpoint) + if err != nil { + return nil, err + } + + typeResponses := []*types.DefinedEntityType{{}} + err = client.OpenApiGetAllItems(apiVersion, urlRef, queryParameters, &typeResponses, nil) + if err != nil { + return nil, err + } + + // Wrap all typeResponses into DefinedEntityType types with client + returnRDEs := make([]*DefinedEntityType, len(typeResponses)) + for sliceIndex := range typeResponses { + returnRDEs[sliceIndex] = &DefinedEntityType{ + DefinedEntityType: typeResponses[sliceIndex], + client: &vcdClient.Client, + } + } + + return returnRDEs, nil +} + +// GetRdeType gets a Runtime Defined Entity type by its unique combination of vendor, namespace and version. +// Only System administrator can retrieve RDE types. +func (vcdClient *VCDClient) GetRdeType(vendor, namespace, version string) (*DefinedEntityType, error) { + client := vcdClient.Client + if !client.IsSysAdmin { + return nil, fmt.Errorf("getting Runtime Defined Entity types requires System user") + } + + queryParameters := url.Values{} + queryParameters.Add("filter", fmt.Sprintf("vendor==%s;nss==%s;version==%s", vendor, namespace, version)) + rdeTypes, err := vcdClient.GetAllRdeTypes(queryParameters) + if err != nil { + return nil, err + } + + if len(rdeTypes) == 0 { + return nil, fmt.Errorf("%s could not find the Runtime Defined Entity type with vendor %s, namespace %s and version %s", ErrorEntityNotFound, vendor, namespace, version) + } + + if len(rdeTypes) > 1 { + return nil, fmt.Errorf("found more than 1 Runtime Defined Entity type with vendor %s, namespace %s and version %s", vendor, namespace, version) + } + + return rdeTypes[0], nil +} + +// GetRdeTypeById gets a Runtime Defined Entity type by its ID +// Only System administrator can retrieve RDE types. +func (vcdClient *VCDClient) GetRdeTypeById(id string) (*DefinedEntityType, error) { + client := vcdClient.Client + if !client.IsSysAdmin { + return nil, fmt.Errorf("getting Runtime Defined Entity types requires System user") + } + + endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEntityTypes + apiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint) + if err != nil { + return nil, err + } + + urlRef, err := client.OpenApiBuildEndpoint(endpoint, id) + if err != nil { + return nil, err + } + + result := &DefinedEntityType{ + DefinedEntityType: &types.DefinedEntityType{}, + client: &vcdClient.Client, + } + + err = client.OpenApiGetItem(apiVersion, urlRef, nil, result.DefinedEntityType, nil) + if err != nil { + return nil, err + } + + return result, nil +} + +// Update updates the receiver Runtime Defined Entity type with the values given by the input. +// Only System administrator can update RDE types. +func (rdeType *DefinedEntityType) Update(rdeTypeToUpdate types.DefinedEntityType) error { + client := rdeType.client + if !client.IsSysAdmin { + return fmt.Errorf("updating Runtime Defined Entity types requires System user") + } + + if rdeType.DefinedEntityType.ID == "" { + return fmt.Errorf("ID of the receiver Runtime Defined Entity type is empty") + } + + if rdeTypeToUpdate.ID != "" && rdeTypeToUpdate.ID != rdeType.DefinedEntityType.ID { + return fmt.Errorf("ID of the receiver Runtime Defined Entity and the input ID don't match") + } + + // Name and schema are mandatory, despite we don't want to update them, so we populate them in this situation to avoid errors + // and make this method more user friendly. + if rdeTypeToUpdate.Name == "" { + rdeTypeToUpdate.Name = rdeType.DefinedEntityType.Name + } + if rdeTypeToUpdate.Schema == nil || len(rdeTypeToUpdate.Schema) == 0 { + rdeTypeToUpdate.Schema = rdeType.DefinedEntityType.Schema + } + + endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEntityTypes + apiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint) + if err != nil { + return err + } + + urlRef, err := client.OpenApiBuildEndpoint(endpoint, rdeType.DefinedEntityType.ID) + if err != nil { + return err + } + + err = client.OpenApiPutItem(apiVersion, urlRef, nil, rdeTypeToUpdate, rdeType.DefinedEntityType, nil) + if err != nil { + return err + } + + return nil +} + +// Delete deletes the receiver Runtime Defined Entity type. +// Only System administrator can delete RDE types. +func (rdeType *DefinedEntityType) Delete() error { + client := rdeType.client + if !client.IsSysAdmin { + return fmt.Errorf("deleting Runtime Defined Entity types requires System user") + } + + if rdeType.DefinedEntityType.ID == "" { + return fmt.Errorf("ID of the receiver Runtime Defined Entity type is empty") + } + + endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEntityTypes + apiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint) + if err != nil { + return err + } + + urlRef, err := client.OpenApiBuildEndpoint(endpoint, rdeType.DefinedEntityType.ID) + if err != nil { + return err + } + + err = client.OpenApiDeleteItem(apiVersion, urlRef, nil, nil) + if err != nil { + return err + } + + rdeType.DefinedEntityType = &types.DefinedEntityType{} + return nil +} diff --git a/govcd/defined_entity_test.go b/govcd/defined_entity_test.go new file mode 100644 index 000000000..0199bb1ca --- /dev/null +++ b/govcd/defined_entity_test.go @@ -0,0 +1,118 @@ +//go:build functional || openapi || rde || ALL +// +build functional openapi rde ALL + +/* + * Copyright 2022 VMware, Inc. All rights reserved. Licensed under the Apache v2 License. + */ + +package govcd + +import ( + "encoding/json" + "fmt" + "github.com/vmware/go-vcloud-director/v2/types/v56" + . "gopkg.in/check.v1" + "io" + "os" + "path/filepath" + "strings" +) + +// Test_Rde tests the complete journey of RDE type and RDE instance with creation, reads, updates and finally deletion. +func (vcd *TestVCD) Test_Rde(check *C) { + if vcd.skipAdminTests { + check.Skip(fmt.Sprintf(TestRequiresSysAdminPrivileges, check.TestName())) + } + + skipOpenApiEndpointTest(vcd, check, types.OpenApiPathVersion1_0_0+types.OpenApiEndpointEntityTypes) + + unmarshaledRdeTypeSchema, err := loadRdeTypeSchemaFromTestResources() + check.Assert(err, IsNil) + check.Assert(true, Equals, len(unmarshaledRdeTypeSchema) > 0) + + rdeTypeToCreate := &types.DefinedEntityType{ + Name: check.TestName(), + Namespace: "namespace14", // Can't put check.TestName() due to a bug that causes RDEs to fail on GET once created with special characters like "." + Version: "1.2.3", + Description: "Description of " + check.TestName(), + Schema: unmarshaledRdeTypeSchema, + Vendor: "vmware", + Interfaces: []string{"urn:vcloud:interface:vmware:k8s:1.0.0"}, + } + + allRdeTypes, err := vcd.client.GetAllRdeTypes(nil) + check.Assert(err, IsNil) + alreadyPresentRdes := len(allRdeTypes) + + createdRdeType, err := vcd.client.CreateRdeType(rdeTypeToCreate) + check.Assert(err, IsNil) + check.Assert(createdRdeType, NotNil) + check.Assert(createdRdeType.DefinedEntityType.Name, Equals, rdeTypeToCreate.Name) + check.Assert(createdRdeType.DefinedEntityType.Namespace, Equals, rdeTypeToCreate.Namespace) + check.Assert(createdRdeType.DefinedEntityType.Version, Equals, rdeTypeToCreate.Version) + check.Assert(createdRdeType.DefinedEntityType.Schema, NotNil) + check.Assert(createdRdeType.DefinedEntityType.Schema["type"], Equals, "object") + check.Assert(createdRdeType.DefinedEntityType.Schema["definitions"], NotNil) + check.Assert(createdRdeType.DefinedEntityType.Schema["required"], NotNil) + check.Assert(createdRdeType.DefinedEntityType.Schema["properties"], NotNil) + + AddToCleanupListOpenApi(createdRdeType.DefinedEntityType.ID, check.TestName(), types.OpenApiPathVersion1_0_0+types.OpenApiEndpointEntityTypes+createdRdeType.DefinedEntityType.ID) + + allRdeTypes, err = vcd.client.GetAllRdeTypes(nil) + check.Assert(err, IsNil) + check.Assert(len(allRdeTypes), Equals, alreadyPresentRdes+1) + + obtainedRdeType, err := vcd.client.GetRdeTypeById(createdRdeType.DefinedEntityType.ID) + check.Assert(err, IsNil) + check.Assert(*obtainedRdeType.DefinedEntityType, DeepEquals, *createdRdeType.DefinedEntityType) + + obtainedRdeType2, err := vcd.client.GetRdeType(obtainedRdeType.DefinedEntityType.Vendor, obtainedRdeType.DefinedEntityType.Namespace, obtainedRdeType.DefinedEntityType.Version) + check.Assert(err, IsNil) + check.Assert(*obtainedRdeType2.DefinedEntityType, DeepEquals, *obtainedRdeType.DefinedEntityType) + + // We don't want to update the name nor the schema. It should populate them from the receiver object automatically + err = obtainedRdeType.Update(types.DefinedEntityType{ + Description: obtainedRdeType.DefinedEntityType.Description + "Updated", + }) + check.Assert(err, IsNil) + check.Assert(obtainedRdeType.DefinedEntityType.Description, Equals, rdeTypeToCreate.Description+"Updated") + + deletedId := createdRdeType.DefinedEntityType.ID + err = createdRdeType.Delete() + check.Assert(err, IsNil) + check.Assert(*createdRdeType.DefinedEntityType, DeepEquals, types.DefinedEntityType{}) + + _, err = vcd.client.GetRdeTypeById(deletedId) + check.Assert(err, NotNil) + check.Assert(strings.Contains(err.Error(), ErrorEntityNotFound.Error()), Equals, true) +} + +// loadRdeTypeSchemaFromTestResources loads the RDE schema present in the test-resources folder and unmarshals it +// into a map. Returns an error if something fails along the way. +func loadRdeTypeSchemaFromTestResources() (map[string]interface{}, error) { + // Load the RDE type schema + rdeFilePath := "../test-resources/rde_type.json" + _, err := os.Stat(rdeFilePath) + if os.IsNotExist(err) { + return nil, fmt.Errorf("unable to find RDE type file '%s': %s", rdeFilePath, err) + } + + rdeFile, err := os.OpenFile(filepath.Clean(rdeFilePath), os.O_RDONLY, 0400) + if err != nil { + return nil, fmt.Errorf("unable to open RDE type file '%s': %s", rdeFilePath, err) + } + defer safeClose(rdeFile) + + rdeSchema, err := io.ReadAll(rdeFile) + if err != nil { + return nil, fmt.Errorf("error reading RDE type file %s: %s", rdeFilePath, err) + } + + var unmarshaledJson map[string]interface{} + err = json.Unmarshal(rdeSchema, &unmarshaledJson) + if err != nil { + return nil, fmt.Errorf("could not unmarshal RDE type file %s: %s", rdeFilePath, err) + } + + return unmarshaledJson, nil +} diff --git a/govcd/openapi_endpoints.go b/govcd/openapi_endpoints.go index 5bb47b70f..286d0c623 100644 --- a/govcd/openapi_endpoints.go +++ b/govcd/openapi_endpoints.go @@ -55,6 +55,7 @@ var endpointMinApiVersions = map[string]string{ types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointNsxtRouteAdvertisement: "34.0", // VCD 10.1+ types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointLogicalVmGroups: "35.0", // VCD 10.2+ types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointInterfaces: "35.0", // VCD 10.2+ + types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEntityTypes: "35.0", // VCD 10.2+ // NSX-T ALB (Advanced/AVI Load Balancer) support was introduced in 10.2 types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointAlbController: "35.0", // VCD 10.2+ diff --git a/test-resources/rde_type.json b/test-resources/rde_type.json new file mode 100644 index 000000000..91775659e --- /dev/null +++ b/test-resources/rde_type.json @@ -0,0 +1,41 @@ +{ + "definitions": { + "foo": { + "description": "Foo definition", + "properties": { + "key": { + "description": "Key for foo", + "type": "string" + } + }, + "type": "object" + } + }, + "properties": { + "bar": { + "description": "Bar", + "type": "string" + }, + "foo": { + "$ref": "#/definitions/foo" + }, + "prop2": { + "properties": { + "subprop1": { + "type": "string" + }, + "subprop2": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + } + }, + "required": [ + "foo" + ], + "type": "object" +} diff --git a/types/v56/constants.go b/types/v56/constants.go index 23c2efa5e..015cbdad8 100644 --- a/types/v56/constants.go +++ b/types/v56/constants.go @@ -390,6 +390,7 @@ const ( OpenApiEndpointEdgeBgpConfigPrefixLists = "edgeGateways/%s/routing/bgp/prefixLists/" // '%s' is NSX-T Edge Gateway ID OpenApiEndpointEdgeBgpConfig = "edgeGateways/%s/routing/bgp" // '%s' is NSX-T Edge Gateway ID OpenApiEndpointInterfaces = "interfaces/" + OpenApiEndpointEntityTypes = "entityTypes/" // NSX-T ALB related endpoints diff --git a/types/v56/openapi.go b/types/v56/openapi.go index 4fab73f98..28bffa4f8 100644 --- a/types/v56/openapi.go +++ b/types/v56/openapi.go @@ -436,3 +436,19 @@ type DefinedInterface struct { Vendor string `json:"vendor,omitempty"` // The vendor name IsReadOnly bool `json:"readonly,omitempty"` // True if the entity type cannot be modified } + +// DefinedEntityType describes what a defined entity type should look like. +type DefinedEntityType struct { + ID string `json:"id,omitempty"` // The id of the defined entity type in URN format + Name string `json:"name,omitempty"` // The name of the defined entity type + Namespace string `json:"nss,omitempty"` // A unique namespace specific string. The combination of nss and version must be unique + Version string `json:"version,omitempty"` // The version of the defined entity. The combination of nss and version must be unique. The version string must follow semantic versioning rules + Description string `json:"description,omitempty"` // Description of the defined entity + ExternalId string `json:"externalId,omitempty"` // An external entity’s id that this definition may apply to + Hooks map[string]string `json:"hooks,omitempty"` // A mapping defining which behaviors should be invoked upon specific lifecycle events, like PostCreate, PostUpdate, PreDelete. For example: "hooks": { "PostCreate": "urn:vcloud:behavior-interface:postCreateHook:vendorA:containerCluster:1.0.0" }. Added in 36.0 + InheritedVersion string `json:"inheritedVersion,omitempty"` // To be used when creating a new version of a defined entity type. Specifies the version of the type that will be the template for the authorization configuration of the new version. The Type ACLs and the access requirements of the Type Behaviors of the new version will be copied from those of the inherited version. If the value of this property is ‘0’, then the new type version will not inherit another version and will have the default authorization settings, just like the first version of a new type. Added in 36.0 + Interfaces []string `json:"interfaces,omitempty"` // List of interface IDs that this defined entity type is referenced by + IsReadOnly bool `json:"readonly,omitempty"` // `true` if the entity type cannot be modified + Schema map[string]interface{} `json:"schema,omitempty"` // The JSON-Schema valid definition of the defined entity type. If no JSON Schema version is specified, version 4 will be assumed + Vendor string `json:"vendor,omitempty"` // The vendor name +} From 65a5071b7a101b99bd66e4b2fb568111bccf25cb Mon Sep 17 00:00:00 2001 From: abarreiro Date: Wed, 25 Jan 2023 11:02:33 +0100 Subject: [PATCH 06/30] Changelog Signed-off-by: abarreiro --- .changes/v2.20.0/{xxx-features.md => 545-features.md} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename .changes/v2.20.0/{xxx-features.md => 545-features.md} (86%) diff --git a/.changes/v2.20.0/xxx-features.md b/.changes/v2.20.0/545-features.md similarity index 86% rename from .changes/v2.20.0/xxx-features.md rename to .changes/v2.20.0/545-features.md index 26f853ca0..1c6057133 100644 --- a/.changes/v2.20.0/xxx-features.md +++ b/.changes/v2.20.0/545-features.md @@ -1,3 +1,3 @@ * Added support for Runtime Defined Entity types with client methods `VCDClient.CreateRdeType`, `VCDClient.GetAllRdeTypes`, `VCDClient.GetRdeType`, `VCDClient.GetRdeTypeById` and methods to manipulate them `DefinedEntityType.Update`, - `DefinedEntityType.Delete` [GH-xxx] + `DefinedEntityType.Delete` [GH-545] From b59e9cefc7dccff05a123c078343f59c62f45193 Mon Sep 17 00:00:00 2001 From: abarreiro Date: Thu, 26 Jan 2023 15:10:51 +0100 Subject: [PATCH 07/30] Improve tests Signed-off-by: abarreiro --- govcd/defined_interface.go | 22 ------ govcd/defined_interface_test.go | 116 +++++++++++++++++++++++--------- 2 files changed, 86 insertions(+), 52 deletions(-) diff --git a/govcd/defined_interface.go b/govcd/defined_interface.go index d3f98745e..1c631fe5e 100644 --- a/govcd/defined_interface.go +++ b/govcd/defined_interface.go @@ -20,10 +20,6 @@ type DefinedInterface struct { // Only System administrator can create Defined Interfaces. func (vcdClient *VCDClient) CreateDefinedInterface(definedInterface *types.DefinedInterface) (*DefinedInterface, error) { client := vcdClient.Client - if !client.IsSysAdmin { - return nil, fmt.Errorf("creating Defined Interfaces requires System user") - } - endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointInterfaces apiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint) if err != nil { @@ -52,10 +48,6 @@ func (vcdClient *VCDClient) CreateDefinedInterface(definedInterface *types.Defin // Only System administrator can retrieve Defined Interfaces. func (vcdClient *VCDClient) GetAllDefinedInterfaces(queryParameters url.Values) ([]*DefinedInterface, error) { client := vcdClient.Client - if !client.IsSysAdmin { - return nil, fmt.Errorf("getting Defined Interfaces requires System user") - } - endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointInterfaces apiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint) if err != nil { @@ -88,11 +80,6 @@ func (vcdClient *VCDClient) GetAllDefinedInterfaces(queryParameters url.Values) // GetDefinedInterface retrieves a single Defined Interface defined by its unique combination of vendor, namespace and version. // Only System administrator can retrieve Defined Interfaces. func (vcdClient *VCDClient) GetDefinedInterface(vendor, namespace, version string) (*DefinedInterface, error) { - client := vcdClient.Client - if !client.IsSysAdmin { - return nil, fmt.Errorf("getting Defined Interfaces requires System user") - } - queryParameters := url.Values{} queryParameters.Add("filter", fmt.Sprintf("vendor==%s;nss==%s;version==%s", vendor, namespace, version)) interfaces, err := vcdClient.GetAllDefinedInterfaces(queryParameters) @@ -115,9 +102,6 @@ func (vcdClient *VCDClient) GetDefinedInterface(vendor, namespace, version strin // Only System administrator can retrieve Defined Interfaces. func (vcdClient *VCDClient) GetDefinedInterfaceById(id string) (*DefinedInterface, error) { client := vcdClient.Client - if !client.IsSysAdmin { - return nil, fmt.Errorf("getting Defined Interfaces requires System user") - } endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointInterfaces apiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint) @@ -147,9 +131,6 @@ func (vcdClient *VCDClient) GetDefinedInterfaceById(id string) (*DefinedInterfac // Only System administrator can update Defined Interfaces. func (di *DefinedInterface) Update(definedInterface types.DefinedInterface) error { client := di.client - if !client.IsSysAdmin { - return fmt.Errorf("updating Defined Interfaces requires System user") - } if di.DefinedInterface.ID == "" { return fmt.Errorf("ID of the receiver Defined Interface is empty") @@ -182,9 +163,6 @@ func (di *DefinedInterface) Update(definedInterface types.DefinedInterface) erro // Only System administrator can delete Defined Interfaces. func (di *DefinedInterface) Delete() error { client := di.client - if !client.IsSysAdmin { - return fmt.Errorf("deleting Defined Interfaces requires System user") - } if di.DefinedInterface.ID == "" { return fmt.Errorf("ID of the receiver Defined Interface is empty") diff --git a/govcd/defined_interface_test.go b/govcd/defined_interface_test.go index 97d5deb40..c9fb5c957 100644 --- a/govcd/defined_interface_test.go +++ b/govcd/defined_interface_test.go @@ -14,62 +14,118 @@ import ( "strings" ) -// Test_DefinedInterface tests the CRUD behavior of Defined Interfaces. First, it checks how many exist already, -// as VCD contains some pre-defined ones. Then we create a new one, so the number of existing ones should be increased by 1. -// We try to get this new created interface with the available getter methods and then perform an update. -// As a final step, we delete it and check that the deletion is correct (the receiver object is empty and doesn't exist in VCD). +// Test_DefinedInterface tests the CRUD behavior of Defined Interfaces as a System administrator and tenant user. +// This test can be run with GOVCD_SKIP_VAPP_CREATION option enabled. func (vcd *TestVCD) Test_DefinedInterface(check *C) { if vcd.skipAdminTests { check.Skip(fmt.Sprintf(TestRequiresSysAdminPrivileges, check.TestName())) } - endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointInterfaces - skipOpenApiEndpointTest(vcd, check, endpoint) + skipOpenApiEndpointTest(vcd, check, types.OpenApiPathVersion1_0_0+types.OpenApiEndpointInterfaces) + if len(vcd.config.Tenants) == 0 { + check.Skip("skipping as there is no configured tenant users") + } + + // Creates the clients for the System admin and the Tenant user + systemAdministratorClient := vcd.client + tenantUserClient := NewVCDClient(vcd.client.Client.VCDHREF, true) + err := tenantUserClient.Authenticate(vcd.config.Tenants[0].User, vcd.config.Tenants[0].Password, vcd.config.Tenants[0].SysOrg) + check.Assert(err, IsNil) - allDefinedInterfaces, err := vcd.client.GetAllDefinedInterfaces(nil) + // First, it checks how many exist already, as VCD contains some pre-defined ones. + allDefinedInterfacesBySysAdmin, err := systemAdministratorClient.GetAllDefinedInterfaces(nil) check.Assert(err, IsNil) - alreadyPresentRDEs := len(allDefinedInterfaces) + alreadyPresentRDEs := len(allDefinedInterfacesBySysAdmin) + allDefinedInterfacesByTenant, err := tenantUserClient.GetAllDefinedInterfaces(nil) + check.Assert(err, IsNil) + check.Assert(len(allDefinedInterfacesByTenant), Equals, len(allDefinedInterfacesBySysAdmin)) + + // Then we create a new Defined Interface with System administrator dummyRde := &types.DefinedInterface{ - Name: check.TestName() + "_name", - Namespace: check.TestName() + "_nss", + Name: strings.ReplaceAll(check.TestName()+"name3", ".", ""), + Namespace: strings.ReplaceAll(check.TestName()+"nss3", ".", ""), Version: "1.2.3", Vendor: "vmware", } - newDefinedInterface, err := vcd.client.CreateDefinedInterface(dummyRde) + newDefinedInterfaceFromSysAdmin, err := systemAdministratorClient.CreateDefinedInterface(dummyRde) + check.Assert(err, IsNil) + check.Assert(newDefinedInterfaceFromSysAdmin, NotNil) + check.Assert(newDefinedInterfaceFromSysAdmin.DefinedInterface.Name, Equals, dummyRde.Name) + check.Assert(newDefinedInterfaceFromSysAdmin.DefinedInterface.Namespace, Equals, dummyRde.Namespace) + check.Assert(newDefinedInterfaceFromSysAdmin.DefinedInterface.Version, Equals, dummyRde.Version) + check.Assert(newDefinedInterfaceFromSysAdmin.DefinedInterface.Vendor, Equals, dummyRde.Vendor) + check.Assert(newDefinedInterfaceFromSysAdmin.DefinedInterface.IsReadOnly, Equals, dummyRde.IsReadOnly) + AddToCleanupListOpenApi(newDefinedInterfaceFromSysAdmin.DefinedInterface.ID, check.TestName(), types.OpenApiPathVersion1_0_0+types.OpenApiEndpointInterfaces+newDefinedInterfaceFromSysAdmin.DefinedInterface.ID) + + // Tenants can't create Defined Interfaces + nilDefinedInterface, err := tenantUserClient.CreateDefinedInterface(&types.DefinedInterface{ + Name: strings.ReplaceAll(check.TestName()+"4", ".", ""), + Namespace: strings.ReplaceAll(check.TestName()+"4", ".", ""), + Version: "4.5.6", + Vendor: "vmware", + }) + check.Assert(err, NotNil) + check.Assert(nilDefinedInterface, IsNil) + check.Assert(strings.Contains(err.Error(), "ACCESS_TO_RESOURCE_IS_FORBIDDEN"), Equals, true) + + // As we created a new one, we check the new count is correct in both System admin and Tenant user + allDefinedInterfacesBySysAdmin, err = systemAdministratorClient.GetAllDefinedInterfaces(nil) check.Assert(err, IsNil) - check.Assert(newDefinedInterface, NotNil) - check.Assert(newDefinedInterface.DefinedInterface.Name, Equals, dummyRde.Name) - check.Assert(newDefinedInterface.DefinedInterface.Namespace, Equals, dummyRde.Namespace) - check.Assert(newDefinedInterface.DefinedInterface.Version, Equals, dummyRde.Version) - check.Assert(newDefinedInterface.DefinedInterface.Vendor, Equals, dummyRde.Vendor) - check.Assert(newDefinedInterface.DefinedInterface.IsReadOnly, Equals, dummyRde.IsReadOnly) + check.Assert(len(allDefinedInterfacesBySysAdmin), Equals, alreadyPresentRDEs+1) - AddToCleanupListOpenApi(newDefinedInterface.DefinedInterface.ID, check.TestName(), types.OpenApiPathVersion1_0_0+types.OpenApiEndpointInterfaces+newDefinedInterface.DefinedInterface.ID) + allDefinedInterfacesByTenant, err = tenantUserClient.GetAllDefinedInterfaces(nil) + check.Assert(err, IsNil) + check.Assert(len(allDefinedInterfacesByTenant), Equals, len(allDefinedInterfacesBySysAdmin)) + + // Test the multiple ways of getting a Defined Interface in both users. + obtainedDefinedInterface, err := systemAdministratorClient.GetDefinedInterfaceById(newDefinedInterfaceFromSysAdmin.DefinedInterface.ID) + check.Assert(err, IsNil) + check.Assert(*obtainedDefinedInterface.DefinedInterface, DeepEquals, *newDefinedInterfaceFromSysAdmin.DefinedInterface) - allDefinedInterfaces, err = vcd.client.GetAllDefinedInterfaces(nil) + obtainedDefinedInterface, err = tenantUserClient.GetDefinedInterfaceById(newDefinedInterfaceFromSysAdmin.DefinedInterface.ID) check.Assert(err, IsNil) - check.Assert(len(allDefinedInterfaces), Equals, alreadyPresentRDEs+1) + check.Assert(*obtainedDefinedInterface.DefinedInterface, DeepEquals, *newDefinedInterfaceFromSysAdmin.DefinedInterface) - obtainedDefinedInterface, err := vcd.client.GetDefinedInterfaceById(newDefinedInterface.DefinedInterface.ID) + obtainedDefinedInterface2, err := systemAdministratorClient.GetDefinedInterface(obtainedDefinedInterface.DefinedInterface.Vendor, obtainedDefinedInterface.DefinedInterface.Namespace, obtainedDefinedInterface.DefinedInterface.Version) check.Assert(err, IsNil) - check.Assert(*obtainedDefinedInterface.DefinedInterface, DeepEquals, *newDefinedInterface.DefinedInterface) + check.Assert(*obtainedDefinedInterface2.DefinedInterface, DeepEquals, *obtainedDefinedInterface.DefinedInterface) - obtainedDefinedInterface2, err := vcd.client.GetDefinedInterface(obtainedDefinedInterface.DefinedInterface.Vendor, obtainedDefinedInterface.DefinedInterface.Namespace, obtainedDefinedInterface.DefinedInterface.Version) + obtainedDefinedInterface2, err = tenantUserClient.GetDefinedInterfaceById(newDefinedInterfaceFromSysAdmin.DefinedInterface.ID) check.Assert(err, IsNil) check.Assert(*obtainedDefinedInterface2.DefinedInterface, DeepEquals, *obtainedDefinedInterface.DefinedInterface) - err = newDefinedInterface.Update(types.DefinedInterface{ + // Update the Defined Interface as System administrator + err = newDefinedInterfaceFromSysAdmin.Update(types.DefinedInterface{ Name: dummyRde.Name + "2", // Only name can be updated }) check.Assert(err, IsNil) - check.Assert(newDefinedInterface.DefinedInterface.Name, Equals, dummyRde.Name+"2") + check.Assert(newDefinedInterfaceFromSysAdmin.DefinedInterface.Name, Equals, dummyRde.Name+"2") - deletedId := newDefinedInterface.DefinedInterface.ID - err = newDefinedInterface.Delete() + // This one was obtained by the tenant, so it shouldn't be updatable + err = obtainedDefinedInterface2.Update(types.DefinedInterface{ + Name: dummyRde.Name + "3", + }) + check.Assert(err, NotNil) + check.Assert(strings.Contains(err.Error(), "ACCESS_TO_RESOURCE_IS_FORBIDDEN"), Equals, true) + + // This one was obtained by the tenant, so it shouldn't be deletable + err = obtainedDefinedInterface2.Delete() + check.Assert(err, NotNil) + check.Assert(strings.Contains(err.Error(), "ACCESS_TO_RESOURCE_IS_FORBIDDEN"), Equals, true) + + // We perform the actual removal with the System administrator + deletedId := newDefinedInterfaceFromSysAdmin.DefinedInterface.ID + err = newDefinedInterfaceFromSysAdmin.Delete() check.Assert(err, IsNil) - check.Assert(*newDefinedInterface.DefinedInterface, DeepEquals, types.DefinedInterface{}) + check.Assert(*newDefinedInterfaceFromSysAdmin.DefinedInterface, DeepEquals, types.DefinedInterface{}) - _, err = vcd.client.GetDefinedInterfaceById(deletedId) + _, err = systemAdministratorClient.GetDefinedInterfaceById(deletedId) check.Assert(err, NotNil) - check.Assert(strings.Contains(err.Error(), ErrorEntityNotFound.Error()), Equals, true) + + // API <= 36.0 returns a 400 BAD REQUEST instead of the usual 403 FORBIDDEN + if vcd.client.Client.APIClientVersionIs("<= 36.0") { + check.Assert(strings.Contains(err.Error(), "BAD_REQUEST"), Equals, true) + } else { + check.Assert(strings.Contains(err.Error(), ErrorEntityNotFound.Error()), Equals, true) + } } From 803a148867a81681d8e115d555fc54f0274955a2 Mon Sep 17 00:00:00 2001 From: abarreiro Date: Thu, 26 Jan 2023 15:12:05 +0100 Subject: [PATCH 08/30] Update docs Signed-off-by: abarreiro --- govcd/defined_interface.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/govcd/defined_interface.go b/govcd/defined_interface.go index 1c631fe5e..6cb63f214 100644 --- a/govcd/defined_interface.go +++ b/govcd/defined_interface.go @@ -45,7 +45,6 @@ func (vcdClient *VCDClient) CreateDefinedInterface(definedInterface *types.Defin } // GetAllDefinedInterfaces retrieves all Defined Interfaces. Query parameters can be supplied to perform additional filtering. -// Only System administrator can retrieve Defined Interfaces. func (vcdClient *VCDClient) GetAllDefinedInterfaces(queryParameters url.Values) ([]*DefinedInterface, error) { client := vcdClient.Client endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointInterfaces @@ -78,7 +77,6 @@ func (vcdClient *VCDClient) GetAllDefinedInterfaces(queryParameters url.Values) } // GetDefinedInterface retrieves a single Defined Interface defined by its unique combination of vendor, namespace and version. -// Only System administrator can retrieve Defined Interfaces. func (vcdClient *VCDClient) GetDefinedInterface(vendor, namespace, version string) (*DefinedInterface, error) { queryParameters := url.Values{} queryParameters.Add("filter", fmt.Sprintf("vendor==%s;nss==%s;version==%s", vendor, namespace, version)) @@ -99,7 +97,6 @@ func (vcdClient *VCDClient) GetDefinedInterface(vendor, namespace, version strin } // GetDefinedInterfaceById gets a Defined Interface identified by its unique URN. -// Only System administrator can retrieve Defined Interfaces. func (vcdClient *VCDClient) GetDefinedInterfaceById(id string) (*DefinedInterface, error) { client := vcdClient.Client From ed24fbca8c6f179a3fe71a5809cd329cb772033e Mon Sep 17 00:00:00 2001 From: abarreiro Date: Thu, 26 Jan 2023 15:36:44 +0100 Subject: [PATCH 09/30] Amend corner case with v36.0 Signed-off-by: abarreiro --- govcd/defined_interface.go | 18 ++++++++++++++---- govcd/defined_interface_test.go | 8 +------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/govcd/defined_interface.go b/govcd/defined_interface.go index 6cb63f214..50e5ee0c2 100644 --- a/govcd/defined_interface.go +++ b/govcd/defined_interface.go @@ -8,6 +8,7 @@ import ( "fmt" "github.com/vmware/go-vcloud-director/v2/types/v56" "net/url" + "strings" ) // DefinedInterface is a type for handling Defined Interfaces in VCD that allow to define new RDEs (DefinedEntityType). @@ -61,7 +62,7 @@ func (vcdClient *VCDClient) GetAllDefinedInterfaces(queryParameters url.Values) typeResponses := []*types.DefinedInterface{{}} err = client.OpenApiGetAllItems(apiVersion, urlRef, queryParameters, &typeResponses, nil) if err != nil { - return nil, err + return nil, amendDefinedInterfaceError(&client, err) } // Wrap all typeResponses into DefinedEntityType types with client @@ -118,7 +119,7 @@ func (vcdClient *VCDClient) GetDefinedInterfaceById(id string) (*DefinedInterfac err = client.OpenApiGetItem(apiVersion, urlRef, nil, result.DefinedInterface, nil) if err != nil { - return nil, err + return nil, amendDefinedInterfaceError(&client, err) } return result, nil @@ -150,7 +151,7 @@ func (di *DefinedInterface) Update(definedInterface types.DefinedInterface) erro err = client.OpenApiPutItem(apiVersion, urlRef, nil, definedInterface, di.DefinedInterface, nil) if err != nil { - return err + return amendDefinedInterfaceError(client, err) } return nil @@ -178,9 +179,18 @@ func (di *DefinedInterface) Delete() error { err = client.OpenApiDeleteItem(apiVersion, urlRef, nil, nil) if err != nil { - return err + return amendDefinedInterfaceError(client, err) } di.DefinedInterface = &types.DefinedInterface{} return nil } + +// amendDefinedInterfaceError fixes a wrong type of error returned by VCD API <= v36.0 on GET operations +// when the defined interface does not exist. +func amendDefinedInterfaceError(client *Client, err error) error { + if client.APIClientVersionIs("<= 36.0") && err != nil && strings.Contains(err.Error(), "does not exist") { + return fmt.Errorf("%s: %s", ErrorEntityNotFound.Error(), err) + } + return err +} diff --git a/govcd/defined_interface_test.go b/govcd/defined_interface_test.go index c9fb5c957..29a3de3b8 100644 --- a/govcd/defined_interface_test.go +++ b/govcd/defined_interface_test.go @@ -121,11 +121,5 @@ func (vcd *TestVCD) Test_DefinedInterface(check *C) { _, err = systemAdministratorClient.GetDefinedInterfaceById(deletedId) check.Assert(err, NotNil) - - // API <= 36.0 returns a 400 BAD REQUEST instead of the usual 403 FORBIDDEN - if vcd.client.Client.APIClientVersionIs("<= 36.0") { - check.Assert(strings.Contains(err.Error(), "BAD_REQUEST"), Equals, true) - } else { - check.Assert(strings.Contains(err.Error(), ErrorEntityNotFound.Error()), Equals, true) - } + check.Assert(strings.Contains(err.Error(), ErrorEntityNotFound.Error()), Equals, true) } From e3d027db420dd8b651e29a5a1d6ec567b7737ea0 Mon Sep 17 00:00:00 2001 From: abarreiro Date: Thu, 26 Jan 2023 15:46:33 +0100 Subject: [PATCH 10/30] Improve docs Signed-off-by: abarreiro --- govcd/defined_interface.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/govcd/defined_interface.go b/govcd/defined_interface.go index 50e5ee0c2..bea03e8bc 100644 --- a/govcd/defined_interface.go +++ b/govcd/defined_interface.go @@ -11,7 +11,7 @@ import ( "strings" ) -// DefinedInterface is a type for handling Defined Interfaces in VCD that allow to define new RDEs (DefinedEntityType). +// DefinedInterface is a type for handling Defined Interfaces, from the Runtime Defined Entities framework, in VCD. type DefinedInterface struct { DefinedInterface *types.DefinedInterface client *Client From fe357acac14c7c51b9f2a34159da455ba6b66406 Mon Sep 17 00:00:00 2001 From: abarreiro Date: Thu, 26 Jan 2023 16:46:10 +0100 Subject: [PATCH 11/30] Checkpoint Signed-off-by: abarreiro --- govcd/defined_entity.go | 44 ++++++++++++++++-------------------- govcd/defined_entity_test.go | 2 +- govcd/openapi_endpoints.go | 4 ++++ types/v56/openapi.go | 3 ++- 4 files changed, 26 insertions(+), 27 deletions(-) diff --git a/govcd/defined_entity.go b/govcd/defined_entity.go index e56b85385..3fc6f5f6d 100644 --- a/govcd/defined_entity.go +++ b/govcd/defined_entity.go @@ -10,19 +10,16 @@ import ( "net/url" ) -// DefinedEntityType is a type for handling Runtime Defined Entity (RDE) type definitions +// DefinedEntityType is a type for handling Runtime Defined Entity (RDE) Type definitions type DefinedEntityType struct { DefinedEntityType *types.DefinedEntityType client *Client } -// CreateRdeType creates a Runtime Defined Entity type. -// Only System administrator can create RDE types. +// CreateRdeType creates a Runtime Defined Entity Type. +// Only System administrator can create RDE Types. func (vcdClient *VCDClient) CreateRdeType(rde *types.DefinedEntityType) (*DefinedEntityType, error) { client := vcdClient.Client - if !client.IsSysAdmin { - return nil, fmt.Errorf("creating Runtime Defined Entity types requires System user") - } endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEntityTypes apiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint) @@ -48,12 +45,11 @@ func (vcdClient *VCDClient) CreateRdeType(rde *types.DefinedEntityType) (*Define return result, nil } -// GetAllRdeTypes retrieves all Runtime Defined Entity types. Query parameters can be supplied to perform additional filtering. -// Only System administrator can retrieve RDE types. +// GetAllRdeTypes retrieves all Runtime Defined Entity Types. Query parameters can be supplied to perform additional filtering. func (vcdClient *VCDClient) GetAllRdeTypes(queryParameters url.Values) ([]*DefinedEntityType, error) { client := vcdClient.Client if !client.IsSysAdmin { - return nil, fmt.Errorf("getting Runtime Defined Entity types requires System user") + return nil, fmt.Errorf("getting Runtime Defined Entity Types requires System user") } endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEntityTypes @@ -85,12 +81,11 @@ func (vcdClient *VCDClient) GetAllRdeTypes(queryParameters url.Values) ([]*Defin return returnRDEs, nil } -// GetRdeType gets a Runtime Defined Entity type by its unique combination of vendor, namespace and version. -// Only System administrator can retrieve RDE types. +// GetRdeType gets a Runtime Defined Entity Type by its unique combination of vendor, namespace and version. func (vcdClient *VCDClient) GetRdeType(vendor, namespace, version string) (*DefinedEntityType, error) { client := vcdClient.Client if !client.IsSysAdmin { - return nil, fmt.Errorf("getting Runtime Defined Entity types requires System user") + return nil, fmt.Errorf("getting Runtime Defined Entity Types requires System user") } queryParameters := url.Values{} @@ -101,22 +96,21 @@ func (vcdClient *VCDClient) GetRdeType(vendor, namespace, version string) (*Defi } if len(rdeTypes) == 0 { - return nil, fmt.Errorf("%s could not find the Runtime Defined Entity type with vendor %s, namespace %s and version %s", ErrorEntityNotFound, vendor, namespace, version) + return nil, fmt.Errorf("%s could not find the Runtime Defined Entity Type with vendor %s, namespace %s and version %s", ErrorEntityNotFound, vendor, namespace, version) } if len(rdeTypes) > 1 { - return nil, fmt.Errorf("found more than 1 Runtime Defined Entity type with vendor %s, namespace %s and version %s", vendor, namespace, version) + return nil, fmt.Errorf("found more than 1 Runtime Defined Entity Type with vendor %s, namespace %s and version %s", vendor, namespace, version) } return rdeTypes[0], nil } -// GetRdeTypeById gets a Runtime Defined Entity type by its ID -// Only System administrator can retrieve RDE types. +// GetRdeTypeById gets a Runtime Defined Entity Type by its ID func (vcdClient *VCDClient) GetRdeTypeById(id string) (*DefinedEntityType, error) { client := vcdClient.Client if !client.IsSysAdmin { - return nil, fmt.Errorf("getting Runtime Defined Entity types requires System user") + return nil, fmt.Errorf("getting Runtime Defined Entity Types requires System user") } endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEntityTypes @@ -143,16 +137,16 @@ func (vcdClient *VCDClient) GetRdeTypeById(id string) (*DefinedEntityType, error return result, nil } -// Update updates the receiver Runtime Defined Entity type with the values given by the input. -// Only System administrator can update RDE types. +// Update updates the receiver Runtime Defined Entity Type with the values given by the input. +// Only System administrator can update RDE Types. func (rdeType *DefinedEntityType) Update(rdeTypeToUpdate types.DefinedEntityType) error { client := rdeType.client if !client.IsSysAdmin { - return fmt.Errorf("updating Runtime Defined Entity types requires System user") + return fmt.Errorf("updating Runtime Defined Entity Types requires System user") } if rdeType.DefinedEntityType.ID == "" { - return fmt.Errorf("ID of the receiver Runtime Defined Entity type is empty") + return fmt.Errorf("ID of the receiver Runtime Defined Entity Type is empty") } if rdeTypeToUpdate.ID != "" && rdeTypeToUpdate.ID != rdeType.DefinedEntityType.ID { @@ -187,16 +181,16 @@ func (rdeType *DefinedEntityType) Update(rdeTypeToUpdate types.DefinedEntityType return nil } -// Delete deletes the receiver Runtime Defined Entity type. -// Only System administrator can delete RDE types. +// Delete deletes the receiver Runtime Defined Entity Type. +// Only System administrator can delete RDE Types. func (rdeType *DefinedEntityType) Delete() error { client := rdeType.client if !client.IsSysAdmin { - return fmt.Errorf("deleting Runtime Defined Entity types requires System user") + return fmt.Errorf("deleting Runtime Defined Entity Types requires System user") } if rdeType.DefinedEntityType.ID == "" { - return fmt.Errorf("ID of the receiver Runtime Defined Entity type is empty") + return fmt.Errorf("ID of the receiver Runtime Defined Entity Type is empty") } endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEntityTypes diff --git a/govcd/defined_entity_test.go b/govcd/defined_entity_test.go index 0199bb1ca..a0757661b 100644 --- a/govcd/defined_entity_test.go +++ b/govcd/defined_entity_test.go @@ -18,7 +18,7 @@ import ( "strings" ) -// Test_Rde tests the complete journey of RDE type and RDE instance with creation, reads, updates and finally deletion. +// Test_Rde tests the CRUD operations for the RDE Type with both System administrator and a tenant user. func (vcd *TestVCD) Test_Rde(check *C) { if vcd.skipAdminTests { check.Skip(fmt.Sprintf(TestRequiresSysAdminPrivileges, check.TestName())) diff --git a/govcd/openapi_endpoints.go b/govcd/openapi_endpoints.go index 286d0c623..0df4247f1 100644 --- a/govcd/openapi_endpoints.go +++ b/govcd/openapi_endpoints.go @@ -128,6 +128,10 @@ var endpointElevatedApiVersions = map[string][]string{ //"36.0", // Introduced support "36.2", // 2 additional fields vappNetworkSegmentProfileTemplateRef and vdcNetworkSegmentProfileTemplateRef added }, + types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEntityTypes: { + //"35.0", // Introduced support + "37.1", // Added MaxImplicitRight property in DefinedEntityType + }, } // checkOpenApiEndpointCompatibility checks if VCD version (to which the client is connected) is sufficient to work with diff --git a/types/v56/openapi.go b/types/v56/openapi.go index 28bffa4f8..f63ee8467 100644 --- a/types/v56/openapi.go +++ b/types/v56/openapi.go @@ -437,7 +437,7 @@ type DefinedInterface struct { IsReadOnly bool `json:"readonly,omitempty"` // True if the entity type cannot be modified } -// DefinedEntityType describes what a defined entity type should look like. +// DefinedEntityType describes what a Defined Entity Type should look like. type DefinedEntityType struct { ID string `json:"id,omitempty"` // The id of the defined entity type in URN format Name string `json:"name,omitempty"` // The name of the defined entity type @@ -448,6 +448,7 @@ type DefinedEntityType struct { Hooks map[string]string `json:"hooks,omitempty"` // A mapping defining which behaviors should be invoked upon specific lifecycle events, like PostCreate, PostUpdate, PreDelete. For example: "hooks": { "PostCreate": "urn:vcloud:behavior-interface:postCreateHook:vendorA:containerCluster:1.0.0" }. Added in 36.0 InheritedVersion string `json:"inheritedVersion,omitempty"` // To be used when creating a new version of a defined entity type. Specifies the version of the type that will be the template for the authorization configuration of the new version. The Type ACLs and the access requirements of the Type Behaviors of the new version will be copied from those of the inherited version. If the value of this property is ‘0’, then the new type version will not inherit another version and will have the default authorization settings, just like the first version of a new type. Added in 36.0 Interfaces []string `json:"interfaces,omitempty"` // List of interface IDs that this defined entity type is referenced by + MaxImplicitRight string `json:"maxImplicitRight,omitempty"` // The maximum Type Right level that will be implied from the user’s Type ACLs if this field is defined. For example, “maxImplicitRight”: “urn:vcloud:accessLevel:ReadWrite” would mean that a user with RO , RW, and FC ACLs to the Type would implicitly get the “Read: ” and “Write: ” rights, but not the “Full Control: ” right. The valid values are “urn:vcloud:accessLevel:ReadOnly”, “urn:vcloud:accessLevel:ReadWrite”, “urn:vcloud:accessLevel:FullControl” IsReadOnly bool `json:"readonly,omitempty"` // `true` if the entity type cannot be modified Schema map[string]interface{} `json:"schema,omitempty"` // The JSON-Schema valid definition of the defined entity type. If no JSON Schema version is specified, version 4 will be assumed Vendor string `json:"vendor,omitempty"` // The vendor name From ac5e6b9c2690f604ff3fad86e0673dc3aff8b7aa Mon Sep 17 00:00:00 2001 From: abarreiro Date: Thu, 26 Jan 2023 16:46:31 +0100 Subject: [PATCH 12/30] Checkpoint Signed-off-by: abarreiro --- types/v56/openapi.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/v56/openapi.go b/types/v56/openapi.go index f63ee8467..5ca78c40a 100644 --- a/types/v56/openapi.go +++ b/types/v56/openapi.go @@ -448,7 +448,7 @@ type DefinedEntityType struct { Hooks map[string]string `json:"hooks,omitempty"` // A mapping defining which behaviors should be invoked upon specific lifecycle events, like PostCreate, PostUpdate, PreDelete. For example: "hooks": { "PostCreate": "urn:vcloud:behavior-interface:postCreateHook:vendorA:containerCluster:1.0.0" }. Added in 36.0 InheritedVersion string `json:"inheritedVersion,omitempty"` // To be used when creating a new version of a defined entity type. Specifies the version of the type that will be the template for the authorization configuration of the new version. The Type ACLs and the access requirements of the Type Behaviors of the new version will be copied from those of the inherited version. If the value of this property is ‘0’, then the new type version will not inherit another version and will have the default authorization settings, just like the first version of a new type. Added in 36.0 Interfaces []string `json:"interfaces,omitempty"` // List of interface IDs that this defined entity type is referenced by - MaxImplicitRight string `json:"maxImplicitRight,omitempty"` // The maximum Type Right level that will be implied from the user’s Type ACLs if this field is defined. For example, “maxImplicitRight”: “urn:vcloud:accessLevel:ReadWrite” would mean that a user with RO , RW, and FC ACLs to the Type would implicitly get the “Read: ” and “Write: ” rights, but not the “Full Control: ” right. The valid values are “urn:vcloud:accessLevel:ReadOnly”, “urn:vcloud:accessLevel:ReadWrite”, “urn:vcloud:accessLevel:FullControl” + MaxImplicitRight string `json:"maxImplicitRight,omitempty"` // The maximum Type Right level that will be implied from the user’s Type ACLs if this field is defined. For example, “maxImplicitRight”: “urn:vcloud:accessLevel:ReadWrite” would mean that a user with RO , RW, and FC ACLs to the Type would implicitly get the “Read: ” and “Write: ” rights, but not the “Full Control: ” right. The valid values are “urn:vcloud:accessLevel:ReadOnly”, “urn:vcloud:accessLevel:ReadWrite”, “urn:vcloud:accessLevel:FullControl” IsReadOnly bool `json:"readonly,omitempty"` // `true` if the entity type cannot be modified Schema map[string]interface{} `json:"schema,omitempty"` // The JSON-Schema valid definition of the defined entity type. If no JSON Schema version is specified, version 4 will be assumed Vendor string `json:"vendor,omitempty"` // The vendor name From 53146c2cae3d605319aa121eafe465da88e626e3 Mon Sep 17 00:00:00 2001 From: abarreiro Date: Thu, 26 Jan 2023 16:48:03 +0100 Subject: [PATCH 13/30] Add comment Signed-off-by: abarreiro --- govcd/defined_interface_test.go | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/govcd/defined_interface_test.go b/govcd/defined_interface_test.go index 29a3de3b8..4ffe9d377 100644 --- a/govcd/defined_interface_test.go +++ b/govcd/defined_interface_test.go @@ -40,10 +40,11 @@ func (vcd *TestVCD) Test_DefinedInterface(check *C) { check.Assert(err, IsNil) check.Assert(len(allDefinedInterfacesByTenant), Equals, len(allDefinedInterfacesBySysAdmin)) - // Then we create a new Defined Interface with System administrator + // Then we create a new Defined Interface with System administrator. We replace the dots in both + // namespace and name as API is quirky at versions of VCD < 37.0 dummyRde := &types.DefinedInterface{ - Name: strings.ReplaceAll(check.TestName()+"name3", ".", ""), - Namespace: strings.ReplaceAll(check.TestName()+"nss3", ".", ""), + Name: strings.ReplaceAll(check.TestName()+"name", ".", ""), + Namespace: strings.ReplaceAll(check.TestName()+"nss", ".", ""), Version: "1.2.3", Vendor: "vmware", } @@ -57,10 +58,11 @@ func (vcd *TestVCD) Test_DefinedInterface(check *C) { check.Assert(newDefinedInterfaceFromSysAdmin.DefinedInterface.IsReadOnly, Equals, dummyRde.IsReadOnly) AddToCleanupListOpenApi(newDefinedInterfaceFromSysAdmin.DefinedInterface.ID, check.TestName(), types.OpenApiPathVersion1_0_0+types.OpenApiEndpointInterfaces+newDefinedInterfaceFromSysAdmin.DefinedInterface.ID) - // Tenants can't create Defined Interfaces + // Tenants can't create Defined Interfaces. We replace the dots in both + // namespace and name as API is quirky at versions of VCD < 37.0 nilDefinedInterface, err := tenantUserClient.CreateDefinedInterface(&types.DefinedInterface{ - Name: strings.ReplaceAll(check.TestName()+"4", ".", ""), - Namespace: strings.ReplaceAll(check.TestName()+"4", ".", ""), + Name: strings.ReplaceAll(check.TestName()+"2", ".", ""), + Namespace: strings.ReplaceAll(check.TestName()+"2", ".", ""), Version: "4.5.6", Vendor: "vmware", }) From 746a113fee0c2381c8deedcd6ef342cc5a411f63 Mon Sep 17 00:00:00 2001 From: abarreiro Date: Thu, 26 Jan 2023 16:48:21 +0100 Subject: [PATCH 14/30] Add comment Signed-off-by: abarreiro --- govcd/defined_interface_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/govcd/defined_interface_test.go b/govcd/defined_interface_test.go index 4ffe9d377..8d5520714 100644 --- a/govcd/defined_interface_test.go +++ b/govcd/defined_interface_test.go @@ -98,14 +98,14 @@ func (vcd *TestVCD) Test_DefinedInterface(check *C) { // Update the Defined Interface as System administrator err = newDefinedInterfaceFromSysAdmin.Update(types.DefinedInterface{ - Name: dummyRde.Name + "2", // Only name can be updated + Name: dummyRde.Name + "3", // Only name can be updated }) check.Assert(err, IsNil) check.Assert(newDefinedInterfaceFromSysAdmin.DefinedInterface.Name, Equals, dummyRde.Name+"2") // This one was obtained by the tenant, so it shouldn't be updatable err = obtainedDefinedInterface2.Update(types.DefinedInterface{ - Name: dummyRde.Name + "3", + Name: dummyRde.Name + "4", }) check.Assert(err, NotNil) check.Assert(strings.Contains(err.Error(), "ACCESS_TO_RESOURCE_IS_FORBIDDEN"), Equals, true) From fb3addbb84cceb7b2eaaae777e66721a73a67c14 Mon Sep 17 00:00:00 2001 From: abarreiro Date: Thu, 26 Jan 2023 17:41:13 +0100 Subject: [PATCH 15/30] Checkpoint Signed-off-by: abarreiro --- govcd/defined_entity.go | 2 +- govcd/defined_entity_test.go | 66 +++++++++++++++++++++++++++++------- 2 files changed, 54 insertions(+), 14 deletions(-) diff --git a/govcd/defined_entity.go b/govcd/defined_entity.go index 3fc6f5f6d..16783c64a 100644 --- a/govcd/defined_entity.go +++ b/govcd/defined_entity.go @@ -153,7 +153,7 @@ func (rdeType *DefinedEntityType) Update(rdeTypeToUpdate types.DefinedEntityType return fmt.Errorf("ID of the receiver Runtime Defined Entity and the input ID don't match") } - // Name and schema are mandatory, despite we don't want to update them, so we populate them in this situation to avoid errors + // Name and schema are mandatory, even when we don't want to update them, so we populate them in this situation to avoid errors // and make this method more user friendly. if rdeTypeToUpdate.Name == "" { rdeTypeToUpdate.Name = rdeType.DefinedEntityType.Name diff --git a/govcd/defined_entity_test.go b/govcd/defined_entity_test.go index a0757661b..52b561e9f 100644 --- a/govcd/defined_entity_test.go +++ b/govcd/defined_entity_test.go @@ -25,26 +25,41 @@ func (vcd *TestVCD) Test_Rde(check *C) { } skipOpenApiEndpointTest(vcd, check, types.OpenApiPathVersion1_0_0+types.OpenApiEndpointEntityTypes) + if len(vcd.config.Tenants) == 0 { + check.Skip("skipping as there is no configured tenant users") + } + + // Creates the clients for the System admin and the Tenant user + systemAdministratorClient := vcd.client + tenantUserClient := NewVCDClient(vcd.client.Client.VCDHREF, true) + err := tenantUserClient.Authenticate(vcd.config.Tenants[0].User, vcd.config.Tenants[0].Password, vcd.config.Tenants[0].SysOrg) + check.Assert(err, IsNil) unmarshaledRdeTypeSchema, err := loadRdeTypeSchemaFromTestResources() check.Assert(err, IsNil) check.Assert(true, Equals, len(unmarshaledRdeTypeSchema) > 0) + // First, it checks how many exist already, as VCD contains some pre-defined ones. + allRdeTypesBySystemAdmin, err := systemAdministratorClient.GetAllRdeTypes(nil) + check.Assert(err, IsNil) + alreadyPresentRdes := len(allRdeTypesBySystemAdmin) + + allRdeTypesByTenant, err := tenantUserClient.GetAllRdeTypes(nil) + check.Assert(err, IsNil) + check.Assert(len(allRdeTypesByTenant), Equals, len(allRdeTypesBySystemAdmin)) + + // Then we create a new RDE Type with System administrator. + // Can't put check.TestName() in namespace due to a bug that causes RDEs to fail on GET once created with special characters like "." rdeTypeToCreate := &types.DefinedEntityType{ Name: check.TestName(), - Namespace: "namespace14", // Can't put check.TestName() due to a bug that causes RDEs to fail on GET once created with special characters like "." + Namespace: strings.ReplaceAll(check.TestName()+"name", ".", ""), Version: "1.2.3", Description: "Description of " + check.TestName(), Schema: unmarshaledRdeTypeSchema, Vendor: "vmware", Interfaces: []string{"urn:vcloud:interface:vmware:k8s:1.0.0"}, } - - allRdeTypes, err := vcd.client.GetAllRdeTypes(nil) - check.Assert(err, IsNil) - alreadyPresentRdes := len(allRdeTypes) - - createdRdeType, err := vcd.client.CreateRdeType(rdeTypeToCreate) + createdRdeType, err := systemAdministratorClient.CreateRdeType(rdeTypeToCreate) check.Assert(err, IsNil) check.Assert(createdRdeType, NotNil) check.Assert(createdRdeType.DefinedEntityType.Name, Equals, rdeTypeToCreate.Name) @@ -55,18 +70,43 @@ func (vcd *TestVCD) Test_Rde(check *C) { check.Assert(createdRdeType.DefinedEntityType.Schema["definitions"], NotNil) check.Assert(createdRdeType.DefinedEntityType.Schema["required"], NotNil) check.Assert(createdRdeType.DefinedEntityType.Schema["properties"], NotNil) - AddToCleanupListOpenApi(createdRdeType.DefinedEntityType.ID, check.TestName(), types.OpenApiPathVersion1_0_0+types.OpenApiEndpointEntityTypes+createdRdeType.DefinedEntityType.ID) - allRdeTypes, err = vcd.client.GetAllRdeTypes(nil) + // Tenants can't create RDE Types + nilRdeType, err := tenantUserClient.CreateRdeType(&types.DefinedEntityType{ + Name: check.TestName(), + Namespace: "notworking", + Version: "4.5.6", + Schema: unmarshaledRdeTypeSchema, + Vendor: "willfail", + }) + check.Assert(err, NotNil) + check.Assert(nilRdeType, IsNil) + check.Assert(strings.Contains(err.Error(), "ACCESS_TO_RESOURCE_IS_FORBIDDEN"), Equals, true) + + // As we created a new one, we check the new count is correct in both System admin and Tenant user + allRdeTypesBySystemAdmin, err = systemAdministratorClient.GetAllRdeTypes(nil) check.Assert(err, IsNil) - check.Assert(len(allRdeTypes), Equals, alreadyPresentRdes+1) + check.Assert(len(allRdeTypesBySystemAdmin), Equals, alreadyPresentRdes+1) - obtainedRdeType, err := vcd.client.GetRdeTypeById(createdRdeType.DefinedEntityType.ID) + allRdeTypesByTenant, err = tenantUserClient.GetAllRdeTypes(nil) + check.Assert(err, IsNil) + check.Assert(len(allRdeTypesByTenant), Equals, len(allRdeTypesBySystemAdmin)) + + // Test the multiple ways of getting a Defined Interface in both users. + obtainedRdeType, err := systemAdministratorClient.GetRdeTypeById(createdRdeType.DefinedEntityType.ID) check.Assert(err, IsNil) check.Assert(*obtainedRdeType.DefinedEntityType, DeepEquals, *createdRdeType.DefinedEntityType) - obtainedRdeType2, err := vcd.client.GetRdeType(obtainedRdeType.DefinedEntityType.Vendor, obtainedRdeType.DefinedEntityType.Namespace, obtainedRdeType.DefinedEntityType.Version) + obtainedRdeType, err = tenantUserClient.GetRdeTypeById(createdRdeType.DefinedEntityType.ID) + check.Assert(err, IsNil) + check.Assert(*obtainedRdeType.DefinedEntityType, DeepEquals, *createdRdeType.DefinedEntityType) + + obtainedRdeType2, err := systemAdministratorClient.GetRdeType(obtainedRdeType.DefinedEntityType.Vendor, obtainedRdeType.DefinedEntityType.Namespace, obtainedRdeType.DefinedEntityType.Version) + check.Assert(err, IsNil) + check.Assert(*obtainedRdeType2.DefinedEntityType, DeepEquals, *obtainedRdeType.DefinedEntityType) + + obtainedRdeType2, err = tenantUserClient.GetRdeType(obtainedRdeType.DefinedEntityType.Vendor, obtainedRdeType.DefinedEntityType.Namespace, obtainedRdeType.DefinedEntityType.Version) check.Assert(err, IsNil) check.Assert(*obtainedRdeType2.DefinedEntityType, DeepEquals, *obtainedRdeType.DefinedEntityType) @@ -82,7 +122,7 @@ func (vcd *TestVCD) Test_Rde(check *C) { check.Assert(err, IsNil) check.Assert(*createdRdeType.DefinedEntityType, DeepEquals, types.DefinedEntityType{}) - _, err = vcd.client.GetRdeTypeById(deletedId) + _, err = systemAdministratorClient.GetRdeTypeById(deletedId) check.Assert(err, NotNil) check.Assert(strings.Contains(err.Error(), ErrorEntityNotFound.Error()), Equals, true) } From 8cec1998f318e4622548527bfe8351baf16e3134 Mon Sep 17 00:00:00 2001 From: abarreiro Date: Thu, 26 Jan 2023 17:44:29 +0100 Subject: [PATCH 16/30] Fix mistake in test Signed-off-by: abarreiro --- govcd/defined_interface_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/govcd/defined_interface_test.go b/govcd/defined_interface_test.go index 8d5520714..d14e7544d 100644 --- a/govcd/defined_interface_test.go +++ b/govcd/defined_interface_test.go @@ -61,8 +61,8 @@ func (vcd *TestVCD) Test_DefinedInterface(check *C) { // Tenants can't create Defined Interfaces. We replace the dots in both // namespace and name as API is quirky at versions of VCD < 37.0 nilDefinedInterface, err := tenantUserClient.CreateDefinedInterface(&types.DefinedInterface{ - Name: strings.ReplaceAll(check.TestName()+"2", ".", ""), - Namespace: strings.ReplaceAll(check.TestName()+"2", ".", ""), + Name: strings.ReplaceAll(check.TestName()+"name2", ".", ""), + Namespace: strings.ReplaceAll(check.TestName()+"name2", ".", ""), Version: "4.5.6", Vendor: "vmware", }) @@ -92,7 +92,7 @@ func (vcd *TestVCD) Test_DefinedInterface(check *C) { check.Assert(err, IsNil) check.Assert(*obtainedDefinedInterface2.DefinedInterface, DeepEquals, *obtainedDefinedInterface.DefinedInterface) - obtainedDefinedInterface2, err = tenantUserClient.GetDefinedInterfaceById(newDefinedInterfaceFromSysAdmin.DefinedInterface.ID) + obtainedDefinedInterface2, err = tenantUserClient.GetDefinedInterface(obtainedDefinedInterface.DefinedInterface.Vendor, obtainedDefinedInterface.DefinedInterface.Namespace, obtainedDefinedInterface.DefinedInterface.Version) check.Assert(err, IsNil) check.Assert(*obtainedDefinedInterface2.DefinedInterface, DeepEquals, *obtainedDefinedInterface.DefinedInterface) @@ -101,7 +101,7 @@ func (vcd *TestVCD) Test_DefinedInterface(check *C) { Name: dummyRde.Name + "3", // Only name can be updated }) check.Assert(err, IsNil) - check.Assert(newDefinedInterfaceFromSysAdmin.DefinedInterface.Name, Equals, dummyRde.Name+"2") + check.Assert(newDefinedInterfaceFromSysAdmin.DefinedInterface.Name, Equals, dummyRde.Name+"3") // This one was obtained by the tenant, so it shouldn't be updatable err = obtainedDefinedInterface2.Update(types.DefinedInterface{ From 5356b29c9e6321ebb5f83f3b462a1328d1867b38 Mon Sep 17 00:00:00 2001 From: abarreiro Date: Thu, 26 Jan 2023 17:57:45 +0100 Subject: [PATCH 17/30] Improved test: Tests pass Signed-off-by: abarreiro --- govcd/defined_entity.go | 21 --------------------- govcd/defined_entity_test.go | 31 ++++++++++++++++++------------- 2 files changed, 18 insertions(+), 34 deletions(-) diff --git a/govcd/defined_entity.go b/govcd/defined_entity.go index 16783c64a..e6b88ee1c 100644 --- a/govcd/defined_entity.go +++ b/govcd/defined_entity.go @@ -48,10 +48,6 @@ func (vcdClient *VCDClient) CreateRdeType(rde *types.DefinedEntityType) (*Define // GetAllRdeTypes retrieves all Runtime Defined Entity Types. Query parameters can be supplied to perform additional filtering. func (vcdClient *VCDClient) GetAllRdeTypes(queryParameters url.Values) ([]*DefinedEntityType, error) { client := vcdClient.Client - if !client.IsSysAdmin { - return nil, fmt.Errorf("getting Runtime Defined Entity Types requires System user") - } - endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEntityTypes apiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint) if err != nil { @@ -83,11 +79,6 @@ func (vcdClient *VCDClient) GetAllRdeTypes(queryParameters url.Values) ([]*Defin // GetRdeType gets a Runtime Defined Entity Type by its unique combination of vendor, namespace and version. func (vcdClient *VCDClient) GetRdeType(vendor, namespace, version string) (*DefinedEntityType, error) { - client := vcdClient.Client - if !client.IsSysAdmin { - return nil, fmt.Errorf("getting Runtime Defined Entity Types requires System user") - } - queryParameters := url.Values{} queryParameters.Add("filter", fmt.Sprintf("vendor==%s;nss==%s;version==%s", vendor, namespace, version)) rdeTypes, err := vcdClient.GetAllRdeTypes(queryParameters) @@ -109,10 +100,6 @@ func (vcdClient *VCDClient) GetRdeType(vendor, namespace, version string) (*Defi // GetRdeTypeById gets a Runtime Defined Entity Type by its ID func (vcdClient *VCDClient) GetRdeTypeById(id string) (*DefinedEntityType, error) { client := vcdClient.Client - if !client.IsSysAdmin { - return nil, fmt.Errorf("getting Runtime Defined Entity Types requires System user") - } - endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEntityTypes apiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint) if err != nil { @@ -141,10 +128,6 @@ func (vcdClient *VCDClient) GetRdeTypeById(id string) (*DefinedEntityType, error // Only System administrator can update RDE Types. func (rdeType *DefinedEntityType) Update(rdeTypeToUpdate types.DefinedEntityType) error { client := rdeType.client - if !client.IsSysAdmin { - return fmt.Errorf("updating Runtime Defined Entity Types requires System user") - } - if rdeType.DefinedEntityType.ID == "" { return fmt.Errorf("ID of the receiver Runtime Defined Entity Type is empty") } @@ -185,10 +168,6 @@ func (rdeType *DefinedEntityType) Update(rdeTypeToUpdate types.DefinedEntityType // Only System administrator can delete RDE Types. func (rdeType *DefinedEntityType) Delete() error { client := rdeType.client - if !client.IsSysAdmin { - return fmt.Errorf("deleting Runtime Defined Entity Types requires System user") - } - if rdeType.DefinedEntityType.ID == "" { return fmt.Errorf("ID of the receiver Runtime Defined Entity Type is empty") } diff --git a/govcd/defined_entity_test.go b/govcd/defined_entity_test.go index 52b561e9f..56e7049af 100644 --- a/govcd/defined_entity_test.go +++ b/govcd/defined_entity_test.go @@ -19,7 +19,7 @@ import ( ) // Test_Rde tests the CRUD operations for the RDE Type with both System administrator and a tenant user. -func (vcd *TestVCD) Test_Rde(check *C) { +func (vcd *TestVCD) Test_RdeType(check *C) { if vcd.skipAdminTests { check.Skip(fmt.Sprintf(TestRequiresSysAdminPrivileges, check.TestName())) } @@ -44,12 +44,14 @@ func (vcd *TestVCD) Test_Rde(check *C) { check.Assert(err, IsNil) alreadyPresentRdes := len(allRdeTypesBySystemAdmin) + // For the tenant, it should return 0 RDE Types, but no error. This is because our tenant user doesn't have + // the required rights to see the RDE Types. allRdeTypesByTenant, err := tenantUserClient.GetAllRdeTypes(nil) check.Assert(err, IsNil) - check.Assert(len(allRdeTypesByTenant), Equals, len(allRdeTypesBySystemAdmin)) + check.Assert(len(allRdeTypesByTenant), Equals, 0) // Then we create a new RDE Type with System administrator. - // Can't put check.TestName() in namespace due to a bug that causes RDEs to fail on GET once created with special characters like "." + // Can't put check.TestName() in namespace due to a bug in VCD 10.4.1 that causes RDEs to fail on GET once created with special characters like "." rdeTypeToCreate := &types.DefinedEntityType{ Name: check.TestName(), Namespace: strings.ReplaceAll(check.TestName()+"name", ".", ""), @@ -89,33 +91,36 @@ func (vcd *TestVCD) Test_Rde(check *C) { check.Assert(err, IsNil) check.Assert(len(allRdeTypesBySystemAdmin), Equals, alreadyPresentRdes+1) + // Count should be still 0 allRdeTypesByTenant, err = tenantUserClient.GetAllRdeTypes(nil) check.Assert(err, IsNil) - check.Assert(len(allRdeTypesByTenant), Equals, len(allRdeTypesBySystemAdmin)) + check.Assert(len(allRdeTypesByTenant), Equals, 0) // Test the multiple ways of getting a Defined Interface in both users. obtainedRdeType, err := systemAdministratorClient.GetRdeTypeById(createdRdeType.DefinedEntityType.ID) check.Assert(err, IsNil) check.Assert(*obtainedRdeType.DefinedEntityType, DeepEquals, *createdRdeType.DefinedEntityType) - obtainedRdeType, err = tenantUserClient.GetRdeTypeById(createdRdeType.DefinedEntityType.ID) - check.Assert(err, IsNil) - check.Assert(*obtainedRdeType.DefinedEntityType, DeepEquals, *createdRdeType.DefinedEntityType) + // The RDE Type is unreachable as user doesn't have permissions + _, err = tenantUserClient.GetRdeTypeById(createdRdeType.DefinedEntityType.ID) + check.Assert(err, NotNil) + check.Assert(strings.Contains(err.Error(), ErrorEntityNotFound.Error()), Equals, true) obtainedRdeType2, err := systemAdministratorClient.GetRdeType(obtainedRdeType.DefinedEntityType.Vendor, obtainedRdeType.DefinedEntityType.Namespace, obtainedRdeType.DefinedEntityType.Version) check.Assert(err, IsNil) check.Assert(*obtainedRdeType2.DefinedEntityType, DeepEquals, *obtainedRdeType.DefinedEntityType) - obtainedRdeType2, err = tenantUserClient.GetRdeType(obtainedRdeType.DefinedEntityType.Vendor, obtainedRdeType.DefinedEntityType.Namespace, obtainedRdeType.DefinedEntityType.Version) - check.Assert(err, IsNil) - check.Assert(*obtainedRdeType2.DefinedEntityType, DeepEquals, *obtainedRdeType.DefinedEntityType) + // The RDE Type is unreachable as user doesn't have permissions + _, err = tenantUserClient.GetRdeType(obtainedRdeType.DefinedEntityType.Vendor, obtainedRdeType.DefinedEntityType.Namespace, obtainedRdeType.DefinedEntityType.Version) + check.Assert(err, NotNil) + check.Assert(strings.Contains(err.Error(), ErrorEntityNotFound.Error()), Equals, true) // We don't want to update the name nor the schema. It should populate them from the receiver object automatically - err = obtainedRdeType.Update(types.DefinedEntityType{ - Description: obtainedRdeType.DefinedEntityType.Description + "Updated", + err = createdRdeType.Update(types.DefinedEntityType{ + Description: rdeTypeToCreate.Description + "Updated", }) check.Assert(err, IsNil) - check.Assert(obtainedRdeType.DefinedEntityType.Description, Equals, rdeTypeToCreate.Description+"Updated") + check.Assert(createdRdeType.DefinedEntityType.Description, Equals, rdeTypeToCreate.Description+"Updated") deletedId := createdRdeType.DefinedEntityType.ID err = createdRdeType.Delete() From 4fbc8752640fdc18acc5c96f11515e64d233838c Mon Sep 17 00:00:00 2001 From: abarreiro Date: Fri, 27 Jan 2023 13:08:12 +0100 Subject: [PATCH 18/30] # Signed-off-by: abarreiro --- .changes/v2.20.0/545-features.md | 2 +- govcd/defined_entity.go | 13 ++++++++----- govcd/defined_entity_test.go | 10 +++++++--- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/.changes/v2.20.0/545-features.md b/.changes/v2.20.0/545-features.md index 1c6057133..32de437d1 100644 --- a/.changes/v2.20.0/545-features.md +++ b/.changes/v2.20.0/545-features.md @@ -1,3 +1,3 @@ -* Added support for Runtime Defined Entity types with client methods `VCDClient.CreateRdeType`, `VCDClient.GetAllRdeTypes`, +* Added support for Runtime Defined Entity Types with client methods `VCDClient.CreateRdeType`, `VCDClient.GetAllRdeTypes`, `VCDClient.GetRdeType`, `VCDClient.GetRdeTypeById` and methods to manipulate them `DefinedEntityType.Update`, `DefinedEntityType.Delete` [GH-545] diff --git a/govcd/defined_entity.go b/govcd/defined_entity.go index e6b88ee1c..6cea12084 100644 --- a/govcd/defined_entity.go +++ b/govcd/defined_entity.go @@ -17,7 +17,7 @@ type DefinedEntityType struct { } // CreateRdeType creates a Runtime Defined Entity Type. -// Only System administrator can create RDE Types. +// Only a System administrator can create RDE Types. func (vcdClient *VCDClient) CreateRdeType(rde *types.DefinedEntityType) (*DefinedEntityType, error) { client := vcdClient.Client @@ -45,7 +45,8 @@ func (vcdClient *VCDClient) CreateRdeType(rde *types.DefinedEntityType) (*Define return result, nil } -// GetAllRdeTypes retrieves all Runtime Defined Entity Types. Query parameters can be supplied to perform additional filtering. +// GetAllRdeTypes retrieves all Runtime Defined Entity Types. The number of items in the returned slice will depend on the rights +// that the user has. Query parameters can be supplied to perform additional filtering. func (vcdClient *VCDClient) GetAllRdeTypes(queryParameters url.Values) ([]*DefinedEntityType, error) { client := vcdClient.Client endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEntityTypes @@ -78,6 +79,7 @@ func (vcdClient *VCDClient) GetAllRdeTypes(queryParameters url.Values) ([]*Defin } // GetRdeType gets a Runtime Defined Entity Type by its unique combination of vendor, namespace and version. +// Only a user with enough rights can get RDE Types. func (vcdClient *VCDClient) GetRdeType(vendor, namespace, version string) (*DefinedEntityType, error) { queryParameters := url.Values{} queryParameters.Add("filter", fmt.Sprintf("vendor==%s;nss==%s;version==%s", vendor, namespace, version)) @@ -97,7 +99,8 @@ func (vcdClient *VCDClient) GetRdeType(vendor, namespace, version string) (*Defi return rdeTypes[0], nil } -// GetRdeTypeById gets a Runtime Defined Entity Type by its ID +// GetRdeTypeById gets a Runtime Defined Entity Type by its ID. +// Only a user with enough rights can get RDE Types. func (vcdClient *VCDClient) GetRdeTypeById(id string) (*DefinedEntityType, error) { client := vcdClient.Client endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEntityTypes @@ -125,7 +128,7 @@ func (vcdClient *VCDClient) GetRdeTypeById(id string) (*DefinedEntityType, error } // Update updates the receiver Runtime Defined Entity Type with the values given by the input. -// Only System administrator can update RDE Types. +// Only a user with enough rights can delete RDE Types. func (rdeType *DefinedEntityType) Update(rdeTypeToUpdate types.DefinedEntityType) error { client := rdeType.client if rdeType.DefinedEntityType.ID == "" { @@ -165,7 +168,7 @@ func (rdeType *DefinedEntityType) Update(rdeTypeToUpdate types.DefinedEntityType } // Delete deletes the receiver Runtime Defined Entity Type. -// Only System administrator can delete RDE Types. +// Only a user with enough rights can delete RDE Types. func (rdeType *DefinedEntityType) Delete() error { client := rdeType.client if rdeType.DefinedEntityType.ID == "" { diff --git a/govcd/defined_entity_test.go b/govcd/defined_entity_test.go index 56e7049af..713b6f467 100644 --- a/govcd/defined_entity_test.go +++ b/govcd/defined_entity_test.go @@ -52,13 +52,16 @@ func (vcd *TestVCD) Test_RdeType(check *C) { // Then we create a new RDE Type with System administrator. // Can't put check.TestName() in namespace due to a bug in VCD 10.4.1 that causes RDEs to fail on GET once created with special characters like "." + vendor := "vmware" + nss := strings.ReplaceAll(check.TestName()+"name", ".", "") + version := "1.2.3" rdeTypeToCreate := &types.DefinedEntityType{ Name: check.TestName(), - Namespace: strings.ReplaceAll(check.TestName()+"name", ".", ""), - Version: "1.2.3", + Namespace: nss, + Version: version, Description: "Description of " + check.TestName(), Schema: unmarshaledRdeTypeSchema, - Vendor: "vmware", + Vendor: vendor, Interfaces: []string{"urn:vcloud:interface:vmware:k8s:1.0.0"}, } createdRdeType, err := systemAdministratorClient.CreateRdeType(rdeTypeToCreate) @@ -122,6 +125,7 @@ func (vcd *TestVCD) Test_RdeType(check *C) { check.Assert(err, IsNil) check.Assert(createdRdeType.DefinedEntityType.Description, Equals, rdeTypeToCreate.Description+"Updated") + // We delete it with Sysadmin deletedId := createdRdeType.DefinedEntityType.ID err = createdRdeType.Delete() check.Assert(err, IsNil) From e3b1486a6565ae69f6b6733f02e87925170296b3 Mon Sep 17 00:00:00 2001 From: abarreiro Date: Fri, 27 Jan 2023 13:13:11 +0100 Subject: [PATCH 19/30] Rollback latest commit Signed-off-by: abarreiro --- govcd/defined_entity.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/govcd/defined_entity.go b/govcd/defined_entity.go index 6cea12084..fd7df1136 100644 --- a/govcd/defined_entity.go +++ b/govcd/defined_entity.go @@ -45,8 +45,7 @@ func (vcdClient *VCDClient) CreateRdeType(rde *types.DefinedEntityType) (*Define return result, nil } -// GetAllRdeTypes retrieves all Runtime Defined Entity Types. The number of items in the returned slice will depend on the rights -// that the user has. Query parameters can be supplied to perform additional filtering. +// GetAllRdeTypes retrieves all Runtime Defined Entity Types. Query parameters can be supplied to perform additional filtering. func (vcdClient *VCDClient) GetAllRdeTypes(queryParameters url.Values) ([]*DefinedEntityType, error) { client := vcdClient.Client endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEntityTypes @@ -79,7 +78,6 @@ func (vcdClient *VCDClient) GetAllRdeTypes(queryParameters url.Values) ([]*Defin } // GetRdeType gets a Runtime Defined Entity Type by its unique combination of vendor, namespace and version. -// Only a user with enough rights can get RDE Types. func (vcdClient *VCDClient) GetRdeType(vendor, namespace, version string) (*DefinedEntityType, error) { queryParameters := url.Values{} queryParameters.Add("filter", fmt.Sprintf("vendor==%s;nss==%s;version==%s", vendor, namespace, version)) @@ -100,7 +98,6 @@ func (vcdClient *VCDClient) GetRdeType(vendor, namespace, version string) (*Defi } // GetRdeTypeById gets a Runtime Defined Entity Type by its ID. -// Only a user with enough rights can get RDE Types. func (vcdClient *VCDClient) GetRdeTypeById(id string) (*DefinedEntityType, error) { client := vcdClient.Client endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointEntityTypes @@ -128,7 +125,6 @@ func (vcdClient *VCDClient) GetRdeTypeById(id string) (*DefinedEntityType, error } // Update updates the receiver Runtime Defined Entity Type with the values given by the input. -// Only a user with enough rights can delete RDE Types. func (rdeType *DefinedEntityType) Update(rdeTypeToUpdate types.DefinedEntityType) error { client := rdeType.client if rdeType.DefinedEntityType.ID == "" { @@ -168,7 +164,7 @@ func (rdeType *DefinedEntityType) Update(rdeTypeToUpdate types.DefinedEntityType } // Delete deletes the receiver Runtime Defined Entity Type. -// Only a user with enough rights can delete RDE Types. +// Only a System administrator can delete RDE Types. func (rdeType *DefinedEntityType) Delete() error { client := rdeType.client if rdeType.DefinedEntityType.ID == "" { From aff2957043d3dcf8ac3a584c3fcf10b54af667d5 Mon Sep 17 00:00:00 2001 From: abarreiro Date: Fri, 27 Jan 2023 13:15:09 +0100 Subject: [PATCH 20/30] Fix comments Signed-off-by: abarreiro --- govcd/defined_entity_test.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/govcd/defined_entity_test.go b/govcd/defined_entity_test.go index 713b6f467..a92234d86 100644 --- a/govcd/defined_entity_test.go +++ b/govcd/defined_entity_test.go @@ -44,8 +44,7 @@ func (vcd *TestVCD) Test_RdeType(check *C) { check.Assert(err, IsNil) alreadyPresentRdes := len(allRdeTypesBySystemAdmin) - // For the tenant, it should return 0 RDE Types, but no error. This is because our tenant user doesn't have - // the required rights to see the RDE Types. + // For the tenant, it returns 0 RDE Types, but no error. allRdeTypesByTenant, err := tenantUserClient.GetAllRdeTypes(nil) check.Assert(err, IsNil) check.Assert(len(allRdeTypesByTenant), Equals, 0) @@ -99,12 +98,12 @@ func (vcd *TestVCD) Test_RdeType(check *C) { check.Assert(err, IsNil) check.Assert(len(allRdeTypesByTenant), Equals, 0) - // Test the multiple ways of getting a Defined Interface in both users. + // Test the multiple ways of getting a RDE Types in both users. obtainedRdeType, err := systemAdministratorClient.GetRdeTypeById(createdRdeType.DefinedEntityType.ID) check.Assert(err, IsNil) check.Assert(*obtainedRdeType.DefinedEntityType, DeepEquals, *createdRdeType.DefinedEntityType) - // The RDE Type is unreachable as user doesn't have permissions + // The RDE Type is unreachable as tenant _, err = tenantUserClient.GetRdeTypeById(createdRdeType.DefinedEntityType.ID) check.Assert(err, NotNil) check.Assert(strings.Contains(err.Error(), ErrorEntityNotFound.Error()), Equals, true) @@ -113,7 +112,7 @@ func (vcd *TestVCD) Test_RdeType(check *C) { check.Assert(err, IsNil) check.Assert(*obtainedRdeType2.DefinedEntityType, DeepEquals, *obtainedRdeType.DefinedEntityType) - // The RDE Type is unreachable as user doesn't have permissions + // The RDE Type is unreachable as tenant _, err = tenantUserClient.GetRdeType(obtainedRdeType.DefinedEntityType.Vendor, obtainedRdeType.DefinedEntityType.Namespace, obtainedRdeType.DefinedEntityType.Version) check.Assert(err, NotNil) check.Assert(strings.Contains(err.Error(), ErrorEntityNotFound.Error()), Equals, true) From 7897340a58a8031eeb840d44028872eeb35ebb1f Mon Sep 17 00:00:00 2001 From: abarreiro Date: Mon, 30 Jan 2023 16:14:42 +0100 Subject: [PATCH 21/30] Improve changelog Signed-off-by: abarreiro --- .changes/v2.20.0/527-features.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changes/v2.20.0/527-features.md b/.changes/v2.20.0/527-features.md index b9cd0c303..2d73d2132 100644 --- a/.changes/v2.20.0/527-features.md +++ b/.changes/v2.20.0/527-features.md @@ -1,3 +1,3 @@ -* Added support for Defined Interfaces with client methods `VCDClient.CreateDefinedInterface`, `VCDClient.GetAllDefinedInterfaces`, +* Added support for Runtime Defined Entity Interfaces with client methods `VCDClient.CreateDefinedInterface`, `VCDClient.GetAllDefinedInterfaces`, `VCDClient.GetDefinedInterface`, `VCDClient.GetDefinedInterfaceById` and methods to manipulate them `DefinedInterface.Update`, `DefinedInterface.Delete` [GH-527] From da460ebd2f21a319797353d667e3a7b17d48e64b Mon Sep 17 00:00:00 2001 From: abarreiro Date: Tue, 31 Jan 2023 11:09:27 +0100 Subject: [PATCH 22/30] Apply PR suggestions Signed-off-by: abarreiro --- govcd/defined_interface.go | 12 ++++++------ govcd/defined_interface_test.go | 6 +++--- govcd/openapi_endpoints.go | 2 +- types/v56/constants.go | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/govcd/defined_interface.go b/govcd/defined_interface.go index bea03e8bc..a21c819ef 100644 --- a/govcd/defined_interface.go +++ b/govcd/defined_interface.go @@ -1,5 +1,5 @@ /* - * Copyright 2022 VMware, Inc. All rights reserved. Licensed under the Apache v2 License. + * Copyright 2023 VMware, Inc. All rights reserved. Licensed under the Apache v2 License. */ package govcd @@ -21,7 +21,7 @@ type DefinedInterface struct { // Only System administrator can create Defined Interfaces. func (vcdClient *VCDClient) CreateDefinedInterface(definedInterface *types.DefinedInterface) (*DefinedInterface, error) { client := vcdClient.Client - endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointInterfaces + endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointRdeInterfaces apiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint) if err != nil { return nil, err @@ -48,7 +48,7 @@ func (vcdClient *VCDClient) CreateDefinedInterface(definedInterface *types.Defin // GetAllDefinedInterfaces retrieves all Defined Interfaces. Query parameters can be supplied to perform additional filtering. func (vcdClient *VCDClient) GetAllDefinedInterfaces(queryParameters url.Values) ([]*DefinedInterface, error) { client := vcdClient.Client - endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointInterfaces + endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointRdeInterfaces apiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint) if err != nil { return nil, err @@ -101,7 +101,7 @@ func (vcdClient *VCDClient) GetDefinedInterface(vendor, namespace, version strin func (vcdClient *VCDClient) GetDefinedInterfaceById(id string) (*DefinedInterface, error) { client := vcdClient.Client - endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointInterfaces + endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointRdeInterfaces apiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint) if err != nil { return nil, err @@ -138,7 +138,7 @@ func (di *DefinedInterface) Update(definedInterface types.DefinedInterface) erro return fmt.Errorf("ID of the receiver Defined Interface and the input ID don't match") } - endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointInterfaces + endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointRdeInterfaces apiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint) if err != nil { return err @@ -166,7 +166,7 @@ func (di *DefinedInterface) Delete() error { return fmt.Errorf("ID of the receiver Defined Interface is empty") } - endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointInterfaces + endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointRdeInterfaces apiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint) if err != nil { return err diff --git a/govcd/defined_interface_test.go b/govcd/defined_interface_test.go index d14e7544d..b79edf367 100644 --- a/govcd/defined_interface_test.go +++ b/govcd/defined_interface_test.go @@ -2,7 +2,7 @@ // +build functional openapi rde ALL /* - * Copyright 2022 VMware, Inc. All rights reserved. Licensed under the Apache v2 License. + * Copyright 2023 VMware, Inc. All rights reserved. Licensed under the Apache v2 License. */ package govcd @@ -20,7 +20,7 @@ func (vcd *TestVCD) Test_DefinedInterface(check *C) { if vcd.skipAdminTests { check.Skip(fmt.Sprintf(TestRequiresSysAdminPrivileges, check.TestName())) } - skipOpenApiEndpointTest(vcd, check, types.OpenApiPathVersion1_0_0+types.OpenApiEndpointInterfaces) + skipOpenApiEndpointTest(vcd, check, types.OpenApiPathVersion1_0_0+types.OpenApiEndpointRdeInterfaces) if len(vcd.config.Tenants) == 0 { check.Skip("skipping as there is no configured tenant users") } @@ -56,7 +56,7 @@ func (vcd *TestVCD) Test_DefinedInterface(check *C) { check.Assert(newDefinedInterfaceFromSysAdmin.DefinedInterface.Version, Equals, dummyRde.Version) check.Assert(newDefinedInterfaceFromSysAdmin.DefinedInterface.Vendor, Equals, dummyRde.Vendor) check.Assert(newDefinedInterfaceFromSysAdmin.DefinedInterface.IsReadOnly, Equals, dummyRde.IsReadOnly) - AddToCleanupListOpenApi(newDefinedInterfaceFromSysAdmin.DefinedInterface.ID, check.TestName(), types.OpenApiPathVersion1_0_0+types.OpenApiEndpointInterfaces+newDefinedInterfaceFromSysAdmin.DefinedInterface.ID) + AddToCleanupListOpenApi(newDefinedInterfaceFromSysAdmin.DefinedInterface.ID, check.TestName(), types.OpenApiPathVersion1_0_0+types.OpenApiEndpointRdeInterfaces+newDefinedInterfaceFromSysAdmin.DefinedInterface.ID) // Tenants can't create Defined Interfaces. We replace the dots in both // namespace and name as API is quirky at versions of VCD < 37.0 diff --git a/govcd/openapi_endpoints.go b/govcd/openapi_endpoints.go index 5bb47b70f..1de51c0da 100644 --- a/govcd/openapi_endpoints.go +++ b/govcd/openapi_endpoints.go @@ -54,7 +54,7 @@ var endpointMinApiVersions = map[string]string{ types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointSecurityTags: "36.0", // VCD 10.3+ types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointNsxtRouteAdvertisement: "34.0", // VCD 10.1+ types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointLogicalVmGroups: "35.0", // VCD 10.2+ - types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointInterfaces: "35.0", // VCD 10.2+ + types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointRdeInterfaces: "35.0", // VCD 10.2+ // NSX-T ALB (Advanced/AVI Load Balancer) support was introduced in 10.2 types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointAlbController: "35.0", // VCD 10.2+ diff --git a/types/v56/constants.go b/types/v56/constants.go index 23c2efa5e..1800738ed 100644 --- a/types/v56/constants.go +++ b/types/v56/constants.go @@ -389,7 +389,7 @@ const ( OpenApiEndpointEdgeBgpNeighbor = "edgeGateways/%s/routing/bgp/neighbors/" // '%s' is NSX-T Edge Gateway ID OpenApiEndpointEdgeBgpConfigPrefixLists = "edgeGateways/%s/routing/bgp/prefixLists/" // '%s' is NSX-T Edge Gateway ID OpenApiEndpointEdgeBgpConfig = "edgeGateways/%s/routing/bgp" // '%s' is NSX-T Edge Gateway ID - OpenApiEndpointInterfaces = "interfaces/" + OpenApiEndpointRdeInterfaces = "interfaces/" // NSX-T ALB related endpoints From ffaab14af8d4c9b059ea1ab524407437e180a332 Mon Sep 17 00:00:00 2001 From: abarreiro Date: Tue, 31 Jan 2023 12:26:32 +0100 Subject: [PATCH 23/30] Add comment Signed-off-by: abarreiro --- govcd/defined_interface.go | 1 + 1 file changed, 1 insertion(+) diff --git a/govcd/defined_interface.go b/govcd/defined_interface.go index a21c819ef..07c44afbf 100644 --- a/govcd/defined_interface.go +++ b/govcd/defined_interface.go @@ -12,6 +12,7 @@ import ( ) // DefinedInterface is a type for handling Defined Interfaces, from the Runtime Defined Entities framework, in VCD. +// This is often referred as Runtime Defined Entity Interface or RDE Interface in documentation. type DefinedInterface struct { DefinedInterface *types.DefinedInterface client *Client From c2b053fd72f4b0f1fc0c6b5f0d1e16d2a747db33 Mon Sep 17 00:00:00 2001 From: abarreiro Date: Tue, 31 Jan 2023 12:30:29 +0100 Subject: [PATCH 24/30] Add clarification comment Signed-off-by: abarreiro --- govcd/defined_interface.go | 1 + 1 file changed, 1 insertion(+) diff --git a/govcd/defined_interface.go b/govcd/defined_interface.go index a21c819ef..07c44afbf 100644 --- a/govcd/defined_interface.go +++ b/govcd/defined_interface.go @@ -12,6 +12,7 @@ import ( ) // DefinedInterface is a type for handling Defined Interfaces, from the Runtime Defined Entities framework, in VCD. +// This is often referred as Runtime Defined Entity Interface or RDE Interface in documentation. type DefinedInterface struct { DefinedInterface *types.DefinedInterface client *Client From a081901c3ad8f3210f2a9ce522589bb735c7b858 Mon Sep 17 00:00:00 2001 From: abarreiro Date: Tue, 7 Feb 2023 12:44:14 +0100 Subject: [PATCH 25/30] Change namespace to nss Signed-off-by: abarreiro --- govcd/defined_interface.go | 10 +++++----- govcd/defined_interface_test.go | 26 +++++++++++++------------- types/v56/openapi.go | 2 +- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/govcd/defined_interface.go b/govcd/defined_interface.go index 07c44afbf..20670c1ed 100644 --- a/govcd/defined_interface.go +++ b/govcd/defined_interface.go @@ -78,21 +78,21 @@ func (vcdClient *VCDClient) GetAllDefinedInterfaces(queryParameters url.Values) return returnRDEs, nil } -// GetDefinedInterface retrieves a single Defined Interface defined by its unique combination of vendor, namespace and version. -func (vcdClient *VCDClient) GetDefinedInterface(vendor, namespace, version string) (*DefinedInterface, error) { +// GetDefinedInterface retrieves a single Defined Interface defined by its unique combination of vendor, nss and version. +func (vcdClient *VCDClient) GetDefinedInterface(vendor, nss, version string) (*DefinedInterface, error) { queryParameters := url.Values{} - queryParameters.Add("filter", fmt.Sprintf("vendor==%s;nss==%s;version==%s", vendor, namespace, version)) + queryParameters.Add("filter", fmt.Sprintf("vendor==%s;nss==%s;version==%s", vendor, nss, version)) interfaces, err := vcdClient.GetAllDefinedInterfaces(queryParameters) if err != nil { return nil, err } if len(interfaces) == 0 { - return nil, fmt.Errorf("%s could not find the Defined Interface with vendor %s, namespace %s and version %s", ErrorEntityNotFound, vendor, namespace, version) + return nil, fmt.Errorf("%s could not find the Defined Interface with vendor %s, nss %s and version %s", ErrorEntityNotFound, vendor, nss, version) } if len(interfaces) > 1 { - return nil, fmt.Errorf("found more than 1 Defined Interface with vendor %s, namespace %s and version %s", vendor, namespace, version) + return nil, fmt.Errorf("found more than 1 Defined Interface with vendor %s, nss %s and version %s", vendor, nss, version) } return interfaces[0], nil diff --git a/govcd/defined_interface_test.go b/govcd/defined_interface_test.go index b79edf367..d34521499 100644 --- a/govcd/defined_interface_test.go +++ b/govcd/defined_interface_test.go @@ -41,30 +41,30 @@ func (vcd *TestVCD) Test_DefinedInterface(check *C) { check.Assert(len(allDefinedInterfacesByTenant), Equals, len(allDefinedInterfacesBySysAdmin)) // Then we create a new Defined Interface with System administrator. We replace the dots in both - // namespace and name as API is quirky at versions of VCD < 37.0 + // nss and name as API is quirky at versions of VCD < 37.0 dummyRde := &types.DefinedInterface{ - Name: strings.ReplaceAll(check.TestName()+"name", ".", ""), - Namespace: strings.ReplaceAll(check.TestName()+"nss", ".", ""), - Version: "1.2.3", - Vendor: "vmware", + Name: strings.ReplaceAll(check.TestName()+"name", ".", ""), + Nss: strings.ReplaceAll(check.TestName()+"nss", ".", ""), + Version: "1.2.3", + Vendor: "vmware", } newDefinedInterfaceFromSysAdmin, err := systemAdministratorClient.CreateDefinedInterface(dummyRde) check.Assert(err, IsNil) check.Assert(newDefinedInterfaceFromSysAdmin, NotNil) check.Assert(newDefinedInterfaceFromSysAdmin.DefinedInterface.Name, Equals, dummyRde.Name) - check.Assert(newDefinedInterfaceFromSysAdmin.DefinedInterface.Namespace, Equals, dummyRde.Namespace) + check.Assert(newDefinedInterfaceFromSysAdmin.DefinedInterface.Nss, Equals, dummyRde.Nss) check.Assert(newDefinedInterfaceFromSysAdmin.DefinedInterface.Version, Equals, dummyRde.Version) check.Assert(newDefinedInterfaceFromSysAdmin.DefinedInterface.Vendor, Equals, dummyRde.Vendor) check.Assert(newDefinedInterfaceFromSysAdmin.DefinedInterface.IsReadOnly, Equals, dummyRde.IsReadOnly) AddToCleanupListOpenApi(newDefinedInterfaceFromSysAdmin.DefinedInterface.ID, check.TestName(), types.OpenApiPathVersion1_0_0+types.OpenApiEndpointRdeInterfaces+newDefinedInterfaceFromSysAdmin.DefinedInterface.ID) // Tenants can't create Defined Interfaces. We replace the dots in both - // namespace and name as API is quirky at versions of VCD < 37.0 + // nss and name as API is quirky at versions of VCD < 37.0 nilDefinedInterface, err := tenantUserClient.CreateDefinedInterface(&types.DefinedInterface{ - Name: strings.ReplaceAll(check.TestName()+"name2", ".", ""), - Namespace: strings.ReplaceAll(check.TestName()+"name2", ".", ""), - Version: "4.5.6", - Vendor: "vmware", + Name: strings.ReplaceAll(check.TestName()+"name2", ".", ""), + Nss: strings.ReplaceAll(check.TestName()+"name2", ".", ""), + Version: "4.5.6", + Vendor: "vmware", }) check.Assert(err, NotNil) check.Assert(nilDefinedInterface, IsNil) @@ -88,11 +88,11 @@ func (vcd *TestVCD) Test_DefinedInterface(check *C) { check.Assert(err, IsNil) check.Assert(*obtainedDefinedInterface.DefinedInterface, DeepEquals, *newDefinedInterfaceFromSysAdmin.DefinedInterface) - obtainedDefinedInterface2, err := systemAdministratorClient.GetDefinedInterface(obtainedDefinedInterface.DefinedInterface.Vendor, obtainedDefinedInterface.DefinedInterface.Namespace, obtainedDefinedInterface.DefinedInterface.Version) + obtainedDefinedInterface2, err := systemAdministratorClient.GetDefinedInterface(obtainedDefinedInterface.DefinedInterface.Vendor, obtainedDefinedInterface.DefinedInterface.Nss, obtainedDefinedInterface.DefinedInterface.Version) check.Assert(err, IsNil) check.Assert(*obtainedDefinedInterface2.DefinedInterface, DeepEquals, *obtainedDefinedInterface.DefinedInterface) - obtainedDefinedInterface2, err = tenantUserClient.GetDefinedInterface(obtainedDefinedInterface.DefinedInterface.Vendor, obtainedDefinedInterface.DefinedInterface.Namespace, obtainedDefinedInterface.DefinedInterface.Version) + obtainedDefinedInterface2, err = tenantUserClient.GetDefinedInterface(obtainedDefinedInterface.DefinedInterface.Vendor, obtainedDefinedInterface.DefinedInterface.Nss, obtainedDefinedInterface.DefinedInterface.Version) check.Assert(err, IsNil) check.Assert(*obtainedDefinedInterface2.DefinedInterface, DeepEquals, *obtainedDefinedInterface.DefinedInterface) diff --git a/types/v56/openapi.go b/types/v56/openapi.go index 4fab73f98..08ac450be 100644 --- a/types/v56/openapi.go +++ b/types/v56/openapi.go @@ -431,7 +431,7 @@ type LogicalVmGroup struct { type DefinedInterface struct { ID string `json:"id,omitempty"` // The id of the defined interface type in URN format Name string `json:"name,omitempty"` // The name of the defined interface - Namespace string `json:"nss,omitempty"` // A unique namespace associated with the interface + Nss string `json:"nss,omitempty"` // A unique namespace associated with the interface Version string `json:"version,omitempty"` // The interface's version. The version should follow semantic versioning rules Vendor string `json:"vendor,omitempty"` // The vendor name IsReadOnly bool `json:"readonly,omitempty"` // True if the entity type cannot be modified From 6d0e2ea1f7f79a5ac0ed60733d7168bee8c932b6 Mon Sep 17 00:00:00 2001 From: abarreiro Date: Tue, 7 Feb 2023 13:12:50 +0100 Subject: [PATCH 26/30] Change namespace to nss Signed-off-by: abarreiro --- govcd/defined_entity.go | 10 +++++----- govcd/defined_entity_test.go | 20 ++++++++++---------- types/v56/openapi.go | 2 +- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/govcd/defined_entity.go b/govcd/defined_entity.go index 86d5f9958..8b67a655a 100644 --- a/govcd/defined_entity.go +++ b/govcd/defined_entity.go @@ -77,21 +77,21 @@ func (vcdClient *VCDClient) GetAllRdeTypes(queryParameters url.Values) ([]*Defin return returnRDEs, nil } -// GetRdeType gets a Runtime Defined Entity Type by its unique combination of vendor, namespace and version. -func (vcdClient *VCDClient) GetRdeType(vendor, namespace, version string) (*DefinedEntityType, error) { +// GetRdeType gets a Runtime Defined Entity Type by its unique combination of vendor, nss and version. +func (vcdClient *VCDClient) GetRdeType(vendor, nss, version string) (*DefinedEntityType, error) { queryParameters := url.Values{} - queryParameters.Add("filter", fmt.Sprintf("vendor==%s;nss==%s;version==%s", vendor, namespace, version)) + queryParameters.Add("filter", fmt.Sprintf("vendor==%s;nss==%s;version==%s", vendor, nss, version)) rdeTypes, err := vcdClient.GetAllRdeTypes(queryParameters) if err != nil { return nil, err } if len(rdeTypes) == 0 { - return nil, fmt.Errorf("%s could not find the Runtime Defined Entity Type with vendor %s, namespace %s and version %s", ErrorEntityNotFound, vendor, namespace, version) + return nil, fmt.Errorf("%s could not find the Runtime Defined Entity Type with vendor %s, nss %s and version %s", ErrorEntityNotFound, vendor, nss, version) } if len(rdeTypes) > 1 { - return nil, fmt.Errorf("found more than 1 Runtime Defined Entity Type with vendor %s, namespace %s and version %s", vendor, namespace, version) + return nil, fmt.Errorf("found more than 1 Runtime Defined Entity Type with vendor %s, nss %s and version %s", vendor, nss, version) } return rdeTypes[0], nil diff --git a/govcd/defined_entity_test.go b/govcd/defined_entity_test.go index 61a6bfe8e..3200a4227 100644 --- a/govcd/defined_entity_test.go +++ b/govcd/defined_entity_test.go @@ -50,13 +50,13 @@ func (vcd *TestVCD) Test_RdeType(check *C) { check.Assert(len(allRdeTypesByTenant), Equals, 0) // Then we create a new RDE Type with System administrator. - // Can't put check.TestName() in namespace due to a bug in VCD 10.4.1 that causes RDEs to fail on GET once created with special characters like "." + // Can't put check.TestName() in nss due to a bug in VCD 10.4.1 that causes RDEs to fail on GET once created with special characters like "." vendor := "vmware" nss := strings.ReplaceAll(check.TestName()+"name", ".", "") version := "1.2.3" rdeTypeToCreate := &types.DefinedEntityType{ Name: check.TestName(), - Namespace: nss, + Nss: nss, Version: version, Description: "Description of " + check.TestName(), Schema: unmarshaledRdeTypeSchema, @@ -67,7 +67,7 @@ func (vcd *TestVCD) Test_RdeType(check *C) { check.Assert(err, IsNil) check.Assert(createdRdeType, NotNil) check.Assert(createdRdeType.DefinedEntityType.Name, Equals, rdeTypeToCreate.Name) - check.Assert(createdRdeType.DefinedEntityType.Namespace, Equals, rdeTypeToCreate.Namespace) + check.Assert(createdRdeType.DefinedEntityType.Nss, Equals, rdeTypeToCreate.Nss) check.Assert(createdRdeType.DefinedEntityType.Version, Equals, rdeTypeToCreate.Version) check.Assert(createdRdeType.DefinedEntityType.Schema, NotNil) check.Assert(createdRdeType.DefinedEntityType.Schema["type"], Equals, "object") @@ -78,11 +78,11 @@ func (vcd *TestVCD) Test_RdeType(check *C) { // Tenants can't create RDE Types nilRdeType, err := tenantUserClient.CreateRdeType(&types.DefinedEntityType{ - Name: check.TestName(), - Namespace: "notworking", - Version: "4.5.6", - Schema: unmarshaledRdeTypeSchema, - Vendor: "willfail", + Name: check.TestName(), + Nss: "notworking", + Version: "4.5.6", + Schema: unmarshaledRdeTypeSchema, + Vendor: "willfail", }) check.Assert(err, NotNil) check.Assert(nilRdeType, IsNil) @@ -108,12 +108,12 @@ func (vcd *TestVCD) Test_RdeType(check *C) { check.Assert(err, NotNil) check.Assert(strings.Contains(err.Error(), ErrorEntityNotFound.Error()), Equals, true) - obtainedRdeType2, err := systemAdministratorClient.GetRdeType(obtainedRdeType.DefinedEntityType.Vendor, obtainedRdeType.DefinedEntityType.Namespace, obtainedRdeType.DefinedEntityType.Version) + obtainedRdeType2, err := systemAdministratorClient.GetRdeType(obtainedRdeType.DefinedEntityType.Vendor, obtainedRdeType.DefinedEntityType.Nss, obtainedRdeType.DefinedEntityType.Version) check.Assert(err, IsNil) check.Assert(*obtainedRdeType2.DefinedEntityType, DeepEquals, *obtainedRdeType.DefinedEntityType) // The RDE Type is unreachable as tenant - _, err = tenantUserClient.GetRdeType(obtainedRdeType.DefinedEntityType.Vendor, obtainedRdeType.DefinedEntityType.Namespace, obtainedRdeType.DefinedEntityType.Version) + _, err = tenantUserClient.GetRdeType(obtainedRdeType.DefinedEntityType.Vendor, obtainedRdeType.DefinedEntityType.Nss, obtainedRdeType.DefinedEntityType.Version) check.Assert(err, NotNil) check.Assert(strings.Contains(err.Error(), ErrorEntityNotFound.Error()), Equals, true) diff --git a/types/v56/openapi.go b/types/v56/openapi.go index 9299893c1..e904b4725 100644 --- a/types/v56/openapi.go +++ b/types/v56/openapi.go @@ -441,7 +441,7 @@ type DefinedInterface struct { type DefinedEntityType struct { ID string `json:"id,omitempty"` // The id of the defined entity type in URN format Name string `json:"name,omitempty"` // The name of the defined entity type - Namespace string `json:"nss,omitempty"` // A unique namespace specific string. The combination of nss and version must be unique + Nss string `json:"nss,omitempty"` // A unique namespace specific string. The combination of nss and version must be unique Version string `json:"version,omitempty"` // The version of the defined entity. The combination of nss and version must be unique. The version string must follow semantic versioning rules Description string `json:"description,omitempty"` // Description of the defined entity ExternalId string `json:"externalId,omitempty"` // An external entity’s id that this definition may apply to From 98f5d5be051687d6d3ff6ab5bb2c58d3d8cc5982 Mon Sep 17 00:00:00 2001 From: abarreiro Date: Thu, 16 Feb 2023 12:25:59 +0100 Subject: [PATCH 27/30] Add test with tenant user Signed-off-by: abarreiro --- govcd/defined_entity.go | 1 + govcd/defined_entity_test.go | 66 +++++++++++++++++++++++++++--------- 2 files changed, 51 insertions(+), 16 deletions(-) diff --git a/govcd/defined_entity.go b/govcd/defined_entity.go index 8b67a655a..19d1e7bad 100644 --- a/govcd/defined_entity.go +++ b/govcd/defined_entity.go @@ -125,6 +125,7 @@ func (vcdClient *VCDClient) GetRdeTypeById(id string) (*DefinedEntityType, error } // Update updates the receiver Runtime Defined Entity Type with the values given by the input. +// Only a System administrator can create RDE Types. func (rdeType *DefinedEntityType) Update(rdeTypeToUpdate types.DefinedEntityType) error { client := rdeType.client if rdeType.DefinedEntityType.ID == "" { diff --git a/govcd/defined_entity_test.go b/govcd/defined_entity_test.go index 3200a4227..06df1334d 100644 --- a/govcd/defined_entity_test.go +++ b/govcd/defined_entity_test.go @@ -88,41 +88,75 @@ func (vcd *TestVCD) Test_RdeType(check *C) { check.Assert(nilRdeType, IsNil) check.Assert(strings.Contains(err.Error(), "ACCESS_TO_RESOURCE_IS_FORBIDDEN"), Equals, true) - // As we created a new one, we check the new count is correct in both System admin and Tenant user + // Assign rights to the tenant user, so it can perform following operations. + // We don't need to clean the rights afterwards as deleting the RDE Type deletes the associated bundle + // with its rights. + role, err := systemAdministratorClient.Client.GetGlobalRoleByName("Organization Administrator") + check.Assert(err, IsNil) + check.Assert(role, NotNil) + + rightsBundleName := fmt.Sprintf("%s:%s Entitlement", vendor, nss) + rightsBundle, err := systemAdministratorClient.Client.GetRightsBundleByName(rightsBundleName) + check.Assert(err, IsNil) + check.Assert(rightsBundle, NotNil) + + err = rightsBundle.PublishAllTenants() + check.Assert(err, IsNil) + + rights, err := rightsBundle.GetRights(nil) + check.Assert(err, IsNil) + check.Assert(len(rights), Not(Equals), 0) + + var rightsToAdd []types.OpenApiReference + for _, right := range rights { + if strings.Contains(right.Name, fmt.Sprintf("%s:%s", vendor, nss)) { + rightsToAdd = append(rightsToAdd, types.OpenApiReference{ + Name: right.Name, + ID: right.ID, + }) + } + } + check.Assert(rightsToAdd, NotNil) + check.Assert(len(rightsToAdd), Not(Equals), 0) + + err = role.AddRights(rightsToAdd) + check.Assert(err, IsNil) + + // As we created a new RDE Type, we check the new count is correct in both System admin and Tenant user allRdeTypesBySystemAdmin, err = systemAdministratorClient.GetAllRdeTypes(nil) check.Assert(err, IsNil) check.Assert(len(allRdeTypesBySystemAdmin), Equals, alreadyPresentRdes+1) - // Count should be still 0 + // Count is 1 for tenant user as it can only retrieve the created type as per the assigned rights above. allRdeTypesByTenant, err = tenantUserClient.GetAllRdeTypes(nil) check.Assert(err, IsNil) - check.Assert(len(allRdeTypesByTenant), Equals, 0) + check.Assert(len(allRdeTypesByTenant), Equals, 1) // Test the multiple ways of getting a RDE Types in both users. obtainedRdeType, err := systemAdministratorClient.GetRdeTypeById(createdRdeType.DefinedEntityType.ID) check.Assert(err, IsNil) check.Assert(*obtainedRdeType.DefinedEntityType, DeepEquals, *createdRdeType.DefinedEntityType) - // The RDE Type is unreachable as tenant - _, err = tenantUserClient.GetRdeTypeById(createdRdeType.DefinedEntityType.ID) - check.Assert(err, NotNil) - check.Assert(strings.Contains(err.Error(), ErrorEntityNotFound.Error()), Equals, true) + // The RDE Type retrieved by the tenant should be the same as the retrieved by Sysadmin + obtainedRdeTypeByTenant, err := tenantUserClient.GetRdeTypeById(createdRdeType.DefinedEntityType.ID) + check.Assert(err, IsNil) + check.Assert(*obtainedRdeTypeByTenant.DefinedEntityType, DeepEquals, *obtainedRdeType.DefinedEntityType) - obtainedRdeType2, err := systemAdministratorClient.GetRdeType(obtainedRdeType.DefinedEntityType.Vendor, obtainedRdeType.DefinedEntityType.Nss, obtainedRdeType.DefinedEntityType.Version) + obtainedRdeType, err = systemAdministratorClient.GetRdeType(createdRdeType.DefinedEntityType.Vendor, createdRdeType.DefinedEntityType.Nss, createdRdeType.DefinedEntityType.Version) check.Assert(err, IsNil) - check.Assert(*obtainedRdeType2.DefinedEntityType, DeepEquals, *obtainedRdeType.DefinedEntityType) + check.Assert(*obtainedRdeType.DefinedEntityType, DeepEquals, *obtainedRdeType.DefinedEntityType) - // The RDE Type is unreachable as tenant - _, err = tenantUserClient.GetRdeType(obtainedRdeType.DefinedEntityType.Vendor, obtainedRdeType.DefinedEntityType.Nss, obtainedRdeType.DefinedEntityType.Version) - check.Assert(err, NotNil) - check.Assert(strings.Contains(err.Error(), ErrorEntityNotFound.Error()), Equals, true) + // The RDE Type retrieved by the tenant should be the same as the retrieved by Sysadmin + obtainedRdeTypeByTenant, err = tenantUserClient.GetRdeType(createdRdeType.DefinedEntityType.Vendor, createdRdeType.DefinedEntityType.Nss, createdRdeType.DefinedEntityType.Version) + check.Assert(err, IsNil) + check.Assert(*obtainedRdeTypeByTenant.DefinedEntityType, DeepEquals, *obtainedRdeType.DefinedEntityType) // We don't want to update the name nor the schema. It should populate them from the receiver object automatically - err = createdRdeType.Update(types.DefinedEntityType{ - Description: rdeTypeToCreate.Description + "Updated", + err = obtainedRdeType.Update(types.DefinedEntityType{ + Description: rdeTypeToCreate.Description + "UpdatedByAdmin", }) check.Assert(err, IsNil) - check.Assert(createdRdeType.DefinedEntityType.Description, Equals, rdeTypeToCreate.Description+"Updated") + check.Assert(obtainedRdeType.DefinedEntityType.Description, Equals, rdeTypeToCreate.Description+"UpdatedByAdmin") // We delete it with Sysadmin deletedId := createdRdeType.DefinedEntityType.ID From 13e24f206b4c16bfeba00b4096b279a8b21db75f Mon Sep 17 00:00:00 2001 From: abarreiro Date: Thu, 23 Feb 2023 10:17:24 +0100 Subject: [PATCH 28/30] Refactor var name Signed-off-by: abarreiro --- govcd/defined_entity_test.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/govcd/defined_entity_test.go b/govcd/defined_entity_test.go index 06df1334d..468894c61 100644 --- a/govcd/defined_entity_test.go +++ b/govcd/defined_entity_test.go @@ -18,7 +18,7 @@ import ( "strings" ) -// Test_Rde tests the CRUD operations for the RDE Type with both System administrator and a tenant user. +// Test_RdeType tests the CRUD operations for the RDE Type with both System administrator and a tenant user. func (vcd *TestVCD) Test_RdeType(check *C) { if vcd.skipAdminTests { check.Skip(fmt.Sprintf(TestRequiresSysAdminPrivileges, check.TestName())) @@ -133,30 +133,30 @@ func (vcd *TestVCD) Test_RdeType(check *C) { check.Assert(len(allRdeTypesByTenant), Equals, 1) // Test the multiple ways of getting a RDE Types in both users. - obtainedRdeType, err := systemAdministratorClient.GetRdeTypeById(createdRdeType.DefinedEntityType.ID) + obtainedRdeTypeBySysAdmin, err := systemAdministratorClient.GetRdeTypeById(createdRdeType.DefinedEntityType.ID) check.Assert(err, IsNil) - check.Assert(*obtainedRdeType.DefinedEntityType, DeepEquals, *createdRdeType.DefinedEntityType) + check.Assert(*obtainedRdeTypeBySysAdmin.DefinedEntityType, DeepEquals, *createdRdeType.DefinedEntityType) // The RDE Type retrieved by the tenant should be the same as the retrieved by Sysadmin obtainedRdeTypeByTenant, err := tenantUserClient.GetRdeTypeById(createdRdeType.DefinedEntityType.ID) check.Assert(err, IsNil) - check.Assert(*obtainedRdeTypeByTenant.DefinedEntityType, DeepEquals, *obtainedRdeType.DefinedEntityType) + check.Assert(*obtainedRdeTypeByTenant.DefinedEntityType, DeepEquals, *obtainedRdeTypeBySysAdmin.DefinedEntityType) - obtainedRdeType, err = systemAdministratorClient.GetRdeType(createdRdeType.DefinedEntityType.Vendor, createdRdeType.DefinedEntityType.Nss, createdRdeType.DefinedEntityType.Version) + obtainedRdeTypeBySysAdmin, err = systemAdministratorClient.GetRdeType(createdRdeType.DefinedEntityType.Vendor, createdRdeType.DefinedEntityType.Nss, createdRdeType.DefinedEntityType.Version) check.Assert(err, IsNil) - check.Assert(*obtainedRdeType.DefinedEntityType, DeepEquals, *obtainedRdeType.DefinedEntityType) + check.Assert(*obtainedRdeTypeBySysAdmin.DefinedEntityType, DeepEquals, *obtainedRdeTypeBySysAdmin.DefinedEntityType) // The RDE Type retrieved by the tenant should be the same as the retrieved by Sysadmin obtainedRdeTypeByTenant, err = tenantUserClient.GetRdeType(createdRdeType.DefinedEntityType.Vendor, createdRdeType.DefinedEntityType.Nss, createdRdeType.DefinedEntityType.Version) check.Assert(err, IsNil) - check.Assert(*obtainedRdeTypeByTenant.DefinedEntityType, DeepEquals, *obtainedRdeType.DefinedEntityType) + check.Assert(*obtainedRdeTypeByTenant.DefinedEntityType, DeepEquals, *obtainedRdeTypeBySysAdmin.DefinedEntityType) // We don't want to update the name nor the schema. It should populate them from the receiver object automatically - err = obtainedRdeType.Update(types.DefinedEntityType{ + err = obtainedRdeTypeBySysAdmin.Update(types.DefinedEntityType{ Description: rdeTypeToCreate.Description + "UpdatedByAdmin", }) check.Assert(err, IsNil) - check.Assert(obtainedRdeType.DefinedEntityType.Description, Equals, rdeTypeToCreate.Description+"UpdatedByAdmin") + check.Assert(obtainedRdeTypeBySysAdmin.DefinedEntityType.Description, Equals, rdeTypeToCreate.Description+"UpdatedByAdmin") // We delete it with Sysadmin deletedId := createdRdeType.DefinedEntityType.ID From 61760e5abad644eba891bb34d1cbdc8b253d4bd3 Mon Sep 17 00:00:00 2001 From: abarreiro Date: Fri, 3 Mar 2023 11:21:46 +0100 Subject: [PATCH 29/30] Address comments Signed-off-by: abarreiro --- govcd/defined_entity.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/govcd/defined_entity.go b/govcd/defined_entity.go index 19d1e7bad..57eeb656f 100644 --- a/govcd/defined_entity.go +++ b/govcd/defined_entity.go @@ -1,5 +1,5 @@ /* - * Copyright 2022 VMware, Inc. All rights reserved. Licensed under the Apache v2 License. + * Copyright 2023 VMware, Inc. All rights reserved. Licensed under the Apache v2 License. */ package govcd @@ -10,7 +10,8 @@ import ( "net/url" ) -// DefinedEntityType is a type for handling Runtime Defined Entity (RDE) Type definitions +// DefinedEntityType is a type for handling Runtime Defined Entity (RDE) Type definitions. +// Note. Running a few of these operations in parallel may corrupt database in VCD (at least <= 10.4.2) type DefinedEntityType struct { DefinedEntityType *types.DefinedEntityType client *Client From 06d8920670e663bcd6ada722934598a6233759f4 Mon Sep 17 00:00:00 2001 From: abarreiro Date: Fri, 10 Mar 2023 12:24:58 +0100 Subject: [PATCH 30/30] 2023 Signed-off-by: abarreiro --- govcd/defined_entity_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/govcd/defined_entity_test.go b/govcd/defined_entity_test.go index 468894c61..baafba387 100644 --- a/govcd/defined_entity_test.go +++ b/govcd/defined_entity_test.go @@ -2,7 +2,7 @@ // +build functional openapi rde ALL /* - * Copyright 2022 VMware, Inc. All rights reserved. Licensed under the Apache v2 License. + * Copyright 2023 VMware, Inc. All rights reserved. Licensed under the Apache v2 License. */ package govcd