Skip to content

Commit

Permalink
api: add Content Library update session file APIs
Browse files Browse the repository at this point in the history
Closes #3258
  • Loading branch information
dougm committed Oct 31, 2023
1 parent f231aa4 commit 6cd6ad1
Show file tree
Hide file tree
Showing 8 changed files with 373 additions and 54 deletions.
3 changes: 3 additions & 0 deletions govc/USAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -3476,6 +3476,7 @@ Examples:
govc library.session.ls -json | jq .
Options:
-i=false List session item files (with -json only)
```

## library.session.rm
Expand All @@ -3487,9 +3488,11 @@ Remove a library item update session.
Examples:
govc library.session.rm session_id
govc library.session.rm -i session_id foo.ovf
Options:
-f=false Cancel session if active
-i=false Remove session item file
```

## library.subscriber.create
Expand Down
5 changes: 5 additions & 0 deletions govc/library/import.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,11 @@ func (cmd *item) Run(ctx context.Context, f *flag.FlagSet) error {
return err
}

err = m.CompleteLibraryItemUpdateSession(ctx, session)
if err != nil {
return err
}

return m.WaitOnLibraryItemUpdateSession(ctx, session, 3*time.Second, nil)
}

Expand Down
115 changes: 115 additions & 0 deletions govc/library/probe.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/*
Copyright (c) 2023-2023 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package library

import (
"context"
"flag"
"fmt"
"io"
"os"
"text/tabwriter"

"github.com/vmware/govmomi/govc/cli"
"github.com/vmware/govmomi/govc/flags"
"github.com/vmware/govmomi/vapi/library"
)

type probe struct {
*flags.ClientFlag
*flags.OutputFlag

fail bool
}

func init() {
cli.Register("library.probe", &probe{}, true)
}

func (cmd *probe) Register(ctx context.Context, f *flag.FlagSet) {
cmd.ClientFlag, ctx = flags.NewClientFlag(ctx)
cmd.ClientFlag.Register(ctx, f)

cmd.OutputFlag, ctx = flags.NewOutputFlag(ctx)
cmd.OutputFlag.Register(ctx, f)

f.BoolVar(&cmd.fail, "f", false, "Fail if probe status is not success")
}

func (cmd *probe) Usage() string {
return "URI"
}

func (cmd *probe) Description() string {
return `Probes the source endpoint URI with https or http schemes.
Examples:
govc library.probe https://example.com/file.ova`
}

type probeResult struct {
*library.ProbeResult
}

func (r *probeResult) Write(w io.Writer) error {
tw := tabwriter.NewWriter(w, 2, 0, 2, ' ', 0)

fmt.Fprintf(tw, "Status:\t%s\n", r.Status)
thumbprint := r.SSLThumbprint
if thumbprint == "" {
thumbprint = "-"
}
fmt.Fprintf(tw, "Thumbprint:\t%s\n", thumbprint)
for _, e := range r.ErrorMessages {
fmt.Fprintf(tw, "%s:\t%s\n", e.ID, e.Error())
}

return tw.Flush()
}

func (cmd *probe) Process(ctx context.Context) error {
if err := cmd.ClientFlag.Process(ctx); err != nil {
return err
}
return cmd.OutputFlag.Process(ctx)
}

func (cmd *probe) Run(ctx context.Context, f *flag.FlagSet) error {
if f.NArg() != 1 {
return flag.ErrHelp
}

c, err := cmd.RestClient()
if err != nil {
return err
}

m := library.NewManager(c)

p, err := m.ProbeTransferEndpoint(ctx, library.TransferEndpoint{URI: f.Arg(0)})
if err != nil {
return err
}

if cmd.fail && p.Status != "SUCCESS" {
cmd.Out = os.Stderr
// using same exit code as curl -f:
defer os.Exit(22)
}

return cmd.WriteResult(&probeResult{p})
}
52 changes: 41 additions & 11 deletions govc/library/session/ls.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ import (
type ls struct {
*flags.ClientFlag
*flags.OutputFlag

files bool
}

func init() {
Expand All @@ -42,6 +44,8 @@ func (cmd *ls) Register(ctx context.Context, f *flag.FlagSet) {
cmd.ClientFlag.Register(ctx, f)
cmd.OutputFlag, ctx = flags.NewOutputFlag(ctx)
cmd.OutputFlag.Register(ctx, f)

f.BoolVar(&cmd.files, "i", false, "List session item files (with -json only)")
}

func (cmd *ls) Process(ctx context.Context) error {
Expand All @@ -59,8 +63,14 @@ Examples:
govc library.session.ls -json | jq .`
}

type librarySession struct {
*library.Session
LibraryItemPath string `json:"library_item_path"`
}

type info struct {
Sessions []*library.Session `json:"sessions"`
Sessions []librarySession `json:"sessions"`
Files map[string]any `json:"files"`
kind string
}

Expand All @@ -70,7 +80,7 @@ func (i *info) Write(w io.Writer) error {

for _, s := range i.Sessions {
_, _ = fmt.Fprintf(tw, "%s\t%s\t%s\t%s\t%d\t%s\t%s\n",
s.ID, s.LibraryItemID, i.kind, s.LibraryItemContentVersion, s.ClientProgress, s.State,
s.ID, s.LibraryItemPath, i.kind, s.LibraryItemContentVersion, s.ClientProgress, s.State,
s.ExpirationTime.Format("2006-01-02 15:04"))
}

Expand Down Expand Up @@ -102,26 +112,46 @@ func (cmd *ls) Run(ctx context.Context, f *flag.FlagSet) error {
if len(ids) == 0 {
continue
}
var sessions []*library.Session
i := &info{
Files: make(map[string]any),
kind: k.kind,
}

for _, id := range ids {
session, err := k.get(ctx, id)
if err != nil {
return err
}
var path string
item, err := m.GetLibraryItem(ctx, session.LibraryItemID)
if err != nil {
return err
if err == nil {
// can only show library path if item exists
lib, err := m.GetLibraryByID(ctx, item.LibraryID)
if err != nil {
return err
}
path = fmt.Sprintf("/%s/%s", lib.Name, item.Name)
}
lib, err := m.GetLibraryByID(ctx, item.LibraryID)
if err != nil {
return err
i.Sessions = append(i.Sessions, librarySession{session, path})
if !cmd.files {
continue
}
if k.kind == "Update" {
f, err := m.ListLibraryItemUpdateSessionFile(ctx, id)
if err != nil {
return err
}
i.Files[id] = f
} else {
f, err := m.ListLibraryItemDownloadSessionFile(ctx, id)
if err != nil {
return err
}
i.Files[id] = f
}
session.LibraryItemID = fmt.Sprintf("/%s/%s", lib.Name, item.Name)
sessions = append(sessions, session)
}

err = cmd.WriteResult(&info{sessions, k.kind})
err = cmd.WriteResult(i)
if err != nil {
return err
}
Expand Down
18 changes: 15 additions & 3 deletions govc/library/session/rm.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
/*
Copyright (c) 2019 VMware, Inc. All Rights Reserved.
Copyright (c) 2019-2023 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
Expand All @@ -18,6 +18,7 @@ package session

import (
"context"
"errors"
"flag"

"github.com/vmware/govmomi/govc/cli"
Expand All @@ -29,6 +30,7 @@ type rm struct {
*flags.ClientFlag

cancel bool
files bool
}

func init() {
Expand All @@ -40,13 +42,15 @@ func (cmd *rm) Register(ctx context.Context, f *flag.FlagSet) {
cmd.ClientFlag.Register(ctx, f)

f.BoolVar(&cmd.cancel, "f", false, "Cancel session if active")
f.BoolVar(&cmd.files, "i", false, "Remove session item file")
}

func (cmd *rm) Description() string {
return `Remove a library item update session.
Examples:
govc library.session.rm session_id`
govc library.session.rm session_id
govc library.session.rm -i session_id foo.ovf`
}

func (cmd *rm) Run(ctx context.Context, f *flag.FlagSet) error {
Expand All @@ -60,11 +64,19 @@ func (cmd *rm) Run(ctx context.Context, f *flag.FlagSet) error {
m := library.NewManager(c)
cancel := m.CancelLibraryItemUpdateSession
remove := m.DeleteLibraryItemUpdateSession
rmfile := m.RemoveLibraryItemUpdateSessionFile

_, err = m.GetLibraryItemUpdateSession(ctx, id)
if err != nil {
cancel = m.CancelLibraryItemDownloadSession
remove = m.DeleteLibraryItemDownloadSession
rmfile = func(context.Context, string, string) error {
return errors.New("cannot delete a download session file")
}
}

if cmd.files {
return rmfile(ctx, id, f.Arg(1))
}

if cmd.cancel {
Expand Down
56 changes: 56 additions & 0 deletions govc/test/library.bats
Original file line number Diff line number Diff line change
Expand Up @@ -551,3 +551,59 @@ EOF
# remove generated cert and key
rm "$pem".{crt,key}
}

@test "library.session" {
vcsim_env

run govc library.session.ls
assert_success

run govc library.create my-content
assert_success

run govc library.import /my-content "$GOVC_IMAGES/$TTYLINUX_NAME.ova"
assert_success

run govc library.session.ls
assert_success
assert_matches ttylinux

run govc library.session.ls -json
assert_success

run govc library.session.ls -json -i
assert_success

n=$(govc library.session.ls -json -i | jq '.files[] | length')
assert_equal 2 "$n" # .ovf + .vmdk

id=$(govc library.session.ls -json | jq -r .sessions[].id)

run govc library.session.rm -i "$id" ttylinux-pc_i486-16.1.ovf
assert_failure # removeFile not allowed in state DONE
assert_matches "500 Internal Server Error"

run govc library.session.rm "$id"
assert_success
}

@test "library.probe" {
vcsim_env

export GOVC_SHOW_UNRELEASED=true

run govc library.probe
assert_failure

run govc library.probe https://www.vmware.com
assert_success

run govc library.probe -f ftp://www.vmware.com
if [ "$status" -ne 22 ]; then
flunk $(printf "expected failed exit status=22, got status=%d" $status)
fi

run govc library.probe -json ftp://www.vmware.com
assert_success
assert_matches INVALID_URL
}
Loading

0 comments on commit 6cd6ad1

Please sign in to comment.