updated 'zrok copy' (#438)

This commit is contained in:
Michael Quigley 2023-12-14 14:25:24 -05:00
parent a975b69541
commit cbb69373f4
No known key found for this signature in database
GPG Key ID: 9B60314A9DD20A62
6 changed files with 159 additions and 99 deletions

118
cmd/zrok/copy.go Normal file
View File

@ -0,0 +1,118 @@
package main
import (
"fmt"
"github.com/openziti/zrok/environment"
"github.com/openziti/zrok/environment/env_core"
"github.com/openziti/zrok/sdk/golang/sdk"
"github.com/openziti/zrok/tui"
"github.com/openziti/zrok/util/sync"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"net/url"
)
func init() {
rootCmd.AddCommand(newCopyCommand().cmd)
}
type copyCommand struct {
cmd *cobra.Command
}
func newCopyCommand() *copyCommand {
cmd := &cobra.Command{
Use: "copy <source> [<target>]",
Short: "Copy zrok drive contents from <source> to <target> ('file://' and 'zrok://' supported)",
Args: cobra.RangeArgs(1, 2),
}
command := &copyCommand{cmd: cmd}
cmd.Run = command.run
return command
}
func (cmd *copyCommand) run(_ *cobra.Command, args []string) {
sourceUrl, err := url.Parse(args[0])
if err != nil {
tui.Error(fmt.Sprintf("invalid source URL '%v'", args[0]), err)
}
if sourceUrl.Scheme != "zrok" && sourceUrl.Scheme != "file" {
tui.Error("source URL must be 'file://' or 'zrok://", nil)
}
targetStr := "file://."
if len(args) == 2 {
targetStr = args[1]
}
targetUrl, err := url.Parse(targetStr)
if err != nil {
tui.Error(fmt.Sprintf("invalid target URL '%v'", targetStr), err)
}
if targetUrl.Scheme != "zrok" && targetUrl.Scheme != "file" {
tui.Error("target URL must be 'file://' or 'zrok://", nil)
}
if sourceUrl.Scheme != "zrok" && targetUrl.Scheme != "zrok" {
tui.Error("either <source> or <target> must be a 'zrok://' URL", nil)
}
if targetUrl.Scheme != "file" && sourceUrl.Scheme != "file" {
tui.Error("either <source> or <target> must be a 'file://' URL", nil)
}
root, err := environment.LoadRoot()
if err != nil {
tui.Error("error loading root", err)
}
var access *sdk.Access
if sourceUrl.Scheme == "zrok" {
access, err = sdk.CreateAccess(root, &sdk.AccessRequest{ShareToken: sourceUrl.Host})
if err != nil {
tui.Error("error creating access", err)
}
}
if targetUrl.Scheme == "zrok" {
access, err = sdk.CreateAccess(root, &sdk.AccessRequest{ShareToken: targetUrl.Host})
if err != nil {
tui.Error("error creating access", err)
}
}
defer func() {
err := sdk.DeleteAccess(root, access)
if err != nil {
tui.Error("error deleting access", err)
}
}()
source, err := cmd.createTarget(sourceUrl, root)
if err != nil {
tui.Error("error creating target", err)
}
target, err := cmd.createTarget(targetUrl, root)
if err != nil {
tui.Error("error creating target", err)
}
if err := sync.Synchronize(source, target); err != nil {
tui.Error("error copying", err)
}
fmt.Println("copy complete!")
}
func (cmd *copyCommand) createTarget(t *url.URL, root env_core.Root) (sync.Target, error) {
switch t.Scheme {
case "zrok":
target, err := sync.NewWebDAVTarget(&sync.WebDAVTargetConfig{URL: t, Username: "", Password: "", Root: root})
if err != nil {
return nil, err
}
return target, nil
case "file":
return sync.NewFilesystemTarget(&sync.FilesystemTargetConfig{Root: t.Path}), nil
default:
return nil, errors.Errorf("invalid scheme")
}
}

View File

@ -1,48 +0,0 @@
package main
import (
"github.com/openziti/zrok/util/sync"
"github.com/spf13/cobra"
)
func init() {
copyCmd.AddCommand(newCopyFromCommand().cmd)
}
type copyFromCommand struct {
cmd *cobra.Command
}
func newCopyFromCommand() *copyFromCommand {
cmd := &cobra.Command{
Use: "from <share> [<destination>]",
Short: "Copy files from zrok drive to destination",
Args: cobra.RangeArgs(1, 2),
}
command := &copyFromCommand{cmd: cmd}
cmd.Run = command.run
return command
}
func (cmd *copyFromCommand) run(_ *cobra.Command, args []string) {
target := "."
if len(args) == 2 {
target = args[1]
}
dst := sync.NewFilesystemTarget(&sync.FilesystemTargetConfig{
Root: target,
})
src, err := sync.NewWebDAVTarget(&sync.WebDAVTargetConfig{
URL: args[0],
Username: "",
Password: "",
})
if err != nil {
panic(err)
}
if err := sync.Synchronize(src, dst); err != nil {
panic(err)
}
}

View File

@ -1,43 +0,0 @@
package main
import (
"github.com/openziti/zrok/util/sync"
"github.com/spf13/cobra"
)
func init() {
copyCmd.AddCommand(newCopyToCommand().cmd)
}
type copyToCommand struct {
cmd *cobra.Command
}
func newCopyToCommand() *copyToCommand {
cmd := &cobra.Command{
Use: "to <share> <source>",
Short: "Copy files to a zrok drive from source",
Args: cobra.ExactArgs(2),
}
command := &copyToCommand{cmd: cmd}
cmd.Run = command.run
return command
}
func (cmd *copyToCommand) run(_ *cobra.Command, args []string) {
dst, err := sync.NewWebDAVTarget(&sync.WebDAVTargetConfig{
URL: args[0],
Username: "",
Password: "",
})
if err != nil {
panic(err)
}
src := sync.NewFilesystemTarget(&sync.FilesystemTargetConfig{
Root: args[1],
})
if err := sync.Synchronize(src, dst); err != nil {
panic(err)
}
}

View File

@ -26,7 +26,6 @@ func init() {
testCmd.AddCommand(loopCmd)
rootCmd.AddCommand(adminCmd)
rootCmd.AddCommand(configCmd)
rootCmd.AddCommand(copyCmd)
rootCmd.AddCommand(shareCmd)
rootCmd.AddCommand(testCmd)
transport.AddAddressParser(tcp.AddressParser{})
@ -80,11 +79,6 @@ var configCmd = &cobra.Command{
Short: "Configure your zrok environment",
}
var copyCmd = &cobra.Command{
Use: "copy",
Short: "Copy files to/from zrok drives",
}
var loopCmd = &cobra.Command{
Use: "loopback",
Aliases: []string{"loop"},

View File

@ -2,18 +2,21 @@ package sync
import (
"fmt"
"github.com/openziti/zrok/environment/env_core"
"github.com/openziti/zrok/util/sync/webdavClient"
"github.com/pkg/errors"
"io"
"net/url"
"os"
"path/filepath"
"time"
)
type WebDAVTargetConfig struct {
URL string
URL *url.URL
Username string
Password string
Root env_core.Root
}
type WebDAVTarget struct {
@ -21,7 +24,10 @@ type WebDAVTarget struct {
}
func NewWebDAVTarget(cfg *WebDAVTargetConfig) (*WebDAVTarget, error) {
c := webdavClient.NewClient(cfg.URL, cfg.Username, cfg.Password)
c, err := webdavClient.NewZrokClient(cfg.URL, cfg.Root, webdavClient.NewAutoAuth(cfg.Username, cfg.Password))
if err != nil {
return nil, err
}
if err := c.Connect(); err != nil {
return nil, errors.Wrap(err, "error connecting to webdav target")
}

View File

@ -2,9 +2,13 @@ package webdavClient
import (
"bytes"
"context"
"encoding/xml"
"fmt"
"github.com/openziti/zrok/environment/env_core"
"github.com/openziti/zrok/sdk/golang/sdk"
"io"
"net"
"net/http"
"net/url"
"os"
@ -29,6 +33,35 @@ func NewClient(uri, user, pw string) *Client {
return NewAuthClient(uri, NewAutoAuth(user, pw))
}
func NewZrokClient(zrokUrl *url.URL, root env_core.Root, auth Authorizer) (*Client, error) {
conn, err := sdk.NewDialer(zrokUrl.Host, root)
if err != nil {
return nil, err
}
transport := http.DefaultTransport.(*http.Transport).Clone()
transport.DialContext = func(_ context.Context, _, _ string) (net.Conn, error) {
return conn, nil
}
c := &http.Client{
Transport: transport,
CheckRedirect: func(rq *http.Request, via []*http.Request) error {
if len(via) >= 10 {
return ErrTooManyRedirects
}
if via[0].Header.Get(XInhibitRedirect) != "" {
return http.ErrUseLastResponse
}
return nil
},
}
httpUrl, err := url.Parse(zrokUrl.String())
if err != nil {
return nil, err
}
httpUrl.Scheme = "http"
return &Client{root: FixSlash(httpUrl.String()), headers: make(http.Header), interceptor: nil, c: c, auth: auth}, nil
}
// NewAuthClient creates a new client instance with a custom Authorizer
func NewAuthClient(uri string, auth Authorizer) *Client {
c := &http.Client{