forked from vmware/go-vcloud-director
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: abarreiro <abarreiro@vmware.com>
- Loading branch information
1 parent
de4d321
commit 54ea7c8
Showing
6 changed files
with
253 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
package govcd | ||
|
||
import ( | ||
"fmt" | ||
"github.com/vmware/go-vcloud-director/v2/types/v56" | ||
"net/http" | ||
"os" | ||
"path/filepath" | ||
"regexp" | ||
"strings" | ||
) | ||
|
||
type UIPluginMetadata struct { | ||
UIPluginMetadata *types.UIPluginMetadata | ||
client *Client | ||
} | ||
|
||
// CreateUIPlugin creates a new UI extension and sets the provided plugin metadata for it. | ||
// Only System administrator can create a UI extension. | ||
func (vcdClient *VCDClient) CreateUIPlugin(uiPluginMetadata *types.UIPluginMetadata) (*UIPluginMetadata, error) { | ||
client := vcdClient.Client | ||
endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointExtensionsUi | ||
apiVersion, err := client.getOpenApiHighestElevatedVersion(endpoint) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
urlRef, err := client.OpenApiBuildEndpoint(endpoint) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
result := &UIPluginMetadata{ | ||
UIPluginMetadata: &types.UIPluginMetadata{}, | ||
client: &vcdClient.Client, | ||
} | ||
|
||
err = client.OpenApiPostItem(apiVersion, urlRef, nil, uiPluginMetadata, result.UIPluginMetadata, nil) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return result, nil | ||
} | ||
|
||
// Upload uploads the given UI Plugin to VCD. Only the file name in the input types.UploadSpec is required. | ||
// The size is calculated automatically if not provided. | ||
func (ui *UIPluginMetadata) Upload(uploadSpec *types.UploadSpec) error { | ||
if strings.TrimSpace(uploadSpec.FileName) == "" { | ||
return fmt.Errorf("file name to upload must not be empty") | ||
} | ||
fileContents, err := os.ReadFile(filepath.Clean(uploadSpec.FileName)) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if uploadSpec.Size <= 0 { | ||
uploadSpec.Size = len(fileContents) | ||
} | ||
|
||
endpoint := types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointExtensionsUiPlugin | ||
apiVersion, err := ui.client.getOpenApiHighestElevatedVersion(endpoint) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
urlRef, err := ui.client.OpenApiBuildEndpoint(endpoint) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
headers, err := ui.client.OpenApiPostItemAndGetHeaders(apiVersion, urlRef, nil, uploadSpec, nil, nil) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
transferId, err := getTransferIdFromHeader(headers) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
endpoint = types.OpenApiPathVersion1_0_0 + types.OpenApiEndpointTransfer | ||
apiVersion, err = ui.client.getOpenApiHighestElevatedVersion(endpoint) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
urlRef, err = ui.client.OpenApiBuildEndpoint(endpoint, transferId) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return ui.client.OpenApiPutItem(apiVersion, urlRef, nil, fileContents, nil, nil) | ||
} | ||
|
||
// getTransferIdFromHeader retrieves a valid transfer ID from any given HTTP headers | ||
func getTransferIdFromHeader(headers http.Header) (string, error) { | ||
rawLinkContent := headers.Get("link") | ||
if rawLinkContent == "" { | ||
return "", fmt.Errorf("error during UI plugin upload, the POST call didn't return any transfer link") | ||
} | ||
linkRegex := regexp.MustCompile(`<\S+/transfer/(\S+)>`) | ||
matches := linkRegex.FindStringSubmatch(rawLinkContent) | ||
if len(matches) < 2 { | ||
return "", fmt.Errorf("error during UI plugin upload, the POST call didn't return a valid transfer link: %s", rawLinkContent) | ||
} | ||
return matches[1], nil | ||
} | ||
|
||
func (*UIPluginMetadata) Publish(orgs types.OpenApiReferences) (types.OpenApiReferences, error) { | ||
return nil, nil | ||
} | ||
|
||
func (*UIPluginMetadata) PublishAll() { | ||
|
||
} | ||
|
||
func (*UIPluginMetadata) Unpublish(orgs types.OpenApiReferences) (types.OpenApiReferences, error) { | ||
return nil, nil | ||
} | ||
|
||
func (*UIPluginMetadata) UnpublishAll() { | ||
|
||
} | ||
|
||
func (*UIPluginMetadata) Delete() { | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
//go:build functional || openapi || plugin || ALL | ||
|
||
package govcd | ||
|
||
import ( | ||
"net/http" | ||
"net/textproto" | ||
"testing" | ||
) | ||
|
||
// Test_getTransferIdFromHeader tests that getTransferIdFromHeader can retrieve correctly a transfer ID from the headers | ||
// of any HTTP response. | ||
func Test_getTransferIdFromHeader(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
headers http.Header | ||
want string | ||
wantErr bool | ||
}{ | ||
{ | ||
name: "valid link in header", | ||
headers: http.Header{ | ||
textproto.CanonicalMIMEHeaderKey("link"): { | ||
"<https://www.my-vcd.com/transfer/cb63b0f6-ba56-43a8-8fe3-a64f0b25e7e5/my-amazing-plugin1.0.zip>;rel=\"upload:default\";type=\"application/octet-stream\"", | ||
}, | ||
}, | ||
want: "cb63b0f6-ba56-43a8-8fe3-a64f0b25e7e5/my-amazing-plugin1.0.zip", | ||
wantErr: false, | ||
}, | ||
{ | ||
name: "valid link in header with special URI", | ||
headers: http.Header{ | ||
textproto.CanonicalMIMEHeaderKey("link"): { | ||
"<my-vcd:8080/transfer/cb63b0f6-ba56-43a8-8fe3-a64f0b25e7e5/my-amazing-plugin1.1.zip>;rel=\"upload:default\";type=\"application/octet-stream\"", | ||
}, | ||
}, | ||
want: "cb63b0f6-ba56-43a8-8fe3-a64f0b25e7e5/my-amazing-plugin1.1.zip", | ||
wantErr: false, | ||
}, | ||
{ | ||
name: "empty header", | ||
headers: http.Header{ | ||
textproto.CanonicalMIMEHeaderKey("link"): { | ||
"", | ||
}, | ||
}, | ||
wantErr: true, | ||
}, | ||
{ | ||
name: "empty link in header", | ||
headers: http.Header{ | ||
textproto.CanonicalMIMEHeaderKey("link"): { | ||
"<>;rel=\"upload:default\";type=\"application/octet-stream\"", | ||
}, | ||
}, | ||
wantErr: true, | ||
}, | ||
{ | ||
name: "no link part in header", | ||
headers: http.Header{ | ||
textproto.CanonicalMIMEHeaderKey("link"): { | ||
"rel=\"upload:default\";type=\"application/octet-stream\"", | ||
}, | ||
}, | ||
wantErr: true, | ||
}, | ||
{ | ||
name: "invalid header", | ||
headers: http.Header{ | ||
textproto.CanonicalMIMEHeaderKey("link"): { | ||
"Error", | ||
}, | ||
}, | ||
wantErr: true, | ||
}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
got, err := getTransferIdFromHeader(tt.headers) | ||
if (err != nil) != tt.wantErr { | ||
t.Errorf("getTransferIdFromHeader() error = %v, wantErr %v", err, tt.wantErr) | ||
return | ||
} | ||
if got != tt.want { | ||
t.Errorf("getTransferIdFromHeader() got = %v, want %v", got, tt.want) | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters