Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add RDE metadata methods #557

Merged
merged 13 commits into from
Nov 28, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .changes/v2.22.0/557-improvements.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
* Added metadata support to Runtime Defined Entities with methods `rde.GetMetadataByKey`, `rde.GetMetadata`, `rde.AddMetadata`,
`rde.UpdateMetadata` and `rde.DeleteMetadata` [GH-557]
153 changes: 153 additions & 0 deletions govcd/metadata_v2.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/vmware/go-vcloud-director/v2/types/v56"
"github.com/vmware/go-vcloud-director/v2/util"
"net/http"
"net/url"
"regexp"
"strings"
)
Expand Down Expand Up @@ -116,6 +117,12 @@ func (openApiOrgVdcNetwork *OpenApiOrgVdcNetwork) GetMetadataByKey(key string, i
return getMetadataByKey(openApiOrgVdcNetwork.client, href, openApiOrgVdcNetwork.OpenApiOrgVdcNetwork.Name, key, isSystem)
}

// GetMetadataByKey returns DefinedEntity metadata corresponding to the given key and domain.
func (rde *DefinedEntity) GetMetadataByKey(key string) (*types.OpenApiMetadataEntry, error) {
endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointRdeEntities
return getOpenApiMetadataByKey(rde.client, endpoint, rde.DefinedEntity.ID, key)
}

// ------------------------------------------------------------------------------------------------
// GET all metadata
// ------------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -209,6 +216,11 @@ func (openApiOrgVdcNetwork *OpenApiOrgVdcNetwork) GetMetadata() (*types.Metadata
return getMetadata(openApiOrgVdcNetwork.client, href, openApiOrgVdcNetwork.OpenApiOrgVdcNetwork.Name)
}

func (rde *DefinedEntity) GetMetadata() ([]*types.OpenApiMetadataEntry, error) {
endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointRdeEntities
return getAllOpenApiMetadata(rde.client, endpoint, rde.DefinedEntity.ID, nil)
}

// ------------------------------------------------------------------------------------------------
// ADD metadata async
// ------------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -381,6 +393,12 @@ func (openApiOrgVdcNetwork *OpenApiOrgVdcNetwork) AddMetadataEntryWithVisibility
return task.WaitTaskCompletion()
}

// AddMetadata adds metadata to the receiver DefinedEntity.
func (rde *DefinedEntity) AddMetadata(metadataEntry types.OpenApiMetadataEntry) (*types.OpenApiMetadataEntry, error) {
endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointRdeEntities
return addOpenApiMetadata(rde.client, endpoint, rde.DefinedEntity.ID, metadataEntry)
}

// ------------------------------------------------------------------------------------------------
// MERGE metadata async
// ------------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -569,6 +587,13 @@ func (openApiOrgVdcNetwork *OpenApiOrgVdcNetwork) MergeMetadataWithMetadataValue
return task.WaitTaskCompletion()
}

// UpdateMetadata updates the DefinedEntity metadata corresponding to the given key with the given value.
// Only the value of the entry can be updated. Re-create the entry in case you want to modify any of the other fields.
func (rde *DefinedEntity) UpdateMetadata(key string, value interface{}) (*types.OpenApiMetadataEntry, error) {
endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointRdeEntities
return updateOpenApiMetadata(rde.client, endpoint, rde.DefinedEntity.ID, key, value)
}

// ------------------------------------------------------------------------------------------------
// DELETE metadata async
// ------------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -730,6 +755,12 @@ func (openApiOrgVdcNetwork *OpenApiOrgVdcNetwork) DeleteMetadataEntryWithDomain(
return task.WaitTaskCompletion()
}

// DeleteMetadata deletes metadata from the receiver DefinedEntity with the given key.
func (rde *DefinedEntity) DeleteMetadata(key string) error {
endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointRdeEntities
return deleteOpenApiMetadata(rde.client, endpoint, rde.DefinedEntity.ID, key)
}

// ------------------------------------------------------------------------------------------------
// Ignored metadata set/unset
// ------------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -762,6 +793,27 @@ func getMetadataByKey(client *Client, requestUri, name, key string, isSystem boo
return filterSingleMetadataEntry(key, requestUri, name, metadata, client.IgnoredMetadata)
}

// getOpenApiMetadataByKey is a generic function to retrieve a unique metadata entry from any VCD object using its ID,
// the metadata key and the given OpenAPI endpoint.
func getOpenApiMetadataByKey(client *Client, endpoint, objectId string, key string) (*types.OpenApiMetadataEntry, error) {
queryParameters := url.Values{}
queryParameters.Add("filter", fmt.Sprintf("keyValue.key==%s", key))
metadata, err := getAllOpenApiMetadata(client, endpoint, objectId, queryParameters)
if err != nil {
return nil, err
}

if len(metadata) == 0 {
return nil, fmt.Errorf("%s could not find the metadata associated to object %s", ErrorEntityNotFound, objectId)
}

if len(metadata) > 1 {
return nil, fmt.Errorf("found more than 1 metadata entries associated to object %s", objectId)
}

return metadata[0], nil
}

// getMetadata is a generic function to retrieve metadata from VCD
func getMetadata(client *Client, requestUri, name string) (*types.Metadata, error) {
metadata := &types.Metadata{}
Expand All @@ -773,6 +825,28 @@ func getMetadata(client *Client, requestUri, name string) (*types.Metadata, erro
return filterMetadata(metadata, requestUri, name, client.IgnoredMetadata)
}

// getAllOpenApiMetadata is a generic function to retrieve all metadata from any VCD object using its ID and the given OpenAPI endpoint.
// It supports query parameters to input, for example, filtering options.
func getAllOpenApiMetadata(client *Client, endpoint, objectId string, queryParameters url.Values) ([]*types.OpenApiMetadataEntry, error) {
apiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint)
if err != nil {
return nil, err
}

urlRef, err := client.OpenApiBuildEndpoint(endpoint, fmt.Sprintf("%s/metadata", objectId))
if err != nil {
return nil, err
}

allMetadata := []*types.OpenApiMetadataEntry{{}}
err = client.OpenApiGetAllItems(apiVersion, urlRef, queryParameters, &allMetadata, nil)
if err != nil {
return nil, err
}

return allMetadata, nil
}

// addMetadata adds metadata to an entity.
// If the metadata entry is of the SYSTEM domain (isSystem=true), one can set different types of Visibility:
// types.MetadataReadOnlyVisibility, types.MetadataHiddenVisibility but NOT types.MetadataReadWriteVisibility.
Expand Down Expand Up @@ -832,6 +906,27 @@ func addMetadataAndWait(client *Client, requestUri, name, key, value, typedValue
return task.WaitTaskCompletion()
}

// addOpenApiMetadata adds one metadata entry to the VCD object with given ID
func addOpenApiMetadata(client *Client, endpoint, objectId string, metadataEntry types.OpenApiMetadataEntry) (*types.OpenApiMetadataEntry, error) {
apiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint)
if err != nil {
return nil, err
}

urlRef, err := client.OpenApiBuildEndpoint(endpoint, fmt.Sprintf("%s/metadata", objectId))
if err != nil {
return nil, err
}

result := &types.OpenApiMetadataEntry{}
err = client.OpenApiPostItem(apiVersion, urlRef, nil, metadataEntry, result, nil)
if err != nil {
return nil, err
}

return result, nil
}

// mergeAllMetadata updates the metadata values that are already present in VCD and creates the ones not present.
// The input metadata map has a "metadata key"->"metadata value" relation.
// If the operation is successful, it returns the created task.
Expand Down Expand Up @@ -879,6 +974,36 @@ func mergeMetadataAndWait(client *Client, requestUri, name string, metadata map[
return task.WaitTaskCompletion()
}

// updateOpenApiMetadata updates the metadata value from the given object.
// Only the value of the entry can be updated. Re-create the entry in case you want to modify any of the other fields.
func updateOpenApiMetadata(client *Client, endpoint, objectId, key string, value interface{}) (*types.OpenApiMetadataEntry, error) {
result, err := getOpenApiMetadataByKey(client, endpoint, objectId, key)
if err != nil {
return nil, err
}
if result.ID == "" {
return nil, fmt.Errorf("could not update metadata, it doesn't have required ID: %v", result)
}

apiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint)
if err != nil {
return nil, err
}

urlRef, err := client.OpenApiBuildEndpoint(endpoint, fmt.Sprintf("%s/metadata/%s", objectId, result.ID))
if err != nil {
return nil, err
}

result.KeyValue.Value.Value = value
err = client.OpenApiPutItem(apiVersion, urlRef, nil, result, &result, nil)
if err != nil {
return nil, err
}

return result, nil
}

// deleteMetadata deletes metadata associated to the input key from an entity referenced by its URI, then returns the
// task.
func deleteMetadata(client *Client, requestUri, name, key string, isSystem bool) (Task, error) {
Expand Down Expand Up @@ -907,6 +1032,34 @@ func deleteMetadataAndWait(client *Client, requestUri, name, key string, isSyste
return task.WaitTaskCompletion()
}

// deleteOpenApiMetadata deletes one metadata entry with the given key from the VCD object with given ID.
func deleteOpenApiMetadata(client *Client, endpoint, objectId, key string) error {
metadataEntry, err := getOpenApiMetadataByKey(client, endpoint, objectId, key)
if err != nil {
return err
}
if metadataEntry.ID == "" {
return fmt.Errorf("could not delete metadata, it doesn't have required ID: %v", metadataEntry)
}

apiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint)
if err != nil {
return err
}

urlRef, err := client.OpenApiBuildEndpoint(endpoint, fmt.Sprintf("%s/metadata/%s", objectId, metadataEntry.ID))
if err != nil {
return err
}

err = client.OpenApiDeleteItem(apiVersion, urlRef, nil, nil)
if err != nil {
return err
}

return nil
}

// IgnoredMetadata is a structure that defines the metadata entries that should be ignored by the VCD Client.
// The filtering works in such a way that all the non-nil pointers in an instance of this struct are evaluated with
// a logical AND.
Expand Down
Loading