From ed1816e170959d4a83cbf8a729c69e3e39217b21 Mon Sep 17 00:00:00 2001 From: Reuben Miller Date: Tue, 14 May 2024 22:27:05 +0200 Subject: [PATCH 01/23] add function to convert wildcard to regex --- pkg/matcher/matcher.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pkg/matcher/matcher.go b/pkg/matcher/matcher.go index 2d7f3c6a0..61fef4d69 100644 --- a/pkg/matcher/matcher.go +++ b/pkg/matcher/matcher.go @@ -6,7 +6,7 @@ import ( "strings" ) -func MatchWithWildcards(s, pattern string) (bool, error) { +func ConvertWildcardToRegex(pattern string) (*regexp.Regexp, error) { pattern = strings.ReplaceAll(pattern, "\\", "\\\\") pattern = strings.ReplaceAll(pattern, ".", "\\.") @@ -17,8 +17,11 @@ func MatchWithWildcards(s, pattern string) (bool, error) { // and whole string matching pattern = "(?ims)^" + pattern + "$" - r, err := regexp.Compile(pattern) + return regexp.Compile(pattern) +} +func MatchWithWildcards(s, pattern string) (bool, error) { + r, err := ConvertWildcardToRegex(pattern) if err != nil { return false, fmt.Errorf("invalid regex patter") } From 5df9b0e003eb368e25557802e5d46fcb7154b4a5 Mon Sep 17 00:00:00 2001 From: Reuben Miller Date: Tue, 14 May 2024 22:27:51 +0200 Subject: [PATCH 02/23] feat(remoteaccess): add new remote access support to create a local proxy server --- go.mod | 2 +- go.sum | 4 +- pkg/c8yfetcher/remoteaccessFetcher.go | 192 +++++++++++++++++ .../remoteaccess/connect/connect.manual.go | 29 +++ .../remoteaccess/connect/ssh/ssh.manual.go | 194 ++++++++++++++++++ pkg/cmd/remoteaccess/remoteaccess.manual.go | 31 +++ pkg/cmd/remoteaccess/server/server.manual.go | 189 +++++++++++++++++ pkg/cmd/root/root.go | 5 + 8 files changed, 643 insertions(+), 3 deletions(-) create mode 100644 pkg/c8yfetcher/remoteaccessFetcher.go create mode 100644 pkg/cmd/remoteaccess/connect/connect.manual.go create mode 100644 pkg/cmd/remoteaccess/connect/ssh/ssh.manual.go create mode 100644 pkg/cmd/remoteaccess/remoteaccess.manual.go create mode 100644 pkg/cmd/remoteaccess/server/server.manual.go diff --git a/go.mod b/go.mod index 8c98e35b7..5c600c9d3 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,7 @@ require ( github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0 github.com/pkg/errors v0.9.1 github.com/pquerna/otp v1.4.0 - github.com/reubenmiller/go-c8y v0.15.10 + github.com/reubenmiller/go-c8y v0.15.11-0.20240514185826-c88bf3005666 github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 github.com/sergi/go-diff v1.3.1 // indirect github.com/sethvargo/go-password v0.2.0 diff --git a/go.sum b/go.sum index 5bc1f97cb..b5c64fcbc 100644 --- a/go.sum +++ b/go.sum @@ -155,8 +155,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pquerna/otp v1.4.0 h1:wZvl1TIVxKRThZIBiwOOHOGP/1+nZyWBil9Y2XNEDzg= github.com/pquerna/otp v1.4.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg= -github.com/reubenmiller/go-c8y v0.15.10 h1:5ur5Fvt9Sab73fnIAPnE//GNax2HZvVPZ34tnncaa/0= -github.com/reubenmiller/go-c8y v0.15.10/go.mod h1:5F2Z5V0A1dkaSva48iVxLUuDE866rvSe8zi3fAPxZ6Y= +github.com/reubenmiller/go-c8y v0.15.11-0.20240514185826-c88bf3005666 h1:alyiZwR5MIbbHiefnN/AKZs462+V6TvlmiGQnPa8y/4= +github.com/reubenmiller/go-c8y v0.15.11-0.20240514185826-c88bf3005666/go.mod h1:5F2Z5V0A1dkaSva48iVxLUuDE866rvSe8zi3fAPxZ6Y= github.com/reubenmiller/gojsonq/v2 v2.0.0-20221119213524-0fd921ac20a3 h1:v8Q77ObTxkm0Wj9iAjcc0VMLxqEzKIdAnaTNPzSiw8Q= github.com/reubenmiller/gojsonq/v2 v2.0.0-20221119213524-0fd921ac20a3/go.mod h1:QidmUT4ebNVwyjKXAQgx9VFHxpOxBKWs32EEXaXnEfE= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= diff --git a/pkg/c8yfetcher/remoteaccessFetcher.go b/pkg/c8yfetcher/remoteaccessFetcher.go new file mode 100644 index 000000000..973a5c269 --- /dev/null +++ b/pkg/c8yfetcher/remoteaccessFetcher.go @@ -0,0 +1,192 @@ +package c8yfetcher + +import ( + "context" + "fmt" + "regexp" + "strings" + + "github.com/pkg/errors" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmdutil" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/matcher" + "github.com/reubenmiller/go-c8y/pkg/c8y" +) + +type RemoteAccessFetcher struct { + *CumulocityFetcher + + ManagedObjectID string +} + +func DetectRemoteAccessConfiguration(client *c8y.Client, mo_id string, name string) (*c8y.RemoteAccessConfiguration, error) { + configs, _, err := client.RemoteAccess.GetConfigurations( + c8y.WithDisabledDryRunContext(context.Background()), + mo_id, + &c8y.RemoteAccessCollectionOptions{ + PaginationOptions: *c8y.NewPaginationOptions(50), + }) + + if err != nil { + return nil, err + } + + passthroughConfigs := make([]c8y.RemoteAccessConfiguration, 0) + + for _, config := range configs { + if strings.EqualFold(config.Protocol, c8y.RemoteAccessProtocolPassthrough) { + passthroughConfigs = append(passthroughConfigs, config) + } + } + + if name != "" { + // Exact match + for _, config := range passthroughConfigs { + if strings.EqualFold(config.Name, name) { + return &config, nil + } + } + + // Regex match + pattern, patternErr := matcher.ConvertWildcardToRegex(name) + if patternErr == nil { + // Match by regex + for _, config := range passthroughConfigs { + if pattern.MatchString(config.Name) { + return &config, nil + } + } + } + + return nil, fmt.Errorf("configuration not found. name=%s", name) + } + + if len(passthroughConfigs) == 0 { + return nil, fmt.Errorf("configuration not found. name=%s", name) + } + + // + // Guess the configuration + // + + // There is only one, so let's use that + if len(passthroughConfigs) == 1 { + return &passthroughConfigs[0], nil + } + + // Use first match which uses port 22 + for _, config := range passthroughConfigs { + if config.Port == 22 { + return &config, nil + } + } + + // Use first match with ssh in its name + for _, config := range passthroughConfigs { + if strings.Contains(strings.ToLower(config.Name), "ssh") { + return &config, nil + } + } + + // Finally just try the first + return &passthroughConfigs[0], nil +} + +func NewRemoteAccessFetcher(factory *cmdutil.Factory, mo_id string) *RemoteAccessFetcher { + return &RemoteAccessFetcher{ + CumulocityFetcher: &CumulocityFetcher{ + factory: factory, + }, + ManagedObjectID: mo_id, + } +} + +func (f *RemoteAccessFetcher) getByID(id string) ([]fetcherResultSet, error) { + config, resp, err := f.Client().RemoteAccess.GetConfiguration( + c8y.WithDisabledDryRunContext(context.Background()), + f.ManagedObjectID, + id, + ) + + if err != nil { + return nil, errors.Wrap(err, "Could not fetch by id") + } + + results := make([]fetcherResultSet, 1) + results[0] = fetcherResultSet{ + ID: config.ID, + Name: config.Name, + Value: resp.JSON(), + } + return results, nil +} + +// getByName returns applications matching a given using regular expression +func (f *RemoteAccessFetcher) getByName(name string) ([]fetcherResultSet, error) { + configs, _, err := f.Client().RemoteAccess.GetConfigurations( + c8y.WithDisabledDryRunContext(context.Background()), + f.ManagedObjectID, + nil, + ) + + if err != nil { + return nil, errors.Wrap(err, "could not fetch microservices") + } + + pattern, err := regexp.Compile("^" + regexp.QuoteMeta(name) + "$") + if err != nil { + return nil, errors.Wrap(err, "invalid regex") + } + + if err != nil { + return nil, errors.Wrap(err, "Could not fetch by id") + } + + results := make([]fetcherResultSet, 0) + + for _, app := range configs { + if pattern.MatchString(app.Name) { + results = append(results, fetcherResultSet{ + ID: app.ID, + Name: app.Name, + Value: app, + }) + } + + } + + return results, nil +} + +// FindRemoteAccessConfigurations returns remote access configurations given either an id or search text +// @values: An array of ids, or names (with wildcards) +// @lookupID: Lookup the data if an id is given. If a non-id text is given, the result will always be looked up. +func FindRemoteAccessConfigurations(factory *cmdutil.Factory, values []string, lookupID bool, format string, mo_id string) ([]entityReference, error) { + f := NewRemoteAccessFetcher(factory, mo_id) + + formattedValues, err := lookupEntity(f, values, lookupID, format) + + if err != nil { + return nil, err + } + + results := []entityReference{} + + invalidLookups := []string{} + for _, item := range formattedValues { + if item.ID != "" { + results = append(results, item) + } else { + if item.Name != "" { + invalidLookups = append(invalidLookups, item.Name) + } + } + } + + var errors error + + if len(invalidLookups) > 0 { + errors = fmt.Errorf("no results %v", invalidLookups) + } + + return results, errors +} diff --git a/pkg/cmd/remoteaccess/connect/connect.manual.go b/pkg/cmd/remoteaccess/connect/connect.manual.go new file mode 100644 index 000000000..786fbaab4 --- /dev/null +++ b/pkg/cmd/remoteaccess/connect/connect.manual.go @@ -0,0 +1,29 @@ +package connect + +import ( + "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/remoteaccess/connect/ssh" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/subcommand" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmdutil" + "github.com/spf13/cobra" +) + +type SubCmdConnect struct { + *subcommand.SubCommand +} + +func NewSubCommand(f *cmdutil.Factory) *SubCmdConnect { + ccmd := &SubCmdConnect{} + + cmd := &cobra.Command{ + Use: "connect", + Short: "Connect to device via remote access", + Long: `Connect to device via remote access`, + } + + // Subcommands + cmd.AddCommand(ssh.NewCmdSSH(f).GetCommand()) + + ccmd.SubCommand = subcommand.NewSubCommand(cmd) + + return ccmd +} diff --git a/pkg/cmd/remoteaccess/connect/ssh/ssh.manual.go b/pkg/cmd/remoteaccess/connect/ssh/ssh.manual.go new file mode 100644 index 000000000..01606e030 --- /dev/null +++ b/pkg/cmd/remoteaccess/connect/ssh/ssh.manual.go @@ -0,0 +1,194 @@ +package ssh + +import ( + "context" + "fmt" + "os" + "os/exec" + "strings" + "time" + + "github.com/MakeNowJust/heredoc/v2" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/c8yfetcher" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/subcommand" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmdutil" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/completion" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/flags" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/mapbuilder" + "github.com/reubenmiller/go-c8y/pkg/c8y" + "github.com/reubenmiller/go-c8y/pkg/remoteaccess" + "github.com/spf13/cobra" + "github.com/tidwall/gjson" +) + +type CmdSSH struct { + device []string + externalID []string + externalIDType string + listen string + user string + configuration string + + *subcommand.SubCommand + + factory *cmdutil.Factory +} + +func NewCmdSSH(f *cmdutil.Factory) *CmdSSH { + ccmd := &CmdSSH{ + factory: f, + } + + cmd := &cobra.Command{ + Use: "ssh", + Short: "Connect to a device via ssh", + Long: heredoc.Doc(` + Connect to a device via ssh + + Additional arguments can be passed to the ssh shell by using the "--" convention where everything + after the "--" will be passed untouched to the ssh shell. In this mode, the shell will not be + interactive, and it will return upon completion of the command. + `), + Example: heredoc.Doc(` +$ c8y remoteaccess ssh --device 12345 +Start an interactive SSH session on the device + +$ c8y remoteaccess ssh --device 12345 --user admin +Start an interactive SSH session on the device with a given ssh user + +$ c8y remoteaccess ssh --device 12345 --user admin -- systemctl status +Use a non-interactive session to execute a single command and print the result + `), + RunE: ccmd.RunE, + } + + // Flags + cmd.Flags().StringSliceVar(&ccmd.device, "device", []string{}, "Device") + // cmd.Flags().StringSliceVar(&ccmd.externalID, "external-id", []string{}, "Device external identity") + // cmd.Flags().StringVar(&ccmd.externalIDType, "external-type", "c8y_Serial", "Device external identity") + cmd.Flags().StringVar(&ccmd.listen, "listen", "127.0.0.1:0", "Listener address. unix:///run/example.sock") + cmd.Flags().StringVar(&ccmd.user, "user", "", "Default ssh user") + cmd.Flags().StringVar(&ccmd.configuration, "configuration", "", "Remote Access Configuration") + + completion.WithOptions( + cmd, + completion.WithDevice("device", func() (*c8y.Client, error) { return ccmd.factory.Client() }), + ) + + // cmd.MarkFlagsMutuallyExclusive("device", "external-id") + // cmd.MarkFlagsMutuallyExclusive("device", "external-type") + + ccmd.SubCommand = subcommand.NewSubCommand(cmd) + + return ccmd +} + +func (n *CmdSSH) RunE(cmd *cobra.Command, args []string) error { + client, err := n.factory.Client() + if err != nil { + return err + } + cfg, err := n.factory.Config() + if err != nil { + return err + } + log, err := n.factory.Logger() + if err != nil { + return err + } + + inputIterators, err := cmdutil.NewRequestInputIterators(cmd, cfg) + if err != nil { + return err + } + + // TODO: This is overly complicated. Refactor once the generic work is done + body := mapbuilder.NewInitializedMapBuilder(true) + body.SetApplyTemplateOnMarshalPreference(true) + err = flags.WithBody( + cmd, + body, + inputIterators, + c8yfetcher.WithDeviceByNameFirstMatch(n.factory, args, "device", "device"), + ) + if err != nil { + return err + } + + out, err := body.MarshalJSON() + if err != nil { + return err + } + op := gjson.ParseBytes(out) + device := op.Get("device").String() + + craConfig, err := c8yfetcher.DetectRemoteAccessConfiguration(client, device, n.configuration) + if err != nil { + return err + } + + log.Debugf("Using remote access configuration: id=%s, name=%s", craConfig.ID, craConfig.Name) + + // Lookup configuration + craClient := remoteaccess.NewRemoteAccessClient(client, remoteaccess.RemoteAccessOptions{ + ManagedObjectID: device, + RemoteAccessID: craConfig.ID, + }, log) + + if n.listen == "-" { + log.Debugf("Listening to request from stdin") + return craClient.ListenServe(n.factory.IOStreams.In, n.factory.IOStreams.Out) + } + + // TCP / socket listener + if err := craClient.Listen(n.listen); err != nil { + return err + } + + localAddress := craClient.GetListenerAddress() + host, port, _ := strings.Cut(localAddress, ":") + if host == "" { + host = "127.0.0.1" + } + + // Start in background + // TODO: Work out how to shut it down cleanly + go craClient.Serve() + + // Build ssh command + sshArgs := []string{ + "-o", "ServerAliveInterval=120", + "-o", "StrictHostKeyChecking=no", + "-o", "UserKnownHostsFile=/dev/null", + } + + sshTarget := host + if n.user != "" { + sshTarget = fmt.Sprintf("%s@%s", n.user, host) + } + sshArgs = append(sshArgs, "-p", port, sshTarget) + + dashIdx := cmd.ArgsLenAtDash() + if dashIdx > -1 { + sshArgs = append(sshArgs, "--") + sshArgs = append(sshArgs, args[dashIdx:]...) + } + + sshCmd := exec.CommandContext(context.Background(), "ssh", sshArgs...) + if n.factory.IOStreams.IsStdoutTTY() { + sshCmd.Stdout = os.Stdout + sshCmd.Stdin = os.Stdin + sshCmd.Stderr = os.Stderr + } + + log.Debugf("Executing command: ssh %s\n", strings.Join(sshArgs, " ")) + + cs := n.factory.IOStreams.ColorScheme() + fmt.Fprintln(n.factory.IOStreams.ErrOut, cs.Green(fmt.Sprintf("Starting interactive ssh session with %s (%s)\n", device, strings.TrimRight(client.BaseURL.String(), "/")))) + + start := time.Now() + sshErr := sshCmd.Run() + duration := time.Since(start).Truncate(time.Millisecond) + fmt.Fprintf(n.factory.IOStreams.ErrOut, "Duration: %s\n", duration) + return sshErr +} diff --git a/pkg/cmd/remoteaccess/remoteaccess.manual.go b/pkg/cmd/remoteaccess/remoteaccess.manual.go new file mode 100644 index 000000000..81d0ebb0e --- /dev/null +++ b/pkg/cmd/remoteaccess/remoteaccess.manual.go @@ -0,0 +1,31 @@ +package remoteaccess + +import ( + "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/remoteaccess/connect" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/remoteaccess/server" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/subcommand" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmdutil" + "github.com/spf13/cobra" +) + +type SubCmdRemoteAccess struct { + *subcommand.SubCommand +} + +func NewSubCommand(f *cmdutil.Factory) *SubCmdRemoteAccess { + ccmd := &SubCmdRemoteAccess{} + + cmd := &cobra.Command{ + Use: "remoteaccess", + Short: "Cumulocity remoteaccess management", + Long: `Cumulocity remoteaccess management`, + } + + // Subcommands + cmd.AddCommand(connect.NewSubCommand(f).GetCommand()) + cmd.AddCommand(server.NewCmdServer(f).GetCommand()) + + ccmd.SubCommand = subcommand.NewSubCommand(cmd) + + return ccmd +} diff --git a/pkg/cmd/remoteaccess/server/server.manual.go b/pkg/cmd/remoteaccess/server/server.manual.go new file mode 100644 index 000000000..cdc1da197 --- /dev/null +++ b/pkg/cmd/remoteaccess/server/server.manual.go @@ -0,0 +1,189 @@ +package server + +import ( + "fmt" + "strings" + "text/template" + + "github.com/MakeNowJust/heredoc/v2" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/c8yfetcher" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/subcommand" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmdutil" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/completion" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/flags" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/mapbuilder" + "github.com/reubenmiller/go-c8y/pkg/remoteaccess" + "github.com/spf13/cobra" + "github.com/tidwall/gjson" +) + +type CmdServer struct { + device []string + listen string + configuration string + open bool + + *subcommand.SubCommand + + factory *cmdutil.Factory +} + +func NewCmdServer(f *cmdutil.Factory) *CmdServer { + ccmd := &CmdServer{ + factory: f, + } + + cmd := &cobra.Command{ + Use: "server", + Short: "Start a local proxy server", + Long: `Start a local proxy server`, + Example: heredoc.Doc(` +$ c8y remoteaccess server --device 12345 +Start a local proxy server on a random local port + +$ c8y remoteaccess server --device 12345 --listen - +Start a local proxy server reading from stdin and writing to stdout (useful for usage with the ProxyCommand in ssh) + +$ c8y remoteaccess server --device 12345 --listen unix:///run/example.sock +Start a local proxy using a UNIX socket + +$ c8y remoteaccess server --device 12345 --listen 127.0.0.1:22022 +Start a local proxy using a local TCP server on a fixed port 22022 + +$ c8y remoteaccess server --device 12345 --configuration "*rugpi*" --browser +Start a local proxy and match on the configuration using wildcards, then open the browser to the endpoint + `), + RunE: ccmd.RunE, + } + + // Flags + cmd.Flags().StringSliceVar(&ccmd.device, "device", []string{}, "Device") + cmd.Flags().StringVar(&ccmd.listen, "listen", "127.0.0.1:0", "Listen. unix:///run/example.sock") + cmd.Flags().StringVar(&ccmd.configuration, "configuration", "", "Remote Access Configuration. Accepts wildcards") + cmd.Flags().BoolVar(&ccmd.open, "browser", false, "Open the endpoint in a browser (if available)") + + completion.WithOptions( + cmd, + completion.WithDevice("device", f.Client), + ) + + ccmd.SubCommand = subcommand.NewSubCommand(cmd) + + return ccmd +} + +func (n *CmdServer) RunE(cmd *cobra.Command, args []string) error { + client, err := n.factory.Client() + if err != nil { + return err + } + + cfg, err := n.factory.Config() + if err != nil { + return err + } + + log, err := n.factory.Logger() + if err != nil { + return err + } + + inputIterators, err := cmdutil.NewRequestInputIterators(cmd, cfg) + if err != nil { + return err + } + + // TODO: This is overly complicated. Refactor once the generic work is done + body := mapbuilder.NewInitializedMapBuilder(true) + body.SetApplyTemplateOnMarshalPreference(true) + err = flags.WithBody( + cmd, + body, + inputIterators, + c8yfetcher.WithDeviceByNameFirstMatch(n.factory, args, "device", "device"), + ) + if err != nil { + return err + } + + out, err := body.MarshalJSON() + if err != nil { + return err + } + op := gjson.ParseBytes(out) + device := op.Get("device").String() + + craConfig, err := c8yfetcher.DetectRemoteAccessConfiguration(client, device, n.configuration) + if err != nil { + return err + } + + craClient := remoteaccess.NewRemoteAccessClient(client, remoteaccess.RemoteAccessOptions{ + ManagedObjectID: device, + RemoteAccessID: craConfig.ID, + }, log) + + if n.listen == "-" { + log.Debugf("Listening to request from stdin") + return craClient.ListenServe(n.factory.IOStreams.In, n.factory.IOStreams.Out) + } + + // TCP / socket listener + if err := craClient.Listen(n.listen); err != nil { + return err + } + + localAddress := craClient.GetListenerAddress() + host, port, _ := strings.Cut(localAddress, ":") + if host == "" { + host = "127.0.0.1" + } + + // cs := n.factory.IOStreams.ColorScheme() + + type ServerInfo struct { + Port string + Host string + Device string + LocalAddress string + User string + CumulocityURL string + } + + messageTmpl := heredoc.Doc(` + Listening for device ({{.Device}}) {{.CumulocityURL}} on {{.LocalAddress}} + + Proxy: {{.LocalAddress}} + + Example clients: + + SSH: ssh -p {{.Port}} {{.User}}@{{.Host}} + + Website: http://{{.LocalAddress}} + + Press ctrl-c to shutdown the server + `) + + t := template.Must(template.New("message").Parse(messageTmpl)) + t.Execute(n.factory.IOStreams.ErrOut, ServerInfo{ + Host: host, + Port: port, + CumulocityURL: strings.TrimRight(client.BaseURL.String(), "/"), + LocalAddress: localAddress, + Device: n.device[0], + User: "", + }) + + if n.open { + go func() { + // TODO: Should it wait for the server to be up? + // time.Sleep(200 * time.Millisecond) + targetURL := fmt.Sprintf("http://%s:%s", host, port) + if err := n.factory.Browser.Browse(targetURL); err != nil { + cfg.Logger.Warnf("%s", err) + } + }() + } + + return craClient.Serve() +} diff --git a/pkg/cmd/root/root.go b/pkg/cmd/root/root.go index c9ec83384..87c9075a2 100644 --- a/pkg/cmd/root/root.go +++ b/pkg/cmd/root/root.go @@ -84,6 +84,7 @@ import ( operationsSubscribeCmd "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/operations/subscribe" operationsWaitCmd "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/operations/wait" realtimeCmd "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/realtime" + remoteaccessCmd "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/remoteaccess" retentionrulesCmd "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/retentionrules" sessionsCmd "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/sessions" settingsCmd "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/settings" @@ -416,6 +417,10 @@ func NewCmdRoot(f *cmdutil.Factory, version, buildDate string) *CmdRoot { notification2.AddCommand(notification2TokensCmd.NewSubCommand(f).GetCommand()) cmd.AddCommand(notification2) + // Cloud Remote Access + remoteaccess := remoteaccessCmd.NewSubCommand(f).GetCommand() + cmd.AddCommand(remoteaccess) + // configuration configuration := configurationCmd.NewSubCommand(f).GetCommand() cmd.AddCommand(configuration) From 4c3fb6ebd687d8b2527fb0bcb9f021ecb4ef6d7b Mon Sep 17 00:00:00 2001 From: Reuben Miller Date: Wed, 15 May 2024 09:18:04 +0200 Subject: [PATCH 03/23] fix(pipeline): set empty output if pipeline json property matching is not successful --- pkg/iterator/pipe.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/iterator/pipe.go b/pkg/iterator/pipe.go index 064b26169..d1e48e8fb 100644 --- a/pkg/iterator/pipe.go +++ b/pkg/iterator/pipe.go @@ -129,8 +129,9 @@ func (i *PipeIterator) GetNext() (line []byte, input interface{}, err error) { return []byte(""), line, nil } - // stop iterator if not found + // stop iterator if not found (and clear line as some consumers will ignore the EOF if there is some remaining data) err = io.EOF + line = []byte("") } } From 087b278fe33b28780970312c252de41e83f94810 Mon Sep 17 00:00:00 2001 From: Reuben Miller Date: Wed, 15 May 2024 09:18:28 +0200 Subject: [PATCH 04/23] feat: ignore arguments after -- --- pkg/c8yfetcher/c8yfetcher.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/pkg/c8yfetcher/c8yfetcher.go b/pkg/c8yfetcher/c8yfetcher.go index 245a65ce2..be990947a 100644 --- a/pkg/c8yfetcher/c8yfetcher.go +++ b/pkg/c8yfetcher/c8yfetcher.go @@ -570,10 +570,17 @@ func WithManagedObjectPropertyFirstMatch(factory *cmdutil.Factory, fetcher Entit } } +func ignoreArgsAfterDash(cmd *cobra.Command, args []string) []string { + if i := cmd.ArgsLenAtDash(); i > -1 { + return args[0:i] + } + return args[:] +} + // WithReferenceByNameFirstMatch add reference by name matching using a fetcher via cli args. Only the first match will be used func WithReferenceByNameFirstMatch(factory *cmdutil.Factory, fetcher EntityFetcher, args []string, opts ...string) flags.GetOption { return func(cmd *cobra.Command, inputIterators *flags.RequestInputIterators) (string, interface{}, error) { - opt := WithReferenceByName(factory, fetcher, args, opts...) + opt := WithReferenceByName(factory, fetcher, ignoreArgsAfterDash(cmd, args), opts...) name, values, err := opt(cmd, inputIterators) if name == "" { @@ -602,7 +609,7 @@ func WithReferenceByNameFirstMatch(factory *cmdutil.Factory, fetcher EntityFetch // WithSelfReferenceByNameFirstMatch add reference by name matching using a fetcher via cli args. Only the first match will be used func WithSelfReferenceByNameFirstMatch(factory *cmdutil.Factory, fetcher EntityFetcher, args []string, opts ...string) flags.GetOption { return func(cmd *cobra.Command, inputIterators *flags.RequestInputIterators) (string, interface{}, error) { - opt := WithSelfReferenceByName(factory, fetcher, args, opts...) + opt := WithSelfReferenceByName(factory, fetcher, ignoreArgsAfterDash(cmd, args), opts...) name, values, err := opt(cmd, inputIterators) if name == "" { From 43e1a4e466fca146ddc1ab9f450f3f5d560e44c0 Mon Sep 17 00:00:00 2001 From: Reuben Miller Date: Wed, 15 May 2024 09:18:59 +0200 Subject: [PATCH 05/23] accept device via pipeline --- .../remoteaccess/connect/ssh/ssh.manual.go | 160 ++++++++++-------- 1 file changed, 88 insertions(+), 72 deletions(-) diff --git a/pkg/cmd/remoteaccess/connect/ssh/ssh.manual.go b/pkg/cmd/remoteaccess/connect/ssh/ssh.manual.go index 01606e030..8563d3c51 100644 --- a/pkg/cmd/remoteaccess/connect/ssh/ssh.manual.go +++ b/pkg/cmd/remoteaccess/connect/ssh/ssh.manual.go @@ -3,7 +3,6 @@ package ssh import ( "context" "fmt" - "os" "os/exec" "strings" "time" @@ -14,7 +13,9 @@ import ( "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmdutil" "github.com/reubenmiller/go-c8y-cli/v2/pkg/completion" "github.com/reubenmiller/go-c8y-cli/v2/pkg/flags" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/iterator" "github.com/reubenmiller/go-c8y-cli/v2/pkg/mapbuilder" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/worker" "github.com/reubenmiller/go-c8y/pkg/c8y" "github.com/reubenmiller/go-c8y/pkg/remoteaccess" "github.com/spf13/cobra" @@ -75,6 +76,11 @@ Use a non-interactive session to execute a single command and print the result completion.WithDevice("device", func() (*c8y.Client, error) { return ccmd.factory.Client() }), ) + flags.WithOptions( + cmd, + flags.WithExtendedPipelineSupport("device", "device", false, "deviceId", "source.id", "managedObject.id", "id"), + ) + // cmd.MarkFlagsMutuallyExclusive("device", "external-id") // cmd.MarkFlagsMutuallyExclusive("device", "external-type") @@ -115,80 +121,90 @@ func (n *CmdSSH) RunE(cmd *cobra.Command, args []string) error { return err } - out, err := body.MarshalJSON() - if err != nil { - return err + var iter iterator.Iterator + if inputIterators.Total > 0 { + iter = mapbuilder.NewMapBuilderIterator(body) + } else { + iter = iterator.NewBoundIterator(mapbuilder.NewMapBuilderIterator(body), 1) } - op := gjson.ParseBytes(out) - device := op.Get("device").String() - craConfig, err := c8yfetcher.DetectRemoteAccessConfiguration(client, device, n.configuration) + commonOptions, err := cfg.GetOutputCommonOptions(cmd) if err != nil { return err } - - log.Debugf("Using remote access configuration: id=%s, name=%s", craConfig.ID, craConfig.Name) - - // Lookup configuration - craClient := remoteaccess.NewRemoteAccessClient(client, remoteaccess.RemoteAccessOptions{ - ManagedObjectID: device, - RemoteAccessID: craConfig.ID, - }, log) - - if n.listen == "-" { - log.Debugf("Listening to request from stdin") - return craClient.ListenServe(n.factory.IOStreams.In, n.factory.IOStreams.Out) - } - - // TCP / socket listener - if err := craClient.Listen(n.listen); err != nil { - return err - } - - localAddress := craClient.GetListenerAddress() - host, port, _ := strings.Cut(localAddress, ":") - if host == "" { - host = "127.0.0.1" - } - - // Start in background - // TODO: Work out how to shut it down cleanly - go craClient.Serve() - - // Build ssh command - sshArgs := []string{ - "-o", "ServerAliveInterval=120", - "-o", "StrictHostKeyChecking=no", - "-o", "UserKnownHostsFile=/dev/null", - } - - sshTarget := host - if n.user != "" { - sshTarget = fmt.Sprintf("%s@%s", n.user, host) - } - sshArgs = append(sshArgs, "-p", port, sshTarget) - - dashIdx := cmd.ArgsLenAtDash() - if dashIdx > -1 { - sshArgs = append(sshArgs, "--") - sshArgs = append(sshArgs, args[dashIdx:]...) - } - - sshCmd := exec.CommandContext(context.Background(), "ssh", sshArgs...) - if n.factory.IOStreams.IsStdoutTTY() { - sshCmd.Stdout = os.Stdout - sshCmd.Stdin = os.Stdin - sshCmd.Stderr = os.Stderr - } - - log.Debugf("Executing command: ssh %s\n", strings.Join(sshArgs, " ")) - - cs := n.factory.IOStreams.ColorScheme() - fmt.Fprintln(n.factory.IOStreams.ErrOut, cs.Green(fmt.Sprintf("Starting interactive ssh session with %s (%s)\n", device, strings.TrimRight(client.BaseURL.String(), "/")))) - - start := time.Now() - sshErr := sshCmd.Run() - duration := time.Since(start).Truncate(time.Millisecond) - fmt.Fprintf(n.factory.IOStreams.ErrOut, "Duration: %s\n", duration) - return sshErr + commonOptions.DisableResultPropertyDetection() + + return n.factory.RunWithGenericWorkers(cmd, inputIterators, iter, func(j worker.Job) (any, error) { + item := gjson.ParseBytes(j.Value.([]byte)) + device := item.Get("device").String() + + craConfig, err := c8yfetcher.DetectRemoteAccessConfiguration(client, device, n.configuration) + if err != nil { + return nil, err + } + + log.Debugf("Using remote access configuration: id=%s, name=%s", craConfig.ID, craConfig.Name) + + // Lookup configuration + craClient := remoteaccess.NewRemoteAccessClient(client, remoteaccess.RemoteAccessOptions{ + ManagedObjectID: device, + RemoteAccessID: craConfig.ID, + }, log) + + // TCP / socket listener + if err := craClient.Listen(n.listen); err != nil { + return nil, err + } + + localAddress := craClient.GetListenerAddress() + host, port, _ := strings.Cut(localAddress, ":") + if host == "" { + host = "127.0.0.1" + } + + // Start in background + // TODO: Work out how to shut it down cleanly + go craClient.Serve() + + // Build ssh command + sshArgs := []string{ + "-o", "ServerAliveInterval=120", + "-o", "StrictHostKeyChecking=no", + "-o", "UserKnownHostsFile=/dev/null", + } + + sshTarget := host + if n.user != "" { + sshTarget = fmt.Sprintf("%s@%s", n.user, host) + } + sshArgs = append(sshArgs, "-p", port, sshTarget) + + dashIdx := cmd.ArgsLenAtDash() + if dashIdx > -1 { + sshArgs = append(sshArgs, "--") + sshArgs = append(sshArgs, args[dashIdx:]...) + } + + // TODO: Should output templates be considered here? + sshCmd := exec.CommandContext(context.Background(), "ssh", sshArgs...) + sshCmd.Stdout = n.factory.IOStreams.Out + sshCmd.Stdin = n.factory.IOStreams.In + sshCmd.Stderr = n.factory.IOStreams.ErrOut + + log.Debugf("Executing command: ssh %s\n", strings.Join(sshArgs, " ")) + + cs := n.factory.IOStreams.ColorScheme() + fmt.Fprintln(n.factory.IOStreams.ErrOut, cs.Green(fmt.Sprintf("Starting interactive ssh session with %s (%s)\n", device, strings.TrimRight(client.BaseURL.String(), "/")))) + + start := time.Now() + sshErr := sshCmd.Run() + duration := time.Since(start).Truncate(time.Millisecond) + fmt.Fprintf(n.factory.IOStreams.ErrOut, "Duration: %s\n", duration) + + // err := n.factory.WriteOutput(output, cmdutil.OutputContext{ + // Input: j.Input, + // }, &commonOptions) + // return nil, err + return nil, sshErr + }) } From 7e95f28c30fd75b7f8308219636b98ad27246f33 Mon Sep 17 00:00:00 2001 From: Reuben Miller Date: Wed, 15 May 2024 10:01:07 +0200 Subject: [PATCH 06/23] fixup! fix(pipeline): set empty output if pipeline json property matching is not successful --- pkg/iterator/pipe.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkg/iterator/pipe.go b/pkg/iterator/pipe.go index d1e48e8fb..064b26169 100644 --- a/pkg/iterator/pipe.go +++ b/pkg/iterator/pipe.go @@ -129,9 +129,8 @@ func (i *PipeIterator) GetNext() (line []byte, input interface{}, err error) { return []byte(""), line, nil } - // stop iterator if not found (and clear line as some consumers will ignore the EOF if there is some remaining data) + // stop iterator if not found err = io.EOF - line = []byte("") } } From 0ed1cfb01d612d9eea3be4b73687084dab468670 Mon Sep 17 00:00:00 2001 From: Reuben Miller Date: Wed, 15 May 2024 19:00:44 +0200 Subject: [PATCH 07/23] add remote access configuration management --- api/spec/json/remoteAccessConfiguration.json | 523 ++++++++++++++++++ api/spec/yaml/remoteAccessConfiguration.yaml | 389 +++++++++++++ .../configuration/configuration.auto.go | 43 ++ .../create_passthrough.auto.go | 186 +++++++ .../create_telnet/create_telnet.auto.go | 185 +++++++ .../create_vnc/create_vnc.auto.go | 188 +++++++ .../create_webssh/create_webssh.auto.go | 195 +++++++ .../configuration/delete/delete.auto.go | 169 ++++++ .../configuration/get/get.auto.go | 173 ++++++ .../configuration/list/list.auto.go | 172 ++++++ .../configuration/update/update.auto.go | 177 ++++++ pkg/cmd/remoteaccess/remoteaccess.manual.go | 2 + scripts/build-cli/New-C8yApi.ps1 | 2 +- scripts/build-cli/New-C8yApiGoCommand.ps1 | 7 +- scripts/build-cli/New-C8yApiGoRootCommand.ps1 | 4 +- .../configuration_create-passthrough.yaml | 20 + .../configuration_create-telnet.yaml | 9 + .../configuration_create-vnc.yaml | 17 + .../configuration_create-webssh.yaml | 18 + .../remoteaccess/configuration_delete.yaml | 8 + .../tests/remoteaccess/configuration_get.yaml | 8 + .../remoteaccess/configuration_list.yaml | 8 + .../remoteaccess/configuration_update.yaml | 8 + tools/PSc8y/PSc8y.psd1 | 7 + .../Public/Get-RemoteAccessConfiguration.ps1 | 73 +++ ...et-RemoteAccessConfigurationCollection.ps1 | 69 +++ ...w-RemoteAccessPassthroughConfiguration.ps1 | 97 ++++ .../New-RemoteAccessTelnetConfiguration.ps1 | 96 ++++ .../New-RemoteAccessVNCConfiguration.ps1 | 101 ++++ .../New-RemoteAccessWebSSHConfiguration.ps1 | 122 ++++ .../Remove-RemoteAccessConfiguration.ps1 | 73 +++ ...t-RemoteAccessConfiguration.auto.Tests.ps1 | 18 + ...cessConfigurationCollection.auto.Tests.ps1 | 18 + ...essPassthroughConfiguration.auto.Tests.ps1 | 25 + ...teAccessTelnetConfiguration.auto.Tests.ps1 | 19 + ...emoteAccessVNCConfiguration.auto.Tests.ps1 | 24 + ...teAccessWebSSHConfiguration.auto.Tests.ps1 | 24 + ...e-RemoteAccessConfiguration.auto.Tests.ps1 | 18 + 38 files changed, 3289 insertions(+), 6 deletions(-) create mode 100644 api/spec/json/remoteAccessConfiguration.json create mode 100644 api/spec/yaml/remoteAccessConfiguration.yaml create mode 100644 pkg/cmd/remoteaccess/configuration/configuration.auto.go create mode 100644 pkg/cmd/remoteaccess/configuration/create_passthrough/create_passthrough.auto.go create mode 100644 pkg/cmd/remoteaccess/configuration/create_telnet/create_telnet.auto.go create mode 100644 pkg/cmd/remoteaccess/configuration/create_vnc/create_vnc.auto.go create mode 100644 pkg/cmd/remoteaccess/configuration/create_webssh/create_webssh.auto.go create mode 100644 pkg/cmd/remoteaccess/configuration/delete/delete.auto.go create mode 100644 pkg/cmd/remoteaccess/configuration/get/get.auto.go create mode 100644 pkg/cmd/remoteaccess/configuration/list/list.auto.go create mode 100644 pkg/cmd/remoteaccess/configuration/update/update.auto.go create mode 100644 tests/auto/remoteaccessconfiguration/tests/remoteaccess/configuration_create-passthrough.yaml create mode 100644 tests/auto/remoteaccessconfiguration/tests/remoteaccess/configuration_create-telnet.yaml create mode 100644 tests/auto/remoteaccessconfiguration/tests/remoteaccess/configuration_create-vnc.yaml create mode 100644 tests/auto/remoteaccessconfiguration/tests/remoteaccess/configuration_create-webssh.yaml create mode 100644 tests/auto/remoteaccessconfiguration/tests/remoteaccess/configuration_delete.yaml create mode 100644 tests/auto/remoteaccessconfiguration/tests/remoteaccess/configuration_get.yaml create mode 100644 tests/auto/remoteaccessconfiguration/tests/remoteaccess/configuration_list.yaml create mode 100644 tests/auto/remoteaccessconfiguration/tests/remoteaccess/configuration_update.yaml create mode 100644 tools/PSc8y/Public/Get-RemoteAccessConfiguration.ps1 create mode 100644 tools/PSc8y/Public/Get-RemoteAccessConfigurationCollection.ps1 create mode 100644 tools/PSc8y/Public/New-RemoteAccessPassthroughConfiguration.ps1 create mode 100644 tools/PSc8y/Public/New-RemoteAccessTelnetConfiguration.ps1 create mode 100644 tools/PSc8y/Public/New-RemoteAccessVNCConfiguration.ps1 create mode 100644 tools/PSc8y/Public/New-RemoteAccessWebSSHConfiguration.ps1 create mode 100644 tools/PSc8y/Public/Remove-RemoteAccessConfiguration.ps1 create mode 100644 tools/PSc8y/Tests/Get-RemoteAccessConfiguration.auto.Tests.ps1 create mode 100644 tools/PSc8y/Tests/Get-RemoteAccessConfigurationCollection.auto.Tests.ps1 create mode 100644 tools/PSc8y/Tests/New-RemoteAccessPassthroughConfiguration.auto.Tests.ps1 create mode 100644 tools/PSc8y/Tests/New-RemoteAccessTelnetConfiguration.auto.Tests.ps1 create mode 100644 tools/PSc8y/Tests/New-RemoteAccessVNCConfiguration.auto.Tests.ps1 create mode 100644 tools/PSc8y/Tests/New-RemoteAccessWebSSHConfiguration.auto.Tests.ps1 create mode 100644 tools/PSc8y/Tests/Remove-RemoteAccessConfiguration.auto.Tests.ps1 diff --git a/api/spec/json/remoteAccessConfiguration.json b/api/spec/json/remoteAccessConfiguration.json new file mode 100644 index 000000000..22ea74bef --- /dev/null +++ b/api/spec/json/remoteAccessConfiguration.json @@ -0,0 +1,523 @@ +{ + "group": { + "name": "remoteaccess/configuration", + "description": "Manage Cloud Remote Access configuration", + "descriptionLong": "Cloud Remote Access configuration management", + "link": "https://cumulocity.com/docs/cloud-remote-access/using-cloud-remote-access/" + }, + "commands": [ + { + "name": "list", + "method": "GET", + "description": "List remote access configurations", + "descriptionLong": "List the remote access configurations already configured for the device\n", + "alias": { + "go": "list", + "powershell": "Get-RemoteAccessConfigurationCollection" + }, + "examples": { + "go": [ + { + "command": "c8y remoteaccess configurations list --device mydevice", + "description": "List remote access configurations for a given device" + } + ], + "powershell": [ + { + "command": "Get-RemoteAccessConfigurationCollection -Device mydevice", + "description": "List remote access configurations for a given device" + } + ] + }, + "path": "/service/remoteaccess/devices/{device}/configurations", + "pathParameters": [ + { + "name": "device", + "type": "device[]", + "description": "Device", + "pipeline": true + } + ] + }, + { + "name": "get", + "description": "Get remote access configuration", + "descriptionLong": "Get an existing remote access configuration for a device", + "alias": { + "go": "get", + "powershell": "Get-RemoteAccessConfiguration" + }, + "method": "GET", + "path": "/service/remoteaccess/devices/{device}/configurations/{id}", + "examples": { + "go": [ + { + "command": "c8y remoteaccess configurations get --device mydevice --id 1", + "description": "Get existing remote access configuration" + } + ], + "powershell": [ + { + "command": "Get-RemoteAccessConfiguration -Device mydevice -Id 1", + "description": "Get existing remote access configuration" + } + ] + }, + "pathParameters": [ + { + "name": "device", + "type": "device[]", + "description": "Device", + "pipeline": false + }, + { + "name": "id", + "type": "string", + "description": "Connection", + "pipeline": true + } + ] + }, + { + "name": "update", + "hidden": true, + "description": "Update remote access configuration", + "descriptionLong": "Update an existing remote access configuration", + "alias": { + "go": "update", + "powershell": "Update-RemoteAccessConfiguration" + }, + "examples": { + "go": [ + { + "command": "c8y remoteaccess configurations update --device mydevice --id 1", + "description": "Update an existing remote access configuration" + } + ], + "powershell": [ + { + "command": "Update-RemoteAccessConfiguration -Device mydevice -Id 1", + "description": "Update an existing remote access configuration" + } + ] + }, + "method": "PUT", + "path": "/service/remoteaccess/devices/{device}/configurations/{id}", + "pathParameters": [ + { + "name": "device", + "type": "device[]", + "description": "Device", + "pipeline": false + }, + { + "name": "id", + "type": "string", + "description": "Connection", + "pipeline": true + } + ], + "body": [ + { + "name": "name", + "type": "string", + "description": "Profile name", + "required": true + } + ] + }, + { + "name": "delete", + "description": "Delete remote access configuration", + "alias": { + "go": "delete", + "powershell": "Remove-RemoteAccessConfiguration" + }, + "examples": { + "go": [ + { + "command": "c8y remoteaccess configurations delete --device mydevice --id 1", + "description": "Delete an existing remote access configuration" + } + ], + "powershell": [ + { + "command": "Remove-RemoteAccessConfiguration -Device mydevice -Id 1", + "description": "Delete an existing remote access configuration" + } + ] + }, + "method": "DELETE", + "path": "/service/remoteaccess/devices/{device}/configurations/{id}", + "pathParameters": [ + { + "name": "device", + "type": "device[]", + "description": "Device", + "pipeline": false + }, + { + "name": "id", + "type": "string", + "description": "Connection", + "pipeline": true + } + ] + }, + { + "name": "create-passthrough", + "description": "Create passthrough configuration", + "descriptionLong": "Create passthrough configuration\n", + "alias": { + "go": "create-passthrough", + "powershell": "New-RemoteAccessPassthroughConfiguration" + }, + "method": "POST", + "path": "/service/remoteaccess/devices/{device}/configurations", + "examples": { + "go": [ + { + "command": "c8y remoteaccess configurations create-passthrough --device mydevice\n", + "description": "Create a SSH passthrough configuration to the localhost" + }, + { + "command": "c8y remoteaccess configurations create-passthrough --device mydevice --hostname customhost --port 1234 --name \"My custom configuration\"\n", + "description": "Create a SSH passthrough configuration with custom details" + } + ], + "powershell": [ + { + "command": "New-RemoteAccessPassthroughConfiguration -Device mydevice\n", + "description": "Create a SSH passthrough configuration to the localhost" + }, + { + "command": "New-RemoteAccessPassthroughConfiguration -Device mydevice -Hostname customhost -Port 1234 -Name \"My custom configuration\"\n", + "description": "Create a SSH passthrough configuration with custom details" + } + ] + }, + "pathParameters": [ + { + "name": "device", + "type": "device[]", + "description": "Device", + "pipeline": true + } + ], + "body": [ + { + "name": "name", + "type": "string", + "description": "Connection name", + "default": "passthrough" + }, + { + "name": "hostname", + "type": "string", + "default": "127.0.0.1", + "description": "Hostname" + }, + { + "name": "port", + "type": "integer", + "default": "22", + "description": "Port" + }, + { + "name": "protocol", + "type": "string", + "default": "PASSTHROUGH", + "validationSet": [ + "PASSTHROUGH" + ], + "description": "Protocol" + } + ], + "bodyRequiredKeys": [ + "hostname", + "port", + "protocol", + "name" + ], + "bodyTemplates": [ + { + "type": "jsonnet", + "template": "{credentialsType:'NONE'}" + } + ] + }, + { + "name": "create-webssh", + "description": "Create web ssh configuration", + "descriptionLong": "Create web ssh configuration\n", + "alias": { + "go": "create-webssh", + "powershell": "New-RemoteAccessWebSSHConfiguration" + }, + "method": "POST", + "path": "/service/remoteaccess/devices/{device}/configurations", + "examples": { + "go": [ + { + "command": "c8y remoteaccess configurations create-webssh\n", + "description": "Create a webssh configuration" + }, + { + "command": "c8y remoteaccess configurations create-webssh --hostname 127.0.0.1 --port 2222", + "description": "Create a webssh configuration with a custom hostname and port" + } + ], + "powershell": [ + { + "command": "New-RemoteAccessWebSSHConfiguration\n", + "description": "Create a webssh configuration" + }, + { + "command": "New-RemoteAccessWebSSHConfiguration -Hostname 127.0.0.1 -Port 2222", + "description": "Create a webssh configuration with a custom hostname and port" + } + ] + }, + "pathParameters": [ + { + "name": "device", + "type": "device[]", + "description": "Device", + "pipeline": true + } + ], + "body": [ + { + "name": "name", + "type": "string", + "description": "Connection name", + "default": "webssh" + }, + { + "name": "hostname", + "type": "string", + "description": "Hostname" + }, + { + "name": "port", + "type": "integer", + "description": "Port" + }, + { + "name": "credentialsType", + "type": "string", + "description": "Credentials type", + "default": "USER_PASS", + "validationSet": [ + "USER_PASS" + ] + }, + { + "name": "privateKey", + "type": "string", + "description": "Private ssh key" + }, + { + "name": "publicKey", + "type": "string", + "description": "Public ssh key" + }, + { + "name": "username", + "type": "string", + "description": "Username" + }, + { + "name": "password", + "type": "string", + "description": "Username" + }, + { + "name": "protocol", + "type": "string", + "default": "SSH", + "validationSet": [ + "PASSTHROUGH", + "SSH" + ], + "description": "Protocol" + } + ], + "bodyRequiredKeys": [ + "hostname", + "port", + "protocol", + "name" + ] + }, + { + "name": "create-vnc", + "description": "Create vnc configuration", + "descriptionLong": "Create vnc configuration\n", + "alias": { + "go": "create-vnc", + "powershell": "New-RemoteAccessVNCConfiguration" + }, + "method": "POST", + "path": "/service/remoteaccess/devices/{device}/configurations", + "examples": { + "go": [ + { + "command": "c8y remoteaccess configurations create-vnc\n", + "description": "Create a VNC configuration that does not require a password" + }, + { + "command": "c8y remoteaccess configurations create-vnc --password 'asd08dcj23dsf{@#9}'", + "description": "Create a VNC configuration that requires a password" + } + ], + "powershell": [ + { + "command": "New-RemoteAccessVNCConfiguration\n", + "description": "Create a VNC configuration that does not require a password" + }, + { + "command": "New-RemoteAccessVNCConfiguration -Password 'asd08dcj23dsf{@#9}'", + "description": "Create a VNC configuration that requires a password" + } + ] + }, + "pathParameters": [ + { + "name": "device", + "type": "device[]", + "description": "Device", + "pipeline": true + } + ], + "body": [ + { + "name": "name", + "type": "string", + "description": "Connection name", + "default": "webssh" + }, + { + "name": "hostname", + "type": "string", + "default": "127.0.0.1", + "description": "Hostname" + }, + { + "name": "port", + "type": "integer", + "default": "5900", + "description": "Port" + }, + { + "name": "password", + "type": "string", + "description": "VNC Password" + }, + { + "name": "protocol", + "type": "string", + "default": "VNC", + "validationSet": [ + "PASSTHROUGH", + "SSH", + "VNC" + ], + "description": "Protocol" + } + ], + "bodyRequiredKeys": [ + "name", + "hostname", + "port", + "protocol", + "credentialsType" + ], + "bodyTemplates": [ + { + "type": "jsonnet", + "template": "{credentialsType: if std.isEmpty(std.get($, 'password', '')) then 'NONE' else 'PASS_ONLY'}" + } + ] + }, + { + "name": "create-telnet", + "description": "Create telnet configuration", + "descriptionLong": "Create telnet configuration\n", + "alias": { + "go": "create-telnet", + "powershell": "New-RemoteAccessTelnetConfiguration" + }, + "method": "POST", + "path": "/service/remoteaccess/devices/{device}/configurations", + "examples": { + "go": [ + { + "command": "c8y remoteaccess configurations create-telnet\n", + "description": "Create a telnet configuration" + } + ], + "powershell": [ + { + "command": "New-RemoteAccessTelnetConfiguration\n", + "description": "Create a telnet configuration" + } + ] + }, + "pathParameters": [ + { + "name": "device", + "type": "device[]", + "description": "Device", + "pipeline": true + } + ], + "body": [ + { + "name": "name", + "type": "string", + "description": "Connection name", + "default": "telnet" + }, + { + "name": "hostname", + "type": "string", + "default": "127.0.0.1", + "description": "Hostname" + }, + { + "name": "port", + "type": "integer", + "default": "23", + "description": "Port" + }, + { + "name": "credentialsType", + "type": "string", + "default": "NONE", + "hidden": true, + "description": "Credentials type" + }, + { + "name": "protocol", + "type": "string", + "default": "TELNET", + "validationSet": [ + "TELNET", + "PASSTHROUGH", + "SSH", + "VNC" + ], + "description": "Protocol" + } + ], + "bodyRequiredKeys": [ + "name", + "hostname", + "port", + "protocol", + "credentialsType" + ] + } + ] +} diff --git a/api/spec/yaml/remoteAccessConfiguration.yaml b/api/spec/yaml/remoteAccessConfiguration.yaml new file mode 100644 index 000000000..8d0b44ff2 --- /dev/null +++ b/api/spec/yaml/remoteAccessConfiguration.yaml @@ -0,0 +1,389 @@ +# yaml-language-server: $schema=../schema.json +--- +group: + name: remoteaccess/configuration + description: Manage Cloud Remote Access configuration + descriptionLong: Cloud Remote Access configuration management + link: https://cumulocity.com/docs/cloud-remote-access/using-cloud-remote-access/ + +commands: + - name: list + method: GET + description: List remote access configurations + descriptionLong: | + List the remote access configurations already configured for the device + alias: + go: list + powershell: Get-RemoteAccessConfigurationCollection + examples: + go: + - command: c8y remoteaccess configurations list --device mydevice + description: List remote access configurations for a given device + powershell: + - command: Get-RemoteAccessConfigurationCollection -Device mydevice + description: List remote access configurations for a given device + + path: /service/remoteaccess/devices/{device}/configurations + pathParameters: + - name: device + type: device[] + description: Device + pipeline: true + + - name: get + description: Get remote access configuration + descriptionLong: Get an existing remote access configuration for a device + alias: + go: get + powershell: Get-RemoteAccessConfiguration + method: GET + path: /service/remoteaccess/devices/{device}/configurations/{id} + examples: + go: + - command: c8y remoteaccess configurations get --device mydevice --id 1 + description: Get existing remote access configuration + powershell: + - command: Get-RemoteAccessConfiguration -Device mydevice -Id 1 + description: Get existing remote access configuration + pathParameters: + - name: device + type: device[] + description: Device + pipeline: false + + - name: id + type: string + description: Connection + pipeline: true + + - name: update + hidden: true + description: Update remote access configuration + descriptionLong: Update an existing remote access configuration + alias: + go: update + powershell: Update-RemoteAccessConfiguration + examples: + go: + - command: c8y remoteaccess configurations update --device mydevice --id 1 + description: Update an existing remote access configuration + powershell: + - command: Update-RemoteAccessConfiguration -Device mydevice -Id 1 + description: Update an existing remote access configuration + method: PUT + path: /service/remoteaccess/devices/{device}/configurations/{id} + pathParameters: + - name: device + type: device[] + description: Device + pipeline: false + + - name: id + type: string + description: Connection + pipeline: true + body: + - name: name + type: string + description: Profile name + required: true + + - name: delete + description: Delete remote access configuration + alias: + go: delete + powershell: Remove-RemoteAccessConfiguration + examples: + go: + - command: c8y remoteaccess configurations delete --device mydevice --id 1 + description: Delete an existing remote access configuration + powershell: + - command: Remove-RemoteAccessConfiguration -Device mydevice -Id 1 + description: Delete an existing remote access configuration + method: DELETE + path: /service/remoteaccess/devices/{device}/configurations/{id} + pathParameters: + - name: device + type: device[] + description: Device + pipeline: false + + - name: id + type: string + description: Connection + pipeline: true + + - name: create-passthrough + description: Create passthrough configuration + descriptionLong: | + Create passthrough configuration + alias: + go: create-passthrough + powershell: New-RemoteAccessPassthroughConfiguration + method: POST + path: /service/remoteaccess/devices/{device}/configurations + examples: + go: + - command: | + c8y remoteaccess configurations create-passthrough --device mydevice + description: Create a SSH passthrough configuration to the localhost + - command: | + c8y remoteaccess configurations create-passthrough --device mydevice --hostname customhost --port 1234 --name "My custom configuration" + description: Create a SSH passthrough configuration with custom details + powershell: + - command: | + New-RemoteAccessPassthroughConfiguration -Device mydevice + description: Create a SSH passthrough configuration to the localhost + - command: | + New-RemoteAccessPassthroughConfiguration -Device mydevice -Hostname customhost -Port 1234 -Name "My custom configuration" + description: Create a SSH passthrough configuration with custom details + + pathParameters: + - name: device + type: device[] + description: Device + pipeline: true + body: + - name: name + type: string + description: Connection name + default: passthrough + + - name: hostname + type: string + default: "127.0.0.1" + description: Hostname + + - name: port + type: integer + default: "22" + description: Port + + - name: protocol + type: string + default: PASSTHROUGH + validationSet: + - PASSTHROUGH + description: Protocol + + bodyRequiredKeys: + - hostname + - port + - protocol + - name + bodyTemplates: + # Apply a static template to enforce specific fragments + - type: jsonnet + template: "{credentialsType:'NONE'}" + + - name: create-webssh + description: Create web ssh configuration + descriptionLong: | + Create web ssh configuration + alias: + go: create-webssh + powershell: New-RemoteAccessWebSSHConfiguration + method: POST + path: /service/remoteaccess/devices/{device}/configurations + examples: + go: + - command: | + c8y remoteaccess configurations create-webssh + description: Create a webssh configuration + + - command: c8y remoteaccess configurations create-webssh --hostname 127.0.0.1 --port 2222 + description: Create a webssh configuration with a custom hostname and port + powershell: + - command: | + New-RemoteAccessWebSSHConfiguration + description: Create a webssh configuration + + - command: New-RemoteAccessWebSSHConfiguration -Hostname 127.0.0.1 -Port 2222 + description: Create a webssh configuration with a custom hostname and port + pathParameters: + - name: device + type: device[] + description: Device + pipeline: true + body: + - name: name + type: string + description: Connection name + default: webssh + + - name: hostname + type: string + description: Hostname + + - name: port + type: integer + description: Port + + - name: credentialsType + type: string + description: Credentials type + default: USER_PASS + validationSet: + - USER_PASS + + - name: privateKey + type: string + description: Private ssh key + + - name: publicKey + type: string + description: Public ssh key + + - name: username + type: string + description: Username + + - name: password + type: string + description: Username + + - name: protocol + type: string + default: SSH + validationSet: + - PASSTHROUGH + - SSH + description: Protocol + + bodyRequiredKeys: + - hostname + - port + - protocol + - name + + - name: create-vnc + description: Create vnc configuration + descriptionLong: | + Create vnc configuration + alias: + go: create-vnc + powershell: New-RemoteAccessVNCConfiguration + method: POST + path: /service/remoteaccess/devices/{device}/configurations + examples: + go: + - command: | + c8y remoteaccess configurations create-vnc + description: Create a VNC configuration that does not require a password + + - command: c8y remoteaccess configurations create-vnc --password 'asd08dcj23dsf{@#9}' + description: Create a VNC configuration that requires a password + powershell: + - command: | + New-RemoteAccessVNCConfiguration + description: Create a VNC configuration that does not require a password + + - command: New-RemoteAccessVNCConfiguration -Password 'asd08dcj23dsf{@#9}' + description: Create a VNC configuration that requires a password + pathParameters: + - name: device + type: device[] + description: Device + pipeline: true + body: + - name: name + type: string + description: Connection name + default: webssh + + - name: hostname + type: string + default: "127.0.0.1" + description: Hostname + + - name: port + type: integer + default: "5900" + description: Port + + - name: password + type: string + description: VNC Password + + - name: protocol + type: string + default: VNC + validationSet: + - PASSTHROUGH + - SSH + - VNC + description: Protocol + + bodyRequiredKeys: + - name + - hostname + - port + - protocol + - credentialsType + bodyTemplates: + # Apply a static template to enforce specific fragments + - type: jsonnet + template: "{credentialsType: if std.isEmpty(std.get($, 'password', '')) then 'NONE' else 'PASS_ONLY'}" + + - name: create-telnet + description: Create telnet configuration + descriptionLong: | + Create telnet configuration + alias: + go: create-telnet + powershell: New-RemoteAccessTelnetConfiguration + method: POST + path: /service/remoteaccess/devices/{device}/configurations + examples: + go: + - command: | + c8y remoteaccess configurations create-telnet + description: Create a telnet configuration + powershell: + - command: | + New-RemoteAccessTelnetConfiguration + description: Create a telnet configuration + + pathParameters: + - name: device + type: device[] + description: Device + pipeline: true + body: + - name: name + type: string + description: Connection name + default: telnet + + - name: hostname + type: string + default: "127.0.0.1" + description: Hostname + + - name: port + type: integer + default: "23" + description: Port + + - name: credentialsType + type: string + default: NONE + hidden: true + description: Credentials type + + - name: protocol + type: string + default: TELNET + validationSet: + - TELNET + - PASSTHROUGH + - SSH + - VNC + description: Protocol + + bodyRequiredKeys: + - name + - hostname + - port + - protocol + - credentialsType diff --git a/pkg/cmd/remoteaccess/configuration/configuration.auto.go b/pkg/cmd/remoteaccess/configuration/configuration.auto.go new file mode 100644 index 000000000..78f2b0dbd --- /dev/null +++ b/pkg/cmd/remoteaccess/configuration/configuration.auto.go @@ -0,0 +1,43 @@ +package configuration + +import ( + cmdCreatePassthrough "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/remoteaccess/configuration/create_passthrough" + cmdCreateTelnet "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/remoteaccess/configuration/create_telnet" + cmdCreateVnc "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/remoteaccess/configuration/create_vnc" + cmdCreateWebssh "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/remoteaccess/configuration/create_webssh" + cmdDelete "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/remoteaccess/configuration/delete" + cmdGet "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/remoteaccess/configuration/get" + cmdList "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/remoteaccess/configuration/list" + cmdUpdate "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/remoteaccess/configuration/update" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/subcommand" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmdutil" + "github.com/spf13/cobra" +) + +type SubCmdConfiguration struct { + *subcommand.SubCommand +} + +func NewSubCommand(f *cmdutil.Factory) *SubCmdConfiguration { + ccmd := &SubCmdConfiguration{} + + cmd := &cobra.Command{ + Use: "configuration", + Short: "Manage Cloud Remote Access configuration", + Long: `Cloud Remote Access configuration management`, + } + + // Subcommands + cmd.AddCommand(cmdList.NewListCmd(f).GetCommand()) + cmd.AddCommand(cmdGet.NewGetCmd(f).GetCommand()) + cmd.AddCommand(cmdUpdate.NewUpdateCmd(f).GetCommand()) + cmd.AddCommand(cmdDelete.NewDeleteCmd(f).GetCommand()) + cmd.AddCommand(cmdCreatePassthrough.NewCreatePassthroughCmd(f).GetCommand()) + cmd.AddCommand(cmdCreateWebssh.NewCreateWebsshCmd(f).GetCommand()) + cmd.AddCommand(cmdCreateVnc.NewCreateVncCmd(f).GetCommand()) + cmd.AddCommand(cmdCreateTelnet.NewCreateTelnetCmd(f).GetCommand()) + + ccmd.SubCommand = subcommand.NewSubCommand(cmd) + + return ccmd +} diff --git a/pkg/cmd/remoteaccess/configuration/create_passthrough/create_passthrough.auto.go b/pkg/cmd/remoteaccess/configuration/create_passthrough/create_passthrough.auto.go new file mode 100644 index 000000000..b7aba8cd3 --- /dev/null +++ b/pkg/cmd/remoteaccess/configuration/create_passthrough/create_passthrough.auto.go @@ -0,0 +1,186 @@ +// Code generated from specification version 1.0.0: DO NOT EDIT +package create_passthrough + +import ( + "io" + "net/http" + + "github.com/MakeNowJust/heredoc/v2" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/c8yfetcher" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/subcommand" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmderrors" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmdutil" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/completion" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/flags" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/mapbuilder" + "github.com/reubenmiller/go-c8y/pkg/c8y" + "github.com/spf13/cobra" +) + +// CreatePassthroughCmd command +type CreatePassthroughCmd struct { + *subcommand.SubCommand + + factory *cmdutil.Factory +} + +// NewCreatePassthroughCmd creates a command to Create passthrough configuration +func NewCreatePassthroughCmd(f *cmdutil.Factory) *CreatePassthroughCmd { + ccmd := &CreatePassthroughCmd{ + factory: f, + } + cmd := &cobra.Command{ + Use: "create-passthrough", + Short: "Create passthrough configuration", + Long: `Create passthrough configuration +`, + Example: heredoc.Doc(` +$ c8y remoteaccess configurations create-passthrough --device mydevice +Create a SSH passthrough configuration to the localhost + +$ c8y remoteaccess configurations create-passthrough --device mydevice --hostname customhost --port 1234 --name "My custom configuration" +Create a SSH passthrough configuration with custom details + `), + PreRunE: func(cmd *cobra.Command, args []string) error { + return f.CreateModeEnabled() + }, + RunE: ccmd.RunE, + } + + cmd.SilenceUsage = true + + cmd.Flags().StringSlice("device", []string{""}, "Device (accepts pipeline)") + cmd.Flags().String("name", "passthrough", "Connection name") + cmd.Flags().String("hostname", "127.0.0.1", "Hostname") + cmd.Flags().Int("port", 22, "Port") + cmd.Flags().String("protocol", "PASSTHROUGH", "Protocol") + + completion.WithOptions( + cmd, + completion.WithDevice("device", func() (*c8y.Client, error) { return ccmd.factory.Client() }), + completion.WithValidateSet("protocol", "PASSTHROUGH"), + ) + + flags.WithOptions( + cmd, + flags.WithProcessingMode(), + + flags.WithExtendedPipelineSupport("device", "device", false, "deviceId", "source.id", "managedObject.id", "id"), + flags.WithPipelineAliases("device", "deviceId", "source.id", "managedObject.id", "id"), + ) + + // Required flags + + ccmd.SubCommand = subcommand.NewSubCommand(cmd) + + return ccmd +} + +// RunE executes the command +func (n *CreatePassthroughCmd) RunE(cmd *cobra.Command, args []string) error { + cfg, err := n.factory.Config() + if err != nil { + return err + } + // Runtime flag options + flags.WithOptions( + cmd, + flags.WithRuntimePipelineProperty(), + ) + client, err := n.factory.Client() + if err != nil { + return err + } + inputIterators, err := cmdutil.NewRequestInputIterators(cmd, cfg) + if err != nil { + return err + } + + // query parameters + query := flags.NewQueryTemplate() + err = flags.WithQueryParameters( + cmd, + query, + inputIterators, + flags.WithCustomStringSlice(func() ([]string, error) { return cfg.GetQueryParameters(), nil }, "custom"), + ) + if err != nil { + return cmderrors.NewUserError(err) + } + + queryValue, err := query.GetQueryUnescape(true) + + if err != nil { + return cmderrors.NewSystemError("Invalid query parameter") + } + + // headers + headers := http.Header{} + err = flags.WithHeaders( + cmd, + headers, + inputIterators, + flags.WithCustomStringSlice(func() ([]string, error) { return cfg.GetHeader(), nil }, "header"), + flags.WithProcessingModeValue(), + ) + if err != nil { + return cmderrors.NewUserError(err) + } + + // form data + formData := make(map[string]io.Reader) + err = flags.WithFormDataOptions( + cmd, + formData, + inputIterators, + ) + if err != nil { + return cmderrors.NewUserError(err) + } + + // body + body := mapbuilder.NewInitializedMapBuilder(true) + err = flags.WithBody( + cmd, + body, + inputIterators, + flags.WithDataFlagValue(), + flags.WithStringValue("name", "name"), + flags.WithStringValue("hostname", "hostname"), + flags.WithIntValue("port", "port"), + flags.WithStringValue("protocol", "protocol"), + flags.WithDefaultTemplateString(` +{credentialsType:'NONE'}`), + cmdutil.WithTemplateValue(n.factory), + flags.WithTemplateVariablesValue(), + flags.WithRequiredProperties("hostname", "port", "protocol", "name"), + ) + if err != nil { + return cmderrors.NewUserError(err) + } + + // path parameters + path := flags.NewStringTemplate("/service/remoteaccess/devices/{device}/configurations") + err = flags.WithPathParameters( + cmd, + path, + inputIterators, + c8yfetcher.WithDeviceByNameFirstMatch(n.factory, args, "device", "device"), + ) + if err != nil { + return err + } + + req := c8y.RequestOptions{ + Method: "POST", + Path: path.GetTemplate(), + Query: queryValue, + Body: body, + FormData: formData, + Header: headers, + IgnoreAccept: cfg.IgnoreAcceptHeader(), + DryRun: cfg.ShouldUseDryRun(cmd.CommandPath()), + } + + return n.factory.RunWithWorkers(client, cmd, &req, inputIterators) +} diff --git a/pkg/cmd/remoteaccess/configuration/create_telnet/create_telnet.auto.go b/pkg/cmd/remoteaccess/configuration/create_telnet/create_telnet.auto.go new file mode 100644 index 000000000..017023263 --- /dev/null +++ b/pkg/cmd/remoteaccess/configuration/create_telnet/create_telnet.auto.go @@ -0,0 +1,185 @@ +// Code generated from specification version 1.0.0: DO NOT EDIT +package create_telnet + +import ( + "io" + "net/http" + + "github.com/MakeNowJust/heredoc/v2" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/c8yfetcher" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/subcommand" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmderrors" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmdutil" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/completion" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/flags" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/mapbuilder" + "github.com/reubenmiller/go-c8y/pkg/c8y" + "github.com/spf13/cobra" +) + +// CreateTelnetCmd command +type CreateTelnetCmd struct { + *subcommand.SubCommand + + factory *cmdutil.Factory +} + +// NewCreateTelnetCmd creates a command to Create telnet configuration +func NewCreateTelnetCmd(f *cmdutil.Factory) *CreateTelnetCmd { + ccmd := &CreateTelnetCmd{ + factory: f, + } + cmd := &cobra.Command{ + Use: "create-telnet", + Short: "Create telnet configuration", + Long: `Create telnet configuration +`, + Example: heredoc.Doc(` +$ c8y remoteaccess configurations create-telnet +Create a telnet configuration + `), + PreRunE: func(cmd *cobra.Command, args []string) error { + return f.CreateModeEnabled() + }, + RunE: ccmd.RunE, + } + + cmd.SilenceUsage = true + + cmd.Flags().StringSlice("device", []string{""}, "Device (accepts pipeline)") + cmd.Flags().String("name", "telnet", "Connection name") + cmd.Flags().String("hostname", "127.0.0.1", "Hostname") + cmd.Flags().Int("port", 23, "Port") + cmd.Flags().String("credentialsType", "NONE", "Credentials type") + cmd.Flags().String("protocol", "TELNET", "Protocol") + + completion.WithOptions( + cmd, + completion.WithDevice("device", func() (*c8y.Client, error) { return ccmd.factory.Client() }), + completion.WithValidateSet("protocol", "TELNET", "PASSTHROUGH", "SSH", "VNC"), + ) + + flags.WithOptions( + cmd, + flags.WithProcessingMode(), + + flags.WithExtendedPipelineSupport("device", "device", false, "deviceId", "source.id", "managedObject.id", "id"), + flags.WithPipelineAliases("device", "deviceId", "source.id", "managedObject.id", "id"), + ) + + // Required flags + + _ = cmd.Flags().MarkHidden("credentialsType") + + ccmd.SubCommand = subcommand.NewSubCommand(cmd) + + return ccmd +} + +// RunE executes the command +func (n *CreateTelnetCmd) RunE(cmd *cobra.Command, args []string) error { + cfg, err := n.factory.Config() + if err != nil { + return err + } + // Runtime flag options + flags.WithOptions( + cmd, + flags.WithRuntimePipelineProperty(), + ) + client, err := n.factory.Client() + if err != nil { + return err + } + inputIterators, err := cmdutil.NewRequestInputIterators(cmd, cfg) + if err != nil { + return err + } + + // query parameters + query := flags.NewQueryTemplate() + err = flags.WithQueryParameters( + cmd, + query, + inputIterators, + flags.WithCustomStringSlice(func() ([]string, error) { return cfg.GetQueryParameters(), nil }, "custom"), + ) + if err != nil { + return cmderrors.NewUserError(err) + } + + queryValue, err := query.GetQueryUnescape(true) + + if err != nil { + return cmderrors.NewSystemError("Invalid query parameter") + } + + // headers + headers := http.Header{} + err = flags.WithHeaders( + cmd, + headers, + inputIterators, + flags.WithCustomStringSlice(func() ([]string, error) { return cfg.GetHeader(), nil }, "header"), + flags.WithProcessingModeValue(), + ) + if err != nil { + return cmderrors.NewUserError(err) + } + + // form data + formData := make(map[string]io.Reader) + err = flags.WithFormDataOptions( + cmd, + formData, + inputIterators, + ) + if err != nil { + return cmderrors.NewUserError(err) + } + + // body + body := mapbuilder.NewInitializedMapBuilder(true) + err = flags.WithBody( + cmd, + body, + inputIterators, + flags.WithDataFlagValue(), + flags.WithStringValue("name", "name"), + flags.WithStringValue("hostname", "hostname"), + flags.WithIntValue("port", "port"), + flags.WithStringValue("credentialsType", "credentialsType"), + flags.WithStringValue("protocol", "protocol"), + cmdutil.WithTemplateValue(n.factory), + flags.WithTemplateVariablesValue(), + flags.WithRequiredProperties("name", "hostname", "port", "protocol", "credentialsType"), + ) + if err != nil { + return cmderrors.NewUserError(err) + } + + // path parameters + path := flags.NewStringTemplate("/service/remoteaccess/devices/{device}/configurations") + err = flags.WithPathParameters( + cmd, + path, + inputIterators, + c8yfetcher.WithDeviceByNameFirstMatch(n.factory, args, "device", "device"), + ) + if err != nil { + return err + } + + req := c8y.RequestOptions{ + Method: "POST", + Path: path.GetTemplate(), + Query: queryValue, + Body: body, + FormData: formData, + Header: headers, + IgnoreAccept: cfg.IgnoreAcceptHeader(), + DryRun: cfg.ShouldUseDryRun(cmd.CommandPath()), + } + + return n.factory.RunWithWorkers(client, cmd, &req, inputIterators) +} diff --git a/pkg/cmd/remoteaccess/configuration/create_vnc/create_vnc.auto.go b/pkg/cmd/remoteaccess/configuration/create_vnc/create_vnc.auto.go new file mode 100644 index 000000000..1e9737f31 --- /dev/null +++ b/pkg/cmd/remoteaccess/configuration/create_vnc/create_vnc.auto.go @@ -0,0 +1,188 @@ +// Code generated from specification version 1.0.0: DO NOT EDIT +package create_vnc + +import ( + "io" + "net/http" + + "github.com/MakeNowJust/heredoc/v2" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/c8yfetcher" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/subcommand" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmderrors" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmdutil" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/completion" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/flags" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/mapbuilder" + "github.com/reubenmiller/go-c8y/pkg/c8y" + "github.com/spf13/cobra" +) + +// CreateVncCmd command +type CreateVncCmd struct { + *subcommand.SubCommand + + factory *cmdutil.Factory +} + +// NewCreateVncCmd creates a command to Create vnc configuration +func NewCreateVncCmd(f *cmdutil.Factory) *CreateVncCmd { + ccmd := &CreateVncCmd{ + factory: f, + } + cmd := &cobra.Command{ + Use: "create-vnc", + Short: "Create vnc configuration", + Long: `Create vnc configuration +`, + Example: heredoc.Doc(` +$ c8y remoteaccess configurations create-vnc +Create a VNC configuration that does not require a password + +$ c8y remoteaccess configurations create-vnc --password 'asd08dcj23dsf{@#9}' +Create a VNC configuration that requires a password + `), + PreRunE: func(cmd *cobra.Command, args []string) error { + return f.CreateModeEnabled() + }, + RunE: ccmd.RunE, + } + + cmd.SilenceUsage = true + + cmd.Flags().StringSlice("device", []string{""}, "Device (accepts pipeline)") + cmd.Flags().String("name", "webssh", "Connection name") + cmd.Flags().String("hostname", "127.0.0.1", "Hostname") + cmd.Flags().Int("port", 5900, "Port") + cmd.Flags().String("password", "", "VNC Password") + cmd.Flags().String("protocol", "VNC", "Protocol") + + completion.WithOptions( + cmd, + completion.WithDevice("device", func() (*c8y.Client, error) { return ccmd.factory.Client() }), + completion.WithValidateSet("protocol", "PASSTHROUGH", "SSH", "VNC"), + ) + + flags.WithOptions( + cmd, + flags.WithProcessingMode(), + + flags.WithExtendedPipelineSupport("device", "device", false, "deviceId", "source.id", "managedObject.id", "id"), + flags.WithPipelineAliases("device", "deviceId", "source.id", "managedObject.id", "id"), + ) + + // Required flags + + ccmd.SubCommand = subcommand.NewSubCommand(cmd) + + return ccmd +} + +// RunE executes the command +func (n *CreateVncCmd) RunE(cmd *cobra.Command, args []string) error { + cfg, err := n.factory.Config() + if err != nil { + return err + } + // Runtime flag options + flags.WithOptions( + cmd, + flags.WithRuntimePipelineProperty(), + ) + client, err := n.factory.Client() + if err != nil { + return err + } + inputIterators, err := cmdutil.NewRequestInputIterators(cmd, cfg) + if err != nil { + return err + } + + // query parameters + query := flags.NewQueryTemplate() + err = flags.WithQueryParameters( + cmd, + query, + inputIterators, + flags.WithCustomStringSlice(func() ([]string, error) { return cfg.GetQueryParameters(), nil }, "custom"), + ) + if err != nil { + return cmderrors.NewUserError(err) + } + + queryValue, err := query.GetQueryUnescape(true) + + if err != nil { + return cmderrors.NewSystemError("Invalid query parameter") + } + + // headers + headers := http.Header{} + err = flags.WithHeaders( + cmd, + headers, + inputIterators, + flags.WithCustomStringSlice(func() ([]string, error) { return cfg.GetHeader(), nil }, "header"), + flags.WithProcessingModeValue(), + ) + if err != nil { + return cmderrors.NewUserError(err) + } + + // form data + formData := make(map[string]io.Reader) + err = flags.WithFormDataOptions( + cmd, + formData, + inputIterators, + ) + if err != nil { + return cmderrors.NewUserError(err) + } + + // body + body := mapbuilder.NewInitializedMapBuilder(true) + err = flags.WithBody( + cmd, + body, + inputIterators, + flags.WithDataFlagValue(), + flags.WithStringValue("name", "name"), + flags.WithStringValue("hostname", "hostname"), + flags.WithIntValue("port", "port"), + flags.WithStringValue("password", "password"), + flags.WithStringValue("protocol", "protocol"), + flags.WithDefaultTemplateString(` +{credentialsType: if std.isEmpty(std.get($, 'password', '')) then 'NONE' else 'PASS_ONLY'}`), + cmdutil.WithTemplateValue(n.factory), + flags.WithTemplateVariablesValue(), + flags.WithRequiredProperties("name", "hostname", "port", "protocol", "credentialsType"), + ) + if err != nil { + return cmderrors.NewUserError(err) + } + + // path parameters + path := flags.NewStringTemplate("/service/remoteaccess/devices/{device}/configurations") + err = flags.WithPathParameters( + cmd, + path, + inputIterators, + c8yfetcher.WithDeviceByNameFirstMatch(n.factory, args, "device", "device"), + ) + if err != nil { + return err + } + + req := c8y.RequestOptions{ + Method: "POST", + Path: path.GetTemplate(), + Query: queryValue, + Body: body, + FormData: formData, + Header: headers, + IgnoreAccept: cfg.IgnoreAcceptHeader(), + DryRun: cfg.ShouldUseDryRun(cmd.CommandPath()), + } + + return n.factory.RunWithWorkers(client, cmd, &req, inputIterators) +} diff --git a/pkg/cmd/remoteaccess/configuration/create_webssh/create_webssh.auto.go b/pkg/cmd/remoteaccess/configuration/create_webssh/create_webssh.auto.go new file mode 100644 index 000000000..fd6871164 --- /dev/null +++ b/pkg/cmd/remoteaccess/configuration/create_webssh/create_webssh.auto.go @@ -0,0 +1,195 @@ +// Code generated from specification version 1.0.0: DO NOT EDIT +package create_webssh + +import ( + "io" + "net/http" + + "github.com/MakeNowJust/heredoc/v2" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/c8yfetcher" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/subcommand" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmderrors" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmdutil" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/completion" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/flags" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/mapbuilder" + "github.com/reubenmiller/go-c8y/pkg/c8y" + "github.com/spf13/cobra" +) + +// CreateWebsshCmd command +type CreateWebsshCmd struct { + *subcommand.SubCommand + + factory *cmdutil.Factory +} + +// NewCreateWebsshCmd creates a command to Create web ssh configuration +func NewCreateWebsshCmd(f *cmdutil.Factory) *CreateWebsshCmd { + ccmd := &CreateWebsshCmd{ + factory: f, + } + cmd := &cobra.Command{ + Use: "create-webssh", + Short: "Create web ssh configuration", + Long: `Create web ssh configuration +`, + Example: heredoc.Doc(` +$ c8y remoteaccess configurations create-webssh +Create a webssh configuration + +$ c8y remoteaccess configurations create-webssh --hostname 127.0.0.1 --port 2222 +Create a webssh configuration with a custom hostname and port + `), + PreRunE: func(cmd *cobra.Command, args []string) error { + return f.CreateModeEnabled() + }, + RunE: ccmd.RunE, + } + + cmd.SilenceUsage = true + + cmd.Flags().StringSlice("device", []string{""}, "Device (accepts pipeline)") + cmd.Flags().String("name", "webssh", "Connection name") + cmd.Flags().String("hostname", "", "Hostname") + cmd.Flags().Int("port", 0, "Port") + cmd.Flags().String("credentialsType", "USER_PASS", "Credentials type") + cmd.Flags().String("privateKey", "", "Private ssh key") + cmd.Flags().String("publicKey", "", "Public ssh key") + cmd.Flags().String("username", "", "Username") + cmd.Flags().String("password", "", "Username") + cmd.Flags().String("protocol", "SSH", "Protocol") + + completion.WithOptions( + cmd, + completion.WithDevice("device", func() (*c8y.Client, error) { return ccmd.factory.Client() }), + completion.WithValidateSet("credentialsType", "USER_PASS"), + completion.WithValidateSet("protocol", "PASSTHROUGH", "SSH"), + ) + + flags.WithOptions( + cmd, + flags.WithProcessingMode(), + + flags.WithExtendedPipelineSupport("device", "device", false, "deviceId", "source.id", "managedObject.id", "id"), + flags.WithPipelineAliases("device", "deviceId", "source.id", "managedObject.id", "id"), + ) + + // Required flags + + ccmd.SubCommand = subcommand.NewSubCommand(cmd) + + return ccmd +} + +// RunE executes the command +func (n *CreateWebsshCmd) RunE(cmd *cobra.Command, args []string) error { + cfg, err := n.factory.Config() + if err != nil { + return err + } + // Runtime flag options + flags.WithOptions( + cmd, + flags.WithRuntimePipelineProperty(), + ) + client, err := n.factory.Client() + if err != nil { + return err + } + inputIterators, err := cmdutil.NewRequestInputIterators(cmd, cfg) + if err != nil { + return err + } + + // query parameters + query := flags.NewQueryTemplate() + err = flags.WithQueryParameters( + cmd, + query, + inputIterators, + flags.WithCustomStringSlice(func() ([]string, error) { return cfg.GetQueryParameters(), nil }, "custom"), + ) + if err != nil { + return cmderrors.NewUserError(err) + } + + queryValue, err := query.GetQueryUnescape(true) + + if err != nil { + return cmderrors.NewSystemError("Invalid query parameter") + } + + // headers + headers := http.Header{} + err = flags.WithHeaders( + cmd, + headers, + inputIterators, + flags.WithCustomStringSlice(func() ([]string, error) { return cfg.GetHeader(), nil }, "header"), + flags.WithProcessingModeValue(), + ) + if err != nil { + return cmderrors.NewUserError(err) + } + + // form data + formData := make(map[string]io.Reader) + err = flags.WithFormDataOptions( + cmd, + formData, + inputIterators, + ) + if err != nil { + return cmderrors.NewUserError(err) + } + + // body + body := mapbuilder.NewInitializedMapBuilder(true) + err = flags.WithBody( + cmd, + body, + inputIterators, + flags.WithDataFlagValue(), + flags.WithStringValue("name", "name"), + flags.WithStringValue("hostname", "hostname"), + flags.WithIntValue("port", "port"), + flags.WithStringValue("credentialsType", "credentialsType"), + flags.WithStringValue("privateKey", "privateKey"), + flags.WithStringValue("publicKey", "publicKey"), + flags.WithStringValue("username", "username"), + flags.WithStringValue("password", "password"), + flags.WithStringValue("protocol", "protocol"), + cmdutil.WithTemplateValue(n.factory), + flags.WithTemplateVariablesValue(), + flags.WithRequiredProperties("hostname", "port", "protocol", "name"), + ) + if err != nil { + return cmderrors.NewUserError(err) + } + + // path parameters + path := flags.NewStringTemplate("/service/remoteaccess/devices/{device}/configurations") + err = flags.WithPathParameters( + cmd, + path, + inputIterators, + c8yfetcher.WithDeviceByNameFirstMatch(n.factory, args, "device", "device"), + ) + if err != nil { + return err + } + + req := c8y.RequestOptions{ + Method: "POST", + Path: path.GetTemplate(), + Query: queryValue, + Body: body, + FormData: formData, + Header: headers, + IgnoreAccept: cfg.IgnoreAcceptHeader(), + DryRun: cfg.ShouldUseDryRun(cmd.CommandPath()), + } + + return n.factory.RunWithWorkers(client, cmd, &req, inputIterators) +} diff --git a/pkg/cmd/remoteaccess/configuration/delete/delete.auto.go b/pkg/cmd/remoteaccess/configuration/delete/delete.auto.go new file mode 100644 index 000000000..81e62582d --- /dev/null +++ b/pkg/cmd/remoteaccess/configuration/delete/delete.auto.go @@ -0,0 +1,169 @@ +// Code generated from specification version 1.0.0: DO NOT EDIT +package delete + +import ( + "io" + "net/http" + + "github.com/MakeNowJust/heredoc/v2" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/c8yfetcher" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/subcommand" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmderrors" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmdutil" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/completion" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/flags" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/mapbuilder" + "github.com/reubenmiller/go-c8y/pkg/c8y" + "github.com/spf13/cobra" +) + +// DeleteCmd command +type DeleteCmd struct { + *subcommand.SubCommand + + factory *cmdutil.Factory +} + +// NewDeleteCmd creates a command to Delete remote access configuration +func NewDeleteCmd(f *cmdutil.Factory) *DeleteCmd { + ccmd := &DeleteCmd{ + factory: f, + } + cmd := &cobra.Command{ + Use: "delete", + Short: "Delete remote access configuration", + Long: ``, + Example: heredoc.Doc(` +$ c8y remoteaccess configurations delete --device mydevice --id 1 +Delete an existing remote access configuration + `), + PreRunE: func(cmd *cobra.Command, args []string) error { + return f.DeleteModeEnabled() + }, + RunE: ccmd.RunE, + } + + cmd.SilenceUsage = true + + cmd.Flags().StringSlice("device", []string{""}, "Device") + cmd.Flags().String("id", "", "Connection (accepts pipeline)") + + completion.WithOptions( + cmd, + completion.WithDevice("device", func() (*c8y.Client, error) { return ccmd.factory.Client() }), + ) + + flags.WithOptions( + cmd, + flags.WithProcessingMode(), + + flags.WithExtendedPipelineSupport("id", "id", false), + flags.WithPipelineAliases("device", "deviceId", "source.id", "managedObject.id", "id"), + ) + + // Required flags + + ccmd.SubCommand = subcommand.NewSubCommand(cmd) + + return ccmd +} + +// RunE executes the command +func (n *DeleteCmd) RunE(cmd *cobra.Command, args []string) error { + cfg, err := n.factory.Config() + if err != nil { + return err + } + // Runtime flag options + flags.WithOptions( + cmd, + flags.WithRuntimePipelineProperty(), + ) + client, err := n.factory.Client() + if err != nil { + return err + } + inputIterators, err := cmdutil.NewRequestInputIterators(cmd, cfg) + if err != nil { + return err + } + + // query parameters + query := flags.NewQueryTemplate() + err = flags.WithQueryParameters( + cmd, + query, + inputIterators, + flags.WithCustomStringSlice(func() ([]string, error) { return cfg.GetQueryParameters(), nil }, "custom"), + ) + if err != nil { + return cmderrors.NewUserError(err) + } + + queryValue, err := query.GetQueryUnescape(true) + + if err != nil { + return cmderrors.NewSystemError("Invalid query parameter") + } + + // headers + headers := http.Header{} + err = flags.WithHeaders( + cmd, + headers, + inputIterators, + flags.WithCustomStringSlice(func() ([]string, error) { return cfg.GetHeader(), nil }, "header"), + flags.WithProcessingModeValue(), + ) + if err != nil { + return cmderrors.NewUserError(err) + } + + // form data + formData := make(map[string]io.Reader) + err = flags.WithFormDataOptions( + cmd, + formData, + inputIterators, + ) + if err != nil { + return cmderrors.NewUserError(err) + } + + // body + body := mapbuilder.NewInitializedMapBuilder(false) + err = flags.WithBody( + cmd, + body, + inputIterators, + ) + if err != nil { + return cmderrors.NewUserError(err) + } + + // path parameters + path := flags.NewStringTemplate("/service/remoteaccess/devices/{device}/configurations/{id}") + err = flags.WithPathParameters( + cmd, + path, + inputIterators, + c8yfetcher.WithDeviceByNameFirstMatch(n.factory, args, "device", "device"), + flags.WithStringValue("id", "id"), + ) + if err != nil { + return err + } + + req := c8y.RequestOptions{ + Method: "DELETE", + Path: path.GetTemplate(), + Query: queryValue, + Body: body, + FormData: formData, + Header: headers, + IgnoreAccept: cfg.IgnoreAcceptHeader(), + DryRun: cfg.ShouldUseDryRun(cmd.CommandPath()), + } + + return n.factory.RunWithWorkers(client, cmd, &req, inputIterators) +} diff --git a/pkg/cmd/remoteaccess/configuration/get/get.auto.go b/pkg/cmd/remoteaccess/configuration/get/get.auto.go new file mode 100644 index 000000000..64870f0bd --- /dev/null +++ b/pkg/cmd/remoteaccess/configuration/get/get.auto.go @@ -0,0 +1,173 @@ +// Code generated from specification version 1.0.0: DO NOT EDIT +package get + +import ( + "fmt" + "io" + "net/http" + + "github.com/MakeNowJust/heredoc/v2" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/c8yfetcher" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/subcommand" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmderrors" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmdutil" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/completion" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/flags" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/mapbuilder" + "github.com/reubenmiller/go-c8y/pkg/c8y" + "github.com/spf13/cobra" +) + +// GetCmd command +type GetCmd struct { + *subcommand.SubCommand + + factory *cmdutil.Factory +} + +// NewGetCmd creates a command to Get remote access configuration +func NewGetCmd(f *cmdutil.Factory) *GetCmd { + ccmd := &GetCmd{ + factory: f, + } + cmd := &cobra.Command{ + Use: "get", + Short: "Get remote access configuration", + Long: `Get an existing remote access configuration for a device`, + Example: heredoc.Doc(` +$ c8y remoteaccess configurations get --device mydevice --id 1 +Get existing remote access configuration + `), + PreRunE: func(cmd *cobra.Command, args []string) error { + return nil + }, + RunE: ccmd.RunE, + } + + cmd.SilenceUsage = true + + cmd.Flags().StringSlice("device", []string{""}, "Device") + cmd.Flags().String("id", "", "Connection (accepts pipeline)") + + completion.WithOptions( + cmd, + completion.WithDevice("device", func() (*c8y.Client, error) { return ccmd.factory.Client() }), + ) + + flags.WithOptions( + cmd, + + flags.WithExtendedPipelineSupport("id", "id", false), + flags.WithPipelineAliases("device", "deviceId", "source.id", "managedObject.id", "id"), + ) + + // Required flags + + ccmd.SubCommand = subcommand.NewSubCommand(cmd) + + return ccmd +} + +// RunE executes the command +func (n *GetCmd) RunE(cmd *cobra.Command, args []string) error { + cfg, err := n.factory.Config() + if err != nil { + return err + } + // Runtime flag options + flags.WithOptions( + cmd, + flags.WithRuntimePipelineProperty(), + ) + client, err := n.factory.Client() + if err != nil { + return err + } + inputIterators, err := cmdutil.NewRequestInputIterators(cmd, cfg) + if err != nil { + return err + } + + // query parameters + query := flags.NewQueryTemplate() + err = flags.WithQueryParameters( + cmd, + query, + inputIterators, + flags.WithCustomStringSlice(func() ([]string, error) { return cfg.GetQueryParameters(), nil }, "custom"), + ) + if err != nil { + return cmderrors.NewUserError(err) + } + commonOptions, err := cfg.GetOutputCommonOptions(cmd) + if err != nil { + return cmderrors.NewUserError(fmt.Sprintf("Failed to get common options. err=%s", err)) + } + commonOptions.AddQueryParameters(query) + + queryValue, err := query.GetQueryUnescape(true) + + if err != nil { + return cmderrors.NewSystemError("Invalid query parameter") + } + + // headers + headers := http.Header{} + err = flags.WithHeaders( + cmd, + headers, + inputIterators, + flags.WithCustomStringSlice(func() ([]string, error) { return cfg.GetHeader(), nil }, "header"), + ) + if err != nil { + return cmderrors.NewUserError(err) + } + + // form data + formData := make(map[string]io.Reader) + err = flags.WithFormDataOptions( + cmd, + formData, + inputIterators, + ) + if err != nil { + return cmderrors.NewUserError(err) + } + + // body + body := mapbuilder.NewInitializedMapBuilder(false) + err = flags.WithBody( + cmd, + body, + inputIterators, + ) + if err != nil { + return cmderrors.NewUserError(err) + } + + // path parameters + path := flags.NewStringTemplate("/service/remoteaccess/devices/{device}/configurations/{id}") + err = flags.WithPathParameters( + cmd, + path, + inputIterators, + c8yfetcher.WithDeviceByNameFirstMatch(n.factory, args, "device", "device"), + flags.WithStringValue("id", "id"), + ) + if err != nil { + return err + } + + req := c8y.RequestOptions{ + Method: "GET", + Path: path.GetTemplate(), + Query: queryValue, + Body: body, + FormData: formData, + Header: headers, + IgnoreAccept: cfg.IgnoreAcceptHeader(), + DryRun: cfg.ShouldUseDryRun(cmd.CommandPath()), + } + + return n.factory.RunWithWorkers(client, cmd, &req, inputIterators) +} diff --git a/pkg/cmd/remoteaccess/configuration/list/list.auto.go b/pkg/cmd/remoteaccess/configuration/list/list.auto.go new file mode 100644 index 000000000..0fc943ddb --- /dev/null +++ b/pkg/cmd/remoteaccess/configuration/list/list.auto.go @@ -0,0 +1,172 @@ +// Code generated from specification version 1.0.0: DO NOT EDIT +package list + +import ( + "fmt" + "io" + "net/http" + + "github.com/MakeNowJust/heredoc/v2" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/c8yfetcher" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/subcommand" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmderrors" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmdutil" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/completion" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/flags" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/mapbuilder" + "github.com/reubenmiller/go-c8y/pkg/c8y" + "github.com/spf13/cobra" +) + +// ListCmd command +type ListCmd struct { + *subcommand.SubCommand + + factory *cmdutil.Factory +} + +// NewListCmd creates a command to List remote access configurations +func NewListCmd(f *cmdutil.Factory) *ListCmd { + ccmd := &ListCmd{ + factory: f, + } + cmd := &cobra.Command{ + Use: "list", + Short: "List remote access configurations", + Long: `List the remote access configurations already configured for the device +`, + Example: heredoc.Doc(` +$ c8y remoteaccess configurations list --device mydevice +List remote access configurations for a given device + `), + PreRunE: func(cmd *cobra.Command, args []string) error { + return nil + }, + RunE: ccmd.RunE, + } + + cmd.SilenceUsage = true + + cmd.Flags().StringSlice("device", []string{""}, "Device (accepts pipeline)") + + completion.WithOptions( + cmd, + completion.WithDevice("device", func() (*c8y.Client, error) { return ccmd.factory.Client() }), + ) + + flags.WithOptions( + cmd, + + flags.WithExtendedPipelineSupport("device", "device", false, "deviceId", "source.id", "managedObject.id", "id"), + flags.WithPipelineAliases("device", "deviceId", "source.id", "managedObject.id", "id"), + ) + + // Required flags + + ccmd.SubCommand = subcommand.NewSubCommand(cmd) + + return ccmd +} + +// RunE executes the command +func (n *ListCmd) RunE(cmd *cobra.Command, args []string) error { + cfg, err := n.factory.Config() + if err != nil { + return err + } + // Runtime flag options + flags.WithOptions( + cmd, + flags.WithRuntimePipelineProperty(), + ) + client, err := n.factory.Client() + if err != nil { + return err + } + inputIterators, err := cmdutil.NewRequestInputIterators(cmd, cfg) + if err != nil { + return err + } + + // query parameters + query := flags.NewQueryTemplate() + err = flags.WithQueryParameters( + cmd, + query, + inputIterators, + flags.WithCustomStringSlice(func() ([]string, error) { return cfg.GetQueryParameters(), nil }, "custom"), + ) + if err != nil { + return cmderrors.NewUserError(err) + } + commonOptions, err := cfg.GetOutputCommonOptions(cmd) + if err != nil { + return cmderrors.NewUserError(fmt.Sprintf("Failed to get common options. err=%s", err)) + } + commonOptions.AddQueryParameters(query) + + queryValue, err := query.GetQueryUnescape(true) + + if err != nil { + return cmderrors.NewSystemError("Invalid query parameter") + } + + // headers + headers := http.Header{} + err = flags.WithHeaders( + cmd, + headers, + inputIterators, + flags.WithCustomStringSlice(func() ([]string, error) { return cfg.GetHeader(), nil }, "header"), + ) + if err != nil { + return cmderrors.NewUserError(err) + } + + // form data + formData := make(map[string]io.Reader) + err = flags.WithFormDataOptions( + cmd, + formData, + inputIterators, + ) + if err != nil { + return cmderrors.NewUserError(err) + } + + // body + body := mapbuilder.NewInitializedMapBuilder(false) + err = flags.WithBody( + cmd, + body, + inputIterators, + ) + if err != nil { + return cmderrors.NewUserError(err) + } + + // path parameters + path := flags.NewStringTemplate("/service/remoteaccess/devices/{device}/configurations") + err = flags.WithPathParameters( + cmd, + path, + inputIterators, + c8yfetcher.WithDeviceByNameFirstMatch(n.factory, args, "device", "device"), + ) + if err != nil { + return err + } + + req := c8y.RequestOptions{ + Method: "GET", + Path: path.GetTemplate(), + Query: queryValue, + Body: body, + FormData: formData, + Header: headers, + IgnoreAccept: cfg.IgnoreAcceptHeader(), + DryRun: cfg.ShouldUseDryRun(cmd.CommandPath()), + } + + return n.factory.RunWithWorkers(client, cmd, &req, inputIterators) +} diff --git a/pkg/cmd/remoteaccess/configuration/update/update.auto.go b/pkg/cmd/remoteaccess/configuration/update/update.auto.go new file mode 100644 index 000000000..e0ae04d43 --- /dev/null +++ b/pkg/cmd/remoteaccess/configuration/update/update.auto.go @@ -0,0 +1,177 @@ +// Code generated from specification version 1.0.0: DO NOT EDIT +package update + +import ( + "io" + "net/http" + + "github.com/MakeNowJust/heredoc/v2" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/c8yfetcher" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/subcommand" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmderrors" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmdutil" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/completion" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/flags" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/mapbuilder" + "github.com/reubenmiller/go-c8y/pkg/c8y" + "github.com/spf13/cobra" +) + +// UpdateCmd command +type UpdateCmd struct { + *subcommand.SubCommand + + factory *cmdutil.Factory +} + +// NewUpdateCmd creates a command to Update remote access configuration +func NewUpdateCmd(f *cmdutil.Factory) *UpdateCmd { + ccmd := &UpdateCmd{ + factory: f, + } + cmd := &cobra.Command{ + Use: "update", + Short: "Update remote access configuration", + Long: `Update an existing remote access configuration`, + Hidden: true, + + Example: heredoc.Doc(` +$ c8y remoteaccess configurations update --device mydevice --id 1 +Update an existing remote access configuration + `), + PreRunE: func(cmd *cobra.Command, args []string) error { + return f.UpdateModeEnabled() + }, + RunE: ccmd.RunE, + } + + cmd.SilenceUsage = true + + cmd.Flags().StringSlice("device", []string{""}, "Device") + cmd.Flags().String("id", "", "Connection (accepts pipeline)") + cmd.Flags().String("name", "", "Profile name (required)") + + completion.WithOptions( + cmd, + completion.WithDevice("device", func() (*c8y.Client, error) { return ccmd.factory.Client() }), + ) + + flags.WithOptions( + cmd, + flags.WithProcessingMode(), + + flags.WithExtendedPipelineSupport("id", "id", false), + flags.WithPipelineAliases("device", "deviceId", "source.id", "managedObject.id", "id"), + ) + + // Required flags + _ = cmd.MarkFlagRequired("name") + + ccmd.SubCommand = subcommand.NewSubCommand(cmd) + + return ccmd +} + +// RunE executes the command +func (n *UpdateCmd) RunE(cmd *cobra.Command, args []string) error { + cfg, err := n.factory.Config() + if err != nil { + return err + } + // Runtime flag options + flags.WithOptions( + cmd, + flags.WithRuntimePipelineProperty(), + ) + client, err := n.factory.Client() + if err != nil { + return err + } + inputIterators, err := cmdutil.NewRequestInputIterators(cmd, cfg) + if err != nil { + return err + } + + // query parameters + query := flags.NewQueryTemplate() + err = flags.WithQueryParameters( + cmd, + query, + inputIterators, + flags.WithCustomStringSlice(func() ([]string, error) { return cfg.GetQueryParameters(), nil }, "custom"), + ) + if err != nil { + return cmderrors.NewUserError(err) + } + + queryValue, err := query.GetQueryUnescape(true) + + if err != nil { + return cmderrors.NewSystemError("Invalid query parameter") + } + + // headers + headers := http.Header{} + err = flags.WithHeaders( + cmd, + headers, + inputIterators, + flags.WithCustomStringSlice(func() ([]string, error) { return cfg.GetHeader(), nil }, "header"), + flags.WithProcessingModeValue(), + ) + if err != nil { + return cmderrors.NewUserError(err) + } + + // form data + formData := make(map[string]io.Reader) + err = flags.WithFormDataOptions( + cmd, + formData, + inputIterators, + ) + if err != nil { + return cmderrors.NewUserError(err) + } + + // body + body := mapbuilder.NewInitializedMapBuilder(true) + err = flags.WithBody( + cmd, + body, + inputIterators, + flags.WithDataFlagValue(), + flags.WithStringValue("name", "name"), + cmdutil.WithTemplateValue(n.factory), + flags.WithTemplateVariablesValue(), + ) + if err != nil { + return cmderrors.NewUserError(err) + } + + // path parameters + path := flags.NewStringTemplate("/service/remoteaccess/devices/{device}/configurations/{id}") + err = flags.WithPathParameters( + cmd, + path, + inputIterators, + c8yfetcher.WithDeviceByNameFirstMatch(n.factory, args, "device", "device"), + flags.WithStringValue("id", "id"), + ) + if err != nil { + return err + } + + req := c8y.RequestOptions{ + Method: "PUT", + Path: path.GetTemplate(), + Query: queryValue, + Body: body, + FormData: formData, + Header: headers, + IgnoreAccept: cfg.IgnoreAcceptHeader(), + DryRun: cfg.ShouldUseDryRun(cmd.CommandPath()), + } + + return n.factory.RunWithWorkers(client, cmd, &req, inputIterators) +} diff --git a/pkg/cmd/remoteaccess/remoteaccess.manual.go b/pkg/cmd/remoteaccess/remoteaccess.manual.go index 81d0ebb0e..6f4618b90 100644 --- a/pkg/cmd/remoteaccess/remoteaccess.manual.go +++ b/pkg/cmd/remoteaccess/remoteaccess.manual.go @@ -1,6 +1,7 @@ package remoteaccess import ( + "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/remoteaccess/configuration" "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/remoteaccess/connect" "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/remoteaccess/server" "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/subcommand" @@ -24,6 +25,7 @@ func NewSubCommand(f *cmdutil.Factory) *SubCmdRemoteAccess { // Subcommands cmd.AddCommand(connect.NewSubCommand(f).GetCommand()) cmd.AddCommand(server.NewCmdServer(f).GetCommand()) + cmd.AddCommand(configuration.NewSubCommand(f).GetCommand()) ccmd.SubCommand = subcommand.NewSubCommand(cmd) diff --git a/scripts/build-cli/New-C8yApi.ps1 b/scripts/build-cli/New-C8yApi.ps1 index f87fdca06..ab8ca4b93 100644 --- a/scripts/build-cli/New-C8yApi.ps1 +++ b/scripts/build-cli/New-C8yApi.ps1 @@ -48,7 +48,7 @@ Write-Verbose ("Skipping [{0}]" -f $iSpec.name) continue } - $SubCommandPackageName = $iSpec.alias.go.ToLower() + $SubCommandPackageName = $iSpec.alias.go.ToLower() -replace "-", "_" $SubCommandOutput = Join-Path -Path $CommandOutput -ChildPath $SubCommandPackageName $null = New-Item -Path $SubCommandOutput -ItemType Directory -Force Write-Host ("Generating subcommand: {0}" -f $SubCommandOutput) -ForegroundColor Magenta diff --git a/scripts/build-cli/New-C8yApiGoCommand.ps1 b/scripts/build-cli/New-C8yApiGoCommand.ps1 index 213f02315..9b8d48737 100644 --- a/scripts/build-cli/New-C8yApiGoCommand.ps1 +++ b/scripts/build-cli/New-C8yApiGoCommand.ps1 @@ -15,9 +15,10 @@ ) $Name = $Specification.alias.go - $NameCamel = $Name[0].ToString().ToUpperInvariant() + $Name.Substring(1) + $NameCamel = ($Name[0].ToString().ToUpperInvariant() + $Name.Substring(1)) -replace '-(\p{L})', { $_.Groups[1].Value.ToUpper() } + $PackageName = $Name.ToLower() -replace "-", "_" - $FileName = $Specification.alias.go + $FileName = $PackageName $File = Join-Path -Path $OutputDir -ChildPath ("{0}.auto.go" -f $FileName) @@ -550,7 +551,7 @@ # $Template = @" // Code generated from specification version 1.0.0: DO NOT EDIT -package $($Name.ToLower()) +package ${PackageName} import ( "fmt" diff --git a/scripts/build-cli/New-C8yApiGoRootCommand.ps1 b/scripts/build-cli/New-C8yApiGoRootCommand.ps1 index 980b8ce08..5b086a286 100644 --- a/scripts/build-cli/New-C8yApiGoRootCommand.ps1 +++ b/scripts/build-cli/New-C8yApiGoRootCommand.ps1 @@ -42,8 +42,8 @@ } $EndpointName = $endpoint.name $GoCmdName = $endpoint.alias.go - $GoCmdNameLower = $GoCmdName.ToLower() - $GoCmdNameCamel = $GoCmdName[0].ToString().ToUpperInvariant() + $GoCmdName.Substring(1) + $GoCmdNameLower = $GoCmdName.ToLower() -replace "-", "_" + $GoCmdNameCamel = ($GoCmdName[0].ToString().ToUpperInvariant() + $GoCmdName.Substring(1)) -replace '-(\p{L})', { $_.Groups[1].Value.ToUpper() } $ImportAlias = "cmd" + $GoCmdNameCamel $null = $GoImports.AppendLine("$ImportAlias `"github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/$Name/$GoCmdNameLower`"") diff --git a/tests/auto/remoteaccessconfiguration/tests/remoteaccess/configuration_create-passthrough.yaml b/tests/auto/remoteaccessconfiguration/tests/remoteaccess/configuration_create-passthrough.yaml new file mode 100644 index 000000000..39dbb2314 --- /dev/null +++ b/tests/auto/remoteaccessconfiguration/tests/remoteaccess/configuration_create-passthrough.yaml @@ -0,0 +1,20 @@ +tests: + remoteaccess/configuration_create-passthrough_Create a SSH passthrough configuration to the localhost: + command: | + c8y remoteaccess configurations create-passthrough --device mydevice + exit-code: 0 + stdout: + json: + method: POST + path: /service/remoteaccess/devices/mydevice/configurations + remoteaccess/configuration_create-passthrough_Create a SSH passthrough configuration with custom details: + command: | + c8y remoteaccess configurations create-passthrough --device mydevice --hostname customhost --port 1234 --name "My custom configuration" + exit-code: 0 + stdout: + json: + body.hostname: customhost + body.name: My custom configuration + body.port: "1234" + method: POST + path: /service/remoteaccess/devices/mydevice/configurations diff --git a/tests/auto/remoteaccessconfiguration/tests/remoteaccess/configuration_create-telnet.yaml b/tests/auto/remoteaccessconfiguration/tests/remoteaccess/configuration_create-telnet.yaml new file mode 100644 index 000000000..57d961458 --- /dev/null +++ b/tests/auto/remoteaccessconfiguration/tests/remoteaccess/configuration_create-telnet.yaml @@ -0,0 +1,9 @@ +tests: + remoteaccess/configuration_create-telnet_Create a telnet configuration: + command: | + c8y remoteaccess configurations create-telnet + exit-code: 0 + stdout: + json: + method: POST + path: /service/remoteaccess/devices/{device}/configurations diff --git a/tests/auto/remoteaccessconfiguration/tests/remoteaccess/configuration_create-vnc.yaml b/tests/auto/remoteaccessconfiguration/tests/remoteaccess/configuration_create-vnc.yaml new file mode 100644 index 000000000..0b151722e --- /dev/null +++ b/tests/auto/remoteaccessconfiguration/tests/remoteaccess/configuration_create-vnc.yaml @@ -0,0 +1,17 @@ +tests: + remoteaccess/configuration_create-vnc_Create a VNC configuration that does not require a password: + command: | + c8y remoteaccess configurations create-vnc + exit-code: 0 + stdout: + json: + method: POST + path: /service/remoteaccess/devices/{device}/configurations + remoteaccess/configuration_create-vnc_Create a VNC configuration that requires a password: + command: c8y remoteaccess configurations create-vnc --password 'asd08dcj23dsf{@#9}' + exit-code: 0 + stdout: + json: + body.password: asd08dcj23dsf{@#9} + method: POST + path: /service/remoteaccess/devices/{device}/configurations diff --git a/tests/auto/remoteaccessconfiguration/tests/remoteaccess/configuration_create-webssh.yaml b/tests/auto/remoteaccessconfiguration/tests/remoteaccess/configuration_create-webssh.yaml new file mode 100644 index 000000000..0ff8ce6a8 --- /dev/null +++ b/tests/auto/remoteaccessconfiguration/tests/remoteaccess/configuration_create-webssh.yaml @@ -0,0 +1,18 @@ +tests: + remoteaccess/configuration_create-webssh_Create a webssh configuration: + command: | + c8y remoteaccess configurations create-webssh + exit-code: 0 + stdout: + json: + method: POST + path: /service/remoteaccess/devices/{device}/configurations + remoteaccess/configuration_create-webssh_Create a webssh configuration with a custom hostname and port: + command: c8y remoteaccess configurations create-webssh --hostname 127.0.0.1 --port 2222 + exit-code: 0 + stdout: + json: + body.hostname: 127.0.0.1 + body.port: "2222" + method: POST + path: /service/remoteaccess/devices/{device}/configurations diff --git a/tests/auto/remoteaccessconfiguration/tests/remoteaccess/configuration_delete.yaml b/tests/auto/remoteaccessconfiguration/tests/remoteaccess/configuration_delete.yaml new file mode 100644 index 000000000..36f6060f6 --- /dev/null +++ b/tests/auto/remoteaccessconfiguration/tests/remoteaccess/configuration_delete.yaml @@ -0,0 +1,8 @@ +tests: + remoteaccess/configuration_delete_Delete an existing remote access configuration: + command: c8y remoteaccess configurations delete --device mydevice --id 1 + exit-code: 0 + stdout: + json: + method: DELETE + path: /service/remoteaccess/devices/mydevice/configurations/1 diff --git a/tests/auto/remoteaccessconfiguration/tests/remoteaccess/configuration_get.yaml b/tests/auto/remoteaccessconfiguration/tests/remoteaccess/configuration_get.yaml new file mode 100644 index 000000000..a25289506 --- /dev/null +++ b/tests/auto/remoteaccessconfiguration/tests/remoteaccess/configuration_get.yaml @@ -0,0 +1,8 @@ +tests: + remoteaccess/configuration_get_Get existing remote access configuration: + command: c8y remoteaccess configurations get --device mydevice --id 1 + exit-code: 0 + stdout: + json: + method: GET + path: /service/remoteaccess/devices/mydevice/configurations/1 diff --git a/tests/auto/remoteaccessconfiguration/tests/remoteaccess/configuration_list.yaml b/tests/auto/remoteaccessconfiguration/tests/remoteaccess/configuration_list.yaml new file mode 100644 index 000000000..ba7831c73 --- /dev/null +++ b/tests/auto/remoteaccessconfiguration/tests/remoteaccess/configuration_list.yaml @@ -0,0 +1,8 @@ +tests: + remoteaccess/configuration_list_List remote access configurations for a given device: + command: c8y remoteaccess configurations list --device mydevice + exit-code: 0 + stdout: + json: + method: GET + path: /service/remoteaccess/devices/mydevice/configurations diff --git a/tests/auto/remoteaccessconfiguration/tests/remoteaccess/configuration_update.yaml b/tests/auto/remoteaccessconfiguration/tests/remoteaccess/configuration_update.yaml new file mode 100644 index 000000000..18033df99 --- /dev/null +++ b/tests/auto/remoteaccessconfiguration/tests/remoteaccess/configuration_update.yaml @@ -0,0 +1,8 @@ +tests: + remoteaccess/configuration_update_Update an existing remote access configuration: + command: c8y remoteaccess configurations update --device mydevice --id 1 + exit-code: 0 + stdout: + json: + method: PUT + path: /service/remoteaccess/devices/mydevice/configurations/1 diff --git a/tools/PSc8y/PSc8y.psd1 b/tools/PSc8y/PSc8y.psd1 index a1339084b..58b3de5c6 100644 --- a/tools/PSc8y/PSc8y.psd1 +++ b/tools/PSc8y/PSc8y.psd1 @@ -188,6 +188,8 @@ FunctionsToExport = @( 'Get-Notification2SubscriptionCollection', 'Get-Operation', 'Get-OperationCollection', + 'Get-RemoteAccessConfiguration', + 'Get-RemoteAccessConfigurationCollection', 'Get-RetentionRule', 'Get-RetentionRuleCollection', 'Get-RoleCollection', @@ -255,6 +257,10 @@ FunctionsToExport = @( 'New-Notification2Subscription', 'New-Notification2Token', 'New-Operation', + 'New-RemoteAccessPassthroughConfiguration', + 'New-RemoteAccessTelnetConfiguration', + 'New-RemoteAccessVNCConfiguration', + 'New-RemoteAccessWebSSHConfiguration', 'New-RetentionRule', 'New-SmartGroup', 'New-Software', @@ -297,6 +303,7 @@ FunctionsToExport = @( 'Remove-Notification2Subscription', 'Remove-Notification2SubscriptionBySource', 'Remove-OperationCollection', + 'Remove-RemoteAccessConfiguration', 'Remove-RetentionRule', 'Remove-RoleFromGroup', 'Remove-RoleFromUser', diff --git a/tools/PSc8y/Public/Get-RemoteAccessConfiguration.ps1 b/tools/PSc8y/Public/Get-RemoteAccessConfiguration.ps1 new file mode 100644 index 000000000..0dc901e89 --- /dev/null +++ b/tools/PSc8y/Public/Get-RemoteAccessConfiguration.ps1 @@ -0,0 +1,73 @@ +# Code generated from specification version 1.0.0: DO NOT EDIT +Function Get-RemoteAccessConfiguration { +<# +.SYNOPSIS +Get remote access configuration + +.DESCRIPTION +Get an existing remote access configuration for a device + +.LINK +https://reubenmiller.github.io/go-c8y-cli/docs/cli/c8y/remoteaccess_configuration_get + +.EXAMPLE +PS> Get-RemoteAccessConfiguration -Device mydevice -Id 1 + +Get existing remote access configuration + + +#> + [cmdletbinding(PositionalBinding=$true, + HelpUri='')] + [Alias()] + [OutputType([object])] + Param( + # Device + [Parameter()] + [object[]] + $Device, + + # Connection + [Parameter(ValueFromPipeline=$true, + ValueFromPipelineByPropertyName=$true)] + [object[]] + $Id + ) + DynamicParam { + Get-ClientCommonParameters -Type "Get" + } + + Begin { + + if ($env:C8Y_DISABLE_INHERITANCE -ne $true) { + # Inherit preference variables + Use-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState + } + + $c8yargs = New-ClientArgument -Parameters $PSBoundParameters -Command "remoteaccess configuration get" + $ClientOptions = Get-ClientOutputOption $PSBoundParameters + $TypeOptions = @{ + Type = "" + ItemType = "" + BoundParameters = $PSBoundParameters + } + } + + Process { + + if ($ClientOptions.ConvertToPS) { + $Id ` + | Group-ClientRequests ` + | c8y remoteaccess configuration get $c8yargs ` + | ConvertFrom-ClientOutput @TypeOptions + } + else { + $Id ` + | Group-ClientRequests ` + | c8y remoteaccess configuration get $c8yargs + } + + } + + End {} +} diff --git a/tools/PSc8y/Public/Get-RemoteAccessConfigurationCollection.ps1 b/tools/PSc8y/Public/Get-RemoteAccessConfigurationCollection.ps1 new file mode 100644 index 000000000..284a34897 --- /dev/null +++ b/tools/PSc8y/Public/Get-RemoteAccessConfigurationCollection.ps1 @@ -0,0 +1,69 @@ +# Code generated from specification version 1.0.0: DO NOT EDIT +Function Get-RemoteAccessConfigurationCollection { +<# +.SYNOPSIS +List remote access configurations + +.DESCRIPTION +List the remote access configurations already configured for the device + + +.LINK +https://reubenmiller.github.io/go-c8y-cli/docs/cli/c8y/remoteaccess_configuration_list + +.EXAMPLE +PS> Get-RemoteAccessConfigurationCollection -Device mydevice + +List remote access configurations for a given device + + +#> + [cmdletbinding(PositionalBinding=$true, + HelpUri='')] + [Alias()] + [OutputType([object])] + Param( + # Device + [Parameter(ValueFromPipeline=$true, + ValueFromPipelineByPropertyName=$true)] + [object[]] + $Device + ) + DynamicParam { + Get-ClientCommonParameters -Type "Get" + } + + Begin { + + if ($env:C8Y_DISABLE_INHERITANCE -ne $true) { + # Inherit preference variables + Use-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState + } + + $c8yargs = New-ClientArgument -Parameters $PSBoundParameters -Command "remoteaccess configuration list" + $ClientOptions = Get-ClientOutputOption $PSBoundParameters + $TypeOptions = @{ + Type = "" + ItemType = "" + BoundParameters = $PSBoundParameters + } + } + + Process { + + if ($ClientOptions.ConvertToPS) { + $Device ` + | Group-ClientRequests ` + | c8y remoteaccess configuration list $c8yargs ` + | ConvertFrom-ClientOutput @TypeOptions + } + else { + $Device ` + | Group-ClientRequests ` + | c8y remoteaccess configuration list $c8yargs + } + + } + + End {} +} diff --git a/tools/PSc8y/Public/New-RemoteAccessPassthroughConfiguration.ps1 b/tools/PSc8y/Public/New-RemoteAccessPassthroughConfiguration.ps1 new file mode 100644 index 000000000..0180c8080 --- /dev/null +++ b/tools/PSc8y/Public/New-RemoteAccessPassthroughConfiguration.ps1 @@ -0,0 +1,97 @@ +# Code generated from specification version 1.0.0: DO NOT EDIT +Function New-RemoteAccessPassthroughConfiguration { +<# +.SYNOPSIS +Create passthrough configuration + +.DESCRIPTION +Create passthrough configuration + + +.LINK +https://reubenmiller.github.io/go-c8y-cli/docs/cli/c8y/remoteaccess_configuration_create-passthrough + +.EXAMPLE +PS> New-RemoteAccessPassthroughConfiguration -Device mydevice + + +Create a SSH passthrough configuration to the localhost + +.EXAMPLE +PS> New-RemoteAccessPassthroughConfiguration -Device mydevice -Hostname customhost -Port 1234 -Name "My custom configuration" + + +Create a SSH passthrough configuration with custom details + + +#> + [cmdletbinding(PositionalBinding=$true, + HelpUri='')] + [Alias()] + [OutputType([object])] + Param( + # Device + [Parameter(ValueFromPipeline=$true, + ValueFromPipelineByPropertyName=$true)] + [object[]] + $Device, + + # Connection name + [Parameter()] + [string] + $Name, + + # Hostname + [Parameter()] + [string] + $Hostname, + + # Port + [Parameter()] + [long] + $Port, + + # Protocol + [Parameter()] + [ValidateSet('PASSTHROUGH')] + [string] + $Protocol + ) + DynamicParam { + Get-ClientCommonParameters -Type "Create", "Template" + } + + Begin { + + if ($env:C8Y_DISABLE_INHERITANCE -ne $true) { + # Inherit preference variables + Use-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState + } + + $c8yargs = New-ClientArgument -Parameters $PSBoundParameters -Command "remoteaccess configuration create-passthrough" + $ClientOptions = Get-ClientOutputOption $PSBoundParameters + $TypeOptions = @{ + Type = "" + ItemType = "" + BoundParameters = $PSBoundParameters + } + } + + Process { + + if ($ClientOptions.ConvertToPS) { + $Device ` + | Group-ClientRequests ` + | c8y remoteaccess configuration create-passthrough $c8yargs ` + | ConvertFrom-ClientOutput @TypeOptions + } + else { + $Device ` + | Group-ClientRequests ` + | c8y remoteaccess configuration create-passthrough $c8yargs + } + + } + + End {} +} diff --git a/tools/PSc8y/Public/New-RemoteAccessTelnetConfiguration.ps1 b/tools/PSc8y/Public/New-RemoteAccessTelnetConfiguration.ps1 new file mode 100644 index 000000000..a060d4207 --- /dev/null +++ b/tools/PSc8y/Public/New-RemoteAccessTelnetConfiguration.ps1 @@ -0,0 +1,96 @@ +# Code generated from specification version 1.0.0: DO NOT EDIT +Function New-RemoteAccessTelnetConfiguration { +<# +.SYNOPSIS +Create telnet configuration + +.DESCRIPTION +Create telnet configuration + + +.LINK +https://reubenmiller.github.io/go-c8y-cli/docs/cli/c8y/remoteaccess_configuration_create-telnet + +.EXAMPLE +PS> New-RemoteAccessTelnetConfiguration + + +Create a telnet configuration + + +#> + [cmdletbinding(PositionalBinding=$true, + HelpUri='')] + [Alias()] + [OutputType([object])] + Param( + # Device + [Parameter(ValueFromPipeline=$true, + ValueFromPipelineByPropertyName=$true)] + [object[]] + $Device, + + # Connection name + [Parameter()] + [string] + $Name, + + # Hostname + [Parameter()] + [string] + $Hostname, + + # Port + [Parameter()] + [long] + $Port, + + # Credentials type + [Parameter()] + [string] + $CredentialsType, + + # Protocol + [Parameter()] + [ValidateSet('TELNET','PASSTHROUGH','SSH','VNC')] + [string] + $Protocol + ) + DynamicParam { + Get-ClientCommonParameters -Type "Create", "Template" + } + + Begin { + + if ($env:C8Y_DISABLE_INHERITANCE -ne $true) { + # Inherit preference variables + Use-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState + } + + $c8yargs = New-ClientArgument -Parameters $PSBoundParameters -Command "remoteaccess configuration create-telnet" + $ClientOptions = Get-ClientOutputOption $PSBoundParameters + $TypeOptions = @{ + Type = "" + ItemType = "" + BoundParameters = $PSBoundParameters + } + } + + Process { + + if ($ClientOptions.ConvertToPS) { + $Device ` + | Group-ClientRequests ` + | c8y remoteaccess configuration create-telnet $c8yargs ` + | ConvertFrom-ClientOutput @TypeOptions + } + else { + $Device ` + | Group-ClientRequests ` + | c8y remoteaccess configuration create-telnet $c8yargs + } + + } + + End {} +} diff --git a/tools/PSc8y/Public/New-RemoteAccessVNCConfiguration.ps1 b/tools/PSc8y/Public/New-RemoteAccessVNCConfiguration.ps1 new file mode 100644 index 000000000..4885841f5 --- /dev/null +++ b/tools/PSc8y/Public/New-RemoteAccessVNCConfiguration.ps1 @@ -0,0 +1,101 @@ +# Code generated from specification version 1.0.0: DO NOT EDIT +Function New-RemoteAccessVNCConfiguration { +<# +.SYNOPSIS +Create vnc configuration + +.DESCRIPTION +Create vnc configuration + + +.LINK +https://reubenmiller.github.io/go-c8y-cli/docs/cli/c8y/remoteaccess_configuration_create-vnc + +.EXAMPLE +PS> New-RemoteAccessVNCConfiguration + + +Create a VNC configuration that does not require a password + +.EXAMPLE +PS> New-RemoteAccessVNCConfiguration -Password 'asd08dcj23dsf{@#9}' + +Create a VNC configuration that requires a password + + +#> + [cmdletbinding(PositionalBinding=$true, + HelpUri='')] + [Alias()] + [OutputType([object])] + Param( + # Device + [Parameter(ValueFromPipeline=$true, + ValueFromPipelineByPropertyName=$true)] + [object[]] + $Device, + + # Connection name + [Parameter()] + [string] + $Name, + + # Hostname + [Parameter()] + [string] + $Hostname, + + # Port + [Parameter()] + [long] + $Port, + + # VNC Password + [Parameter()] + [string] + $Password, + + # Protocol + [Parameter()] + [ValidateSet('PASSTHROUGH','SSH','VNC')] + [string] + $Protocol + ) + DynamicParam { + Get-ClientCommonParameters -Type "Create", "Template" + } + + Begin { + + if ($env:C8Y_DISABLE_INHERITANCE -ne $true) { + # Inherit preference variables + Use-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState + } + + $c8yargs = New-ClientArgument -Parameters $PSBoundParameters -Command "remoteaccess configuration create-vnc" + $ClientOptions = Get-ClientOutputOption $PSBoundParameters + $TypeOptions = @{ + Type = "" + ItemType = "" + BoundParameters = $PSBoundParameters + } + } + + Process { + + if ($ClientOptions.ConvertToPS) { + $Device ` + | Group-ClientRequests ` + | c8y remoteaccess configuration create-vnc $c8yargs ` + | ConvertFrom-ClientOutput @TypeOptions + } + else { + $Device ` + | Group-ClientRequests ` + | c8y remoteaccess configuration create-vnc $c8yargs + } + + } + + End {} +} diff --git a/tools/PSc8y/Public/New-RemoteAccessWebSSHConfiguration.ps1 b/tools/PSc8y/Public/New-RemoteAccessWebSSHConfiguration.ps1 new file mode 100644 index 000000000..8f526e82c --- /dev/null +++ b/tools/PSc8y/Public/New-RemoteAccessWebSSHConfiguration.ps1 @@ -0,0 +1,122 @@ +# Code generated from specification version 1.0.0: DO NOT EDIT +Function New-RemoteAccessWebSSHConfiguration { +<# +.SYNOPSIS +Create web ssh configuration + +.DESCRIPTION +Create web ssh configuration + + +.LINK +https://reubenmiller.github.io/go-c8y-cli/docs/cli/c8y/remoteaccess_configuration_create-webssh + +.EXAMPLE +PS> New-RemoteAccessWebSSHConfiguration + + +Create a webssh configuration + +.EXAMPLE +PS> New-RemoteAccessWebSSHConfiguration -Hostname 127.0.0.1 -Port 2222 + +Create a webssh configuration with a custom hostname and port + + +#> + [cmdletbinding(PositionalBinding=$true, + HelpUri='')] + [Alias()] + [OutputType([object])] + Param( + # Device + [Parameter(ValueFromPipeline=$true, + ValueFromPipelineByPropertyName=$true)] + [object[]] + $Device, + + # Connection name + [Parameter()] + [string] + $Name, + + # Hostname + [Parameter()] + [string] + $Hostname, + + # Port + [Parameter()] + [long] + $Port, + + # Credentials type + [Parameter()] + [ValidateSet('USER_PASS')] + [string] + $CredentialsType, + + # Private ssh key + [Parameter()] + [string] + $PrivateKey, + + # Public ssh key + [Parameter()] + [string] + $PublicKey, + + # Username + [Parameter()] + [string] + $Username, + + # Username + [Parameter()] + [string] + $Password, + + # Protocol + [Parameter()] + [ValidateSet('PASSTHROUGH','SSH')] + [string] + $Protocol + ) + DynamicParam { + Get-ClientCommonParameters -Type "Create", "Template" + } + + Begin { + + if ($env:C8Y_DISABLE_INHERITANCE -ne $true) { + # Inherit preference variables + Use-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState + } + + $c8yargs = New-ClientArgument -Parameters $PSBoundParameters -Command "remoteaccess configuration create-webssh" + $ClientOptions = Get-ClientOutputOption $PSBoundParameters + $TypeOptions = @{ + Type = "" + ItemType = "" + BoundParameters = $PSBoundParameters + } + } + + Process { + + if ($ClientOptions.ConvertToPS) { + $Device ` + | Group-ClientRequests ` + | c8y remoteaccess configuration create-webssh $c8yargs ` + | ConvertFrom-ClientOutput @TypeOptions + } + else { + $Device ` + | Group-ClientRequests ` + | c8y remoteaccess configuration create-webssh $c8yargs + } + + } + + End {} +} diff --git a/tools/PSc8y/Public/Remove-RemoteAccessConfiguration.ps1 b/tools/PSc8y/Public/Remove-RemoteAccessConfiguration.ps1 new file mode 100644 index 000000000..56af72a9f --- /dev/null +++ b/tools/PSc8y/Public/Remove-RemoteAccessConfiguration.ps1 @@ -0,0 +1,73 @@ +# Code generated from specification version 1.0.0: DO NOT EDIT +Function Remove-RemoteAccessConfiguration { +<# +.SYNOPSIS +Delete remote access configuration + +.DESCRIPTION +Delete remote access configuration + +.LINK +https://reubenmiller.github.io/go-c8y-cli/docs/cli/c8y/remoteaccess_configuration_delete + +.EXAMPLE +PS> Remove-RemoteAccessConfiguration -Device mydevice -Id 1 + +Delete an existing remote access configuration + + +#> + [cmdletbinding(PositionalBinding=$true, + HelpUri='')] + [Alias()] + [OutputType([object])] + Param( + # Device + [Parameter()] + [object[]] + $Device, + + # Connection + [Parameter(ValueFromPipeline=$true, + ValueFromPipelineByPropertyName=$true)] + [object[]] + $Id + ) + DynamicParam { + Get-ClientCommonParameters -Type "Delete" + } + + Begin { + + if ($env:C8Y_DISABLE_INHERITANCE -ne $true) { + # Inherit preference variables + Use-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState + } + + $c8yargs = New-ClientArgument -Parameters $PSBoundParameters -Command "remoteaccess configuration delete" + $ClientOptions = Get-ClientOutputOption $PSBoundParameters + $TypeOptions = @{ + Type = "" + ItemType = "" + BoundParameters = $PSBoundParameters + } + } + + Process { + + if ($ClientOptions.ConvertToPS) { + $Id ` + | Group-ClientRequests ` + | c8y remoteaccess configuration delete $c8yargs ` + | ConvertFrom-ClientOutput @TypeOptions + } + else { + $Id ` + | Group-ClientRequests ` + | c8y remoteaccess configuration delete $c8yargs + } + + } + + End {} +} diff --git a/tools/PSc8y/Tests/Get-RemoteAccessConfiguration.auto.Tests.ps1 b/tools/PSc8y/Tests/Get-RemoteAccessConfiguration.auto.Tests.ps1 new file mode 100644 index 000000000..6e730f489 --- /dev/null +++ b/tools/PSc8y/Tests/Get-RemoteAccessConfiguration.auto.Tests.ps1 @@ -0,0 +1,18 @@ +. $PSScriptRoot/imports.ps1 + +Describe -Name "Get-RemoteAccessConfiguration" { + BeforeEach { + + } + + It "Get existing remote access configuration" { + $Response = PSc8y\Get-RemoteAccessConfiguration -Device mydevice -Id 1 + $LASTEXITCODE | Should -Be 0 + } + + + AfterEach { + + } +} + diff --git a/tools/PSc8y/Tests/Get-RemoteAccessConfigurationCollection.auto.Tests.ps1 b/tools/PSc8y/Tests/Get-RemoteAccessConfigurationCollection.auto.Tests.ps1 new file mode 100644 index 000000000..57645b216 --- /dev/null +++ b/tools/PSc8y/Tests/Get-RemoteAccessConfigurationCollection.auto.Tests.ps1 @@ -0,0 +1,18 @@ +. $PSScriptRoot/imports.ps1 + +Describe -Name "Get-RemoteAccessConfigurationCollection" { + BeforeEach { + + } + + It "List remote access configurations for a given device" { + $Response = PSc8y\Get-RemoteAccessConfigurationCollection -Device mydevice + $LASTEXITCODE | Should -Be 0 + } + + + AfterEach { + + } +} + diff --git a/tools/PSc8y/Tests/New-RemoteAccessPassthroughConfiguration.auto.Tests.ps1 b/tools/PSc8y/Tests/New-RemoteAccessPassthroughConfiguration.auto.Tests.ps1 new file mode 100644 index 000000000..011ca2dce --- /dev/null +++ b/tools/PSc8y/Tests/New-RemoteAccessPassthroughConfiguration.auto.Tests.ps1 @@ -0,0 +1,25 @@ +. $PSScriptRoot/imports.ps1 + +Describe -Name "New-RemoteAccessPassthroughConfiguration" { + BeforeEach { + + } + + It "Create a SSH passthrough configuration to the localhost" { + $Response = PSc8y\New-RemoteAccessPassthroughConfiguration -Device mydevice + + $LASTEXITCODE | Should -Be 0 + } + + It "Create a SSH passthrough configuration with custom details" { + $Response = PSc8y\New-RemoteAccessPassthroughConfiguration -Device mydevice -Hostname customhost -Port 1234 -Name "My custom configuration" + + $LASTEXITCODE | Should -Be 0 + } + + + AfterEach { + + } +} + diff --git a/tools/PSc8y/Tests/New-RemoteAccessTelnetConfiguration.auto.Tests.ps1 b/tools/PSc8y/Tests/New-RemoteAccessTelnetConfiguration.auto.Tests.ps1 new file mode 100644 index 000000000..d9368a988 --- /dev/null +++ b/tools/PSc8y/Tests/New-RemoteAccessTelnetConfiguration.auto.Tests.ps1 @@ -0,0 +1,19 @@ +. $PSScriptRoot/imports.ps1 + +Describe -Name "New-RemoteAccessTelnetConfiguration" { + BeforeEach { + + } + + It "Create a telnet configuration" { + $Response = PSc8y\New-RemoteAccessTelnetConfiguration + + $LASTEXITCODE | Should -Be 0 + } + + + AfterEach { + + } +} + diff --git a/tools/PSc8y/Tests/New-RemoteAccessVNCConfiguration.auto.Tests.ps1 b/tools/PSc8y/Tests/New-RemoteAccessVNCConfiguration.auto.Tests.ps1 new file mode 100644 index 000000000..91deb8e8e --- /dev/null +++ b/tools/PSc8y/Tests/New-RemoteAccessVNCConfiguration.auto.Tests.ps1 @@ -0,0 +1,24 @@ +. $PSScriptRoot/imports.ps1 + +Describe -Name "New-RemoteAccessVNCConfiguration" { + BeforeEach { + + } + + It "Create a VNC configuration that does not require a password" { + $Response = PSc8y\New-RemoteAccessVNCConfiguration + + $LASTEXITCODE | Should -Be 0 + } + + It "Create a VNC configuration that requires a password" { + $Response = PSc8y\New-RemoteAccessVNCConfiguration -Password 'asd08dcj23dsf{@#9}' + $LASTEXITCODE | Should -Be 0 + } + + + AfterEach { + + } +} + diff --git a/tools/PSc8y/Tests/New-RemoteAccessWebSSHConfiguration.auto.Tests.ps1 b/tools/PSc8y/Tests/New-RemoteAccessWebSSHConfiguration.auto.Tests.ps1 new file mode 100644 index 000000000..2a40a444b --- /dev/null +++ b/tools/PSc8y/Tests/New-RemoteAccessWebSSHConfiguration.auto.Tests.ps1 @@ -0,0 +1,24 @@ +. $PSScriptRoot/imports.ps1 + +Describe -Name "New-RemoteAccessWebSSHConfiguration" { + BeforeEach { + + } + + It "Create a webssh configuration" { + $Response = PSc8y\New-RemoteAccessWebSSHConfiguration + + $LASTEXITCODE | Should -Be 0 + } + + It "Create a webssh configuration with a custom hostname and port" { + $Response = PSc8y\New-RemoteAccessWebSSHConfiguration -Hostname 127.0.0.1 -Port 2222 + $LASTEXITCODE | Should -Be 0 + } + + + AfterEach { + + } +} + diff --git a/tools/PSc8y/Tests/Remove-RemoteAccessConfiguration.auto.Tests.ps1 b/tools/PSc8y/Tests/Remove-RemoteAccessConfiguration.auto.Tests.ps1 new file mode 100644 index 000000000..4f1c245fe --- /dev/null +++ b/tools/PSc8y/Tests/Remove-RemoteAccessConfiguration.auto.Tests.ps1 @@ -0,0 +1,18 @@ +. $PSScriptRoot/imports.ps1 + +Describe -Name "Remove-RemoteAccessConfiguration" { + BeforeEach { + + } + + It "Delete an existing remote access configuration" { + $Response = PSc8y\Remove-RemoteAccessConfiguration -Device mydevice -Id 1 + $LASTEXITCODE | Should -Be 0 + } + + + AfterEach { + + } +} + From bd5a9bd3e186d84c46f28193b2a0c55a6f88fba3 Mon Sep 17 00:00:00 2001 From: Reuben Miller Date: Wed, 15 May 2024 19:00:59 +0200 Subject: [PATCH 08/23] support reading device from pipeline --- pkg/cmd/remoteaccess/server/server.manual.go | 172 +++++++++++-------- 1 file changed, 97 insertions(+), 75 deletions(-) diff --git a/pkg/cmd/remoteaccess/server/server.manual.go b/pkg/cmd/remoteaccess/server/server.manual.go index cdc1da197..e77ff2132 100644 --- a/pkg/cmd/remoteaccess/server/server.manual.go +++ b/pkg/cmd/remoteaccess/server/server.manual.go @@ -11,7 +11,10 @@ import ( "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmdutil" "github.com/reubenmiller/go-c8y-cli/v2/pkg/completion" "github.com/reubenmiller/go-c8y-cli/v2/pkg/flags" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/iterator" "github.com/reubenmiller/go-c8y-cli/v2/pkg/mapbuilder" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/worker" + "github.com/reubenmiller/go-c8y/pkg/c8y" "github.com/reubenmiller/go-c8y/pkg/remoteaccess" "github.com/spf13/cobra" "github.com/tidwall/gjson" @@ -64,7 +67,12 @@ Start a local proxy and match on the configuration using wildcards, then open th completion.WithOptions( cmd, - completion.WithDevice("device", f.Client), + completion.WithDevice("device", func() (*c8y.Client, error) { return ccmd.factory.Client() }), + ) + + flags.WithOptions( + cmd, + flags.WithExtendedPipelineSupport("device", "device", false, "deviceId", "source.id", "managedObject.id", "id"), ) ccmd.SubCommand = subcommand.NewSubCommand(cmd) @@ -106,84 +114,98 @@ func (n *CmdServer) RunE(cmd *cobra.Command, args []string) error { return err } - out, err := body.MarshalJSON() - if err != nil { - return err + var iter iterator.Iterator + if inputIterators.Total > 0 { + iter = mapbuilder.NewMapBuilderIterator(body) + } else { + iter = iterator.NewBoundIterator(mapbuilder.NewMapBuilderIterator(body), 1) } - op := gjson.ParseBytes(out) - device := op.Get("device").String() - craConfig, err := c8yfetcher.DetectRemoteAccessConfiguration(client, device, n.configuration) + commonOptions, err := cfg.GetOutputCommonOptions(cmd) if err != nil { return err } - - craClient := remoteaccess.NewRemoteAccessClient(client, remoteaccess.RemoteAccessOptions{ - ManagedObjectID: device, - RemoteAccessID: craConfig.ID, - }, log) - - if n.listen == "-" { - log.Debugf("Listening to request from stdin") - return craClient.ListenServe(n.factory.IOStreams.In, n.factory.IOStreams.Out) - } - - // TCP / socket listener - if err := craClient.Listen(n.listen); err != nil { - return err - } - - localAddress := craClient.GetListenerAddress() - host, port, _ := strings.Cut(localAddress, ":") - if host == "" { - host = "127.0.0.1" - } - - // cs := n.factory.IOStreams.ColorScheme() - - type ServerInfo struct { - Port string - Host string - Device string - LocalAddress string - User string - CumulocityURL string - } - - messageTmpl := heredoc.Doc(` - Listening for device ({{.Device}}) {{.CumulocityURL}} on {{.LocalAddress}} - - Proxy: {{.LocalAddress}} - - Example clients: - - SSH: ssh -p {{.Port}} {{.User}}@{{.Host}} - - Website: http://{{.LocalAddress}} - - Press ctrl-c to shutdown the server - `) - - t := template.Must(template.New("message").Parse(messageTmpl)) - t.Execute(n.factory.IOStreams.ErrOut, ServerInfo{ - Host: host, - Port: port, - CumulocityURL: strings.TrimRight(client.BaseURL.String(), "/"), - LocalAddress: localAddress, - Device: n.device[0], - User: "", + commonOptions.DisableResultPropertyDetection() + + return n.factory.RunWithGenericWorkers(cmd, inputIterators, iter, func(j worker.Job) (any, error) { + item := gjson.ParseBytes(j.Value.([]byte)) + device := item.Get("device").String() + + craConfig, err := c8yfetcher.DetectRemoteAccessConfiguration(client, device, n.configuration) + if err != nil { + return nil, err + } + + log.Debugf("Using remote access configuration: id=%s, name=%s", craConfig.ID, craConfig.Name) + + // Lookup configuration + craClient := remoteaccess.NewRemoteAccessClient(client, remoteaccess.RemoteAccessOptions{ + ManagedObjectID: device, + RemoteAccessID: craConfig.ID, + }, log) + + if n.listen == "-" { + log.Debugf("Listening to request from stdin") + serverErr := craClient.ListenServe(n.factory.IOStreams.In, n.factory.IOStreams.Out) + return nil, serverErr + } + + // TCP / socket listener + if err := craClient.Listen(n.listen); err != nil { + return nil, err + } + + localAddress := craClient.GetListenerAddress() + host, port, _ := strings.Cut(localAddress, ":") + if host == "" { + host = "127.0.0.1" + } + + type ServerInfo struct { + Port string + Host string + Device string + LocalAddress string + User string + CumulocityURL string + } + + messageTmpl := heredoc.Doc(` + Listening for device ({{.Device}}) {{.CumulocityURL}} on {{.LocalAddress}} + + Proxy: {{.LocalAddress}} + + Example clients: + + SSH: ssh -p {{.Port}} {{.User}}@{{.Host}} + + Website: http://{{.LocalAddress}} + + Press ctrl-c to shutdown the server + `) + + t := template.Must(template.New("message").Parse(messageTmpl)) + t.Execute(n.factory.IOStreams.ErrOut, ServerInfo{ + Host: host, + Port: port, + CumulocityURL: strings.TrimRight(client.BaseURL.String(), "/"), + LocalAddress: localAddress, + Device: device, + User: "", + }) + + if n.open { + go func() { + // TODO: Should it wait for the server to be up? + // time.Sleep(200 * time.Millisecond) + targetURL := fmt.Sprintf("http://%s:%s", host, port) + if err := n.factory.Browser.Browse(targetURL); err != nil { + cfg.Logger.Warnf("%s", err) + } + }() + } + + serverErr := craClient.Serve() + return nil, serverErr }) - - if n.open { - go func() { - // TODO: Should it wait for the server to be up? - // time.Sleep(200 * time.Millisecond) - targetURL := fmt.Sprintf("http://%s:%s", host, port) - if err := n.factory.Browser.Browse(targetURL); err != nil { - cfg.Logger.Warnf("%s", err) - } - }() - } - - return craClient.Serve() } From 61b1901073a0f80b44bb0f05febf11d0db40ad65 Mon Sep 17 00:00:00 2001 From: Reuben Miller Date: Thu, 16 May 2024 08:56:53 +0200 Subject: [PATCH 09/23] rename remote access configurations spec --- ...n.json => remoteAccessConfigurations.json} | 37 +++++++------- ...n.yaml => remoteAccessConfigurations.yaml} | 48 ++++++++++++------- 2 files changed, 49 insertions(+), 36 deletions(-) rename api/spec/json/{remoteAccessConfiguration.json => remoteAccessConfigurations.json} (91%) rename api/spec/yaml/{remoteAccessConfiguration.yaml => remoteAccessConfigurations.yaml} (88%) diff --git a/api/spec/json/remoteAccessConfiguration.json b/api/spec/json/remoteAccessConfigurations.json similarity index 91% rename from api/spec/json/remoteAccessConfiguration.json rename to api/spec/json/remoteAccessConfigurations.json index 22ea74bef..09e00690e 100644 --- a/api/spec/json/remoteAccessConfiguration.json +++ b/api/spec/json/remoteAccessConfigurations.json @@ -1,6 +1,6 @@ { "group": { - "name": "remoteaccess/configuration", + "name": "remoteaccess/configurations", "description": "Manage Cloud Remote Access configuration", "descriptionLong": "Cloud Remote Access configuration management", "link": "https://cumulocity.com/docs/cloud-remote-access/using-cloud-remote-access/" @@ -10,7 +10,7 @@ "name": "list", "method": "GET", "description": "List remote access configurations", - "descriptionLong": "List the remote access configurations already configured for the device\n", + "descriptionLong": "List the remote access configurations already configured for a device\n", "alias": { "go": "list", "powershell": "Get-RemoteAccessConfigurationCollection" @@ -18,13 +18,13 @@ "examples": { "go": [ { - "command": "c8y remoteaccess configurations list --device mydevice", + "command": "c8y remoteaccess configurations list --device device01", "description": "List remote access configurations for a given device" } ], "powershell": [ { - "command": "Get-RemoteAccessConfigurationCollection -Device mydevice", + "command": "Get-RemoteAccessConfigurationCollection -Device device01", "description": "List remote access configurations for a given device" } ] @@ -52,13 +52,13 @@ "examples": { "go": [ { - "command": "c8y remoteaccess configurations get --device mydevice --id 1", + "command": "c8y remoteaccess configurations get --device device01 --id 1", "description": "Get existing remote access configuration" } ], "powershell": [ { - "command": "Get-RemoteAccessConfiguration -Device mydevice -Id 1", + "command": "Get-RemoteAccessConfiguration -Device device01 -Id 1", "description": "Get existing remote access configuration" } ] @@ -90,13 +90,13 @@ "examples": { "go": [ { - "command": "c8y remoteaccess configurations update --device mydevice --id 1", + "command": "c8y remoteaccess configurations update --device device01 --id 1", "description": "Update an existing remote access configuration" } ], "powershell": [ { - "command": "Update-RemoteAccessConfiguration -Device mydevice -Id 1", + "command": "Update-RemoteAccessConfiguration -Device device01 -Id 1", "description": "Update an existing remote access configuration" } ] @@ -129,6 +129,7 @@ { "name": "delete", "description": "Delete remote access configuration", + "descriptionLong": "Delete an existing remote access configuration", "alias": { "go": "delete", "powershell": "Remove-RemoteAccessConfiguration" @@ -136,13 +137,13 @@ "examples": { "go": [ { - "command": "c8y remoteaccess configurations delete --device mydevice --id 1", + "command": "c8y remoteaccess configurations delete --device device01 --id 1", "description": "Delete an existing remote access configuration" } ], "powershell": [ { - "command": "Remove-RemoteAccessConfiguration -Device mydevice -Id 1", + "command": "Remove-RemoteAccessConfiguration -Device device01 -Id 1", "description": "Delete an existing remote access configuration" } ] @@ -167,7 +168,7 @@ { "name": "create-passthrough", "description": "Create passthrough configuration", - "descriptionLong": "Create passthrough configuration\n", + "descriptionLong": "Create a passthrough configuration which enables you to connect\ndirectly to the device (via Cumulocity IoT) using a native client such as ssh.\n\nAfter a passthrough connection has been added, you can open a proxy to it using\none of the following commands:\n\n * c8y remoteaccess server\n * c8y remoteaccess connect ssh\n", "alias": { "go": "create-passthrough", "powershell": "New-RemoteAccessPassthroughConfiguration" @@ -177,21 +178,21 @@ "examples": { "go": [ { - "command": "c8y remoteaccess configurations create-passthrough --device mydevice\n", + "command": "c8y remoteaccess configurations create-passthrough --device device01\n", "description": "Create a SSH passthrough configuration to the localhost" }, { - "command": "c8y remoteaccess configurations create-passthrough --device mydevice --hostname customhost --port 1234 --name \"My custom configuration\"\n", + "command": "c8y remoteaccess configurations create-passthrough --device device01 --hostname customhost --port 1234 --name \"My custom configuration\"\n", "description": "Create a SSH passthrough configuration with custom details" } ], "powershell": [ { - "command": "New-RemoteAccessPassthroughConfiguration -Device mydevice\n", + "command": "New-RemoteAccessPassthroughConfiguration -Device device01\n", "description": "Create a SSH passthrough configuration to the localhost" }, { - "command": "New-RemoteAccessPassthroughConfiguration -Device mydevice -Hostname customhost -Port 1234 -Name \"My custom configuration\"\n", + "command": "New-RemoteAccessPassthroughConfiguration -Device device01 -Hostname customhost -Port 1234 -Name \"My custom configuration\"\n", "description": "Create a SSH passthrough configuration with custom details" } ] @@ -249,7 +250,7 @@ { "name": "create-webssh", "description": "Create web ssh configuration", - "descriptionLong": "Create web ssh configuration\n", + "descriptionLong": "Create a new WebSSH configuration. If no arguments are provided\nthen sensible defaults will be used.\n", "alias": { "go": "create-webssh", "powershell": "New-RemoteAccessWebSSHConfiguration" @@ -353,7 +354,7 @@ { "name": "create-vnc", "description": "Create vnc configuration", - "descriptionLong": "Create vnc configuration\n", + "descriptionLong": "Create a new VNC configuration. If no arguments are provided\nthen sensible defaults will be used.\n", "alias": { "go": "create-vnc", "powershell": "New-RemoteAccessVNCConfiguration" @@ -443,7 +444,7 @@ { "name": "create-telnet", "description": "Create telnet configuration", - "descriptionLong": "Create telnet configuration\n", + "descriptionLong": "Create a new Telnet configuration. If no arguments are provided\nthen sensible defaults will be used.\n", "alias": { "go": "create-telnet", "powershell": "New-RemoteAccessTelnetConfiguration" diff --git a/api/spec/yaml/remoteAccessConfiguration.yaml b/api/spec/yaml/remoteAccessConfigurations.yaml similarity index 88% rename from api/spec/yaml/remoteAccessConfiguration.yaml rename to api/spec/yaml/remoteAccessConfigurations.yaml index 8d0b44ff2..8e19cb07b 100644 --- a/api/spec/yaml/remoteAccessConfiguration.yaml +++ b/api/spec/yaml/remoteAccessConfigurations.yaml @@ -1,7 +1,7 @@ # yaml-language-server: $schema=../schema.json --- group: - name: remoteaccess/configuration + name: remoteaccess/configurations description: Manage Cloud Remote Access configuration descriptionLong: Cloud Remote Access configuration management link: https://cumulocity.com/docs/cloud-remote-access/using-cloud-remote-access/ @@ -11,16 +11,16 @@ commands: method: GET description: List remote access configurations descriptionLong: | - List the remote access configurations already configured for the device + List the remote access configurations already configured for a device alias: go: list powershell: Get-RemoteAccessConfigurationCollection examples: go: - - command: c8y remoteaccess configurations list --device mydevice + - command: c8y remoteaccess configurations list --device device01 description: List remote access configurations for a given device powershell: - - command: Get-RemoteAccessConfigurationCollection -Device mydevice + - command: Get-RemoteAccessConfigurationCollection -Device device01 description: List remote access configurations for a given device path: /service/remoteaccess/devices/{device}/configurations @@ -40,10 +40,10 @@ commands: path: /service/remoteaccess/devices/{device}/configurations/{id} examples: go: - - command: c8y remoteaccess configurations get --device mydevice --id 1 + - command: c8y remoteaccess configurations get --device device01 --id 1 description: Get existing remote access configuration powershell: - - command: Get-RemoteAccessConfiguration -Device mydevice -Id 1 + - command: Get-RemoteAccessConfiguration -Device device01 -Id 1 description: Get existing remote access configuration pathParameters: - name: device @@ -65,10 +65,10 @@ commands: powershell: Update-RemoteAccessConfiguration examples: go: - - command: c8y remoteaccess configurations update --device mydevice --id 1 + - command: c8y remoteaccess configurations update --device device01 --id 1 description: Update an existing remote access configuration powershell: - - command: Update-RemoteAccessConfiguration -Device mydevice -Id 1 + - command: Update-RemoteAccessConfiguration -Device device01 -Id 1 description: Update an existing remote access configuration method: PUT path: /service/remoteaccess/devices/{device}/configurations/{id} @@ -90,15 +90,16 @@ commands: - name: delete description: Delete remote access configuration + descriptionLong: Delete an existing remote access configuration alias: go: delete powershell: Remove-RemoteAccessConfiguration examples: go: - - command: c8y remoteaccess configurations delete --device mydevice --id 1 + - command: c8y remoteaccess configurations delete --device device01 --id 1 description: Delete an existing remote access configuration powershell: - - command: Remove-RemoteAccessConfiguration -Device mydevice -Id 1 + - command: Remove-RemoteAccessConfiguration -Device device01 -Id 1 description: Delete an existing remote access configuration method: DELETE path: /service/remoteaccess/devices/{device}/configurations/{id} @@ -116,7 +117,15 @@ commands: - name: create-passthrough description: Create passthrough configuration descriptionLong: | - Create passthrough configuration + Create a passthrough configuration which enables you to connect + directly to the device (via Cumulocity IoT) using a native client such as ssh. + + After a passthrough connection has been added, you can open a proxy to it using + one of the following commands: + + * c8y remoteaccess server + * c8y remoteaccess connect ssh + alias: go: create-passthrough powershell: New-RemoteAccessPassthroughConfiguration @@ -125,17 +134,17 @@ commands: examples: go: - command: | - c8y remoteaccess configurations create-passthrough --device mydevice + c8y remoteaccess configurations create-passthrough --device device01 description: Create a SSH passthrough configuration to the localhost - command: | - c8y remoteaccess configurations create-passthrough --device mydevice --hostname customhost --port 1234 --name "My custom configuration" + c8y remoteaccess configurations create-passthrough --device device01 --hostname customhost --port 1234 --name "My custom configuration" description: Create a SSH passthrough configuration with custom details powershell: - command: | - New-RemoteAccessPassthroughConfiguration -Device mydevice + New-RemoteAccessPassthroughConfiguration -Device device01 description: Create a SSH passthrough configuration to the localhost - command: | - New-RemoteAccessPassthroughConfiguration -Device mydevice -Hostname customhost -Port 1234 -Name "My custom configuration" + New-RemoteAccessPassthroughConfiguration -Device device01 -Hostname customhost -Port 1234 -Name "My custom configuration" description: Create a SSH passthrough configuration with custom details pathParameters: @@ -179,7 +188,8 @@ commands: - name: create-webssh description: Create web ssh configuration descriptionLong: | - Create web ssh configuration + Create a new WebSSH configuration. If no arguments are provided + then sensible defaults will be used. alias: go: create-webssh powershell: New-RemoteAccessWebSSHConfiguration @@ -259,7 +269,8 @@ commands: - name: create-vnc description: Create vnc configuration descriptionLong: | - Create vnc configuration + Create a new VNC configuration. If no arguments are provided + then sensible defaults will be used. alias: go: create-vnc powershell: New-RemoteAccessVNCConfiguration @@ -328,7 +339,8 @@ commands: - name: create-telnet description: Create telnet configuration descriptionLong: | - Create telnet configuration + Create a new Telnet configuration. If no arguments are provided + then sensible defaults will be used. alias: go: create-telnet powershell: New-RemoteAccessTelnetConfiguration From 613ee06a94032533fbbc87c13809b3cb6fedfa60 Mon Sep 17 00:00:00 2001 From: Reuben Miller Date: Thu, 16 May 2024 08:57:24 +0200 Subject: [PATCH 10/23] regenerate remote access code --- .../configurations.auto.go} | 26 +++++++++---------- .../create_passthrough.auto.go | 13 +++++++--- .../create_telnet/create_telnet.auto.go | 3 ++- .../create_vnc/create_vnc.auto.go | 3 ++- .../create_webssh/create_webssh.auto.go | 3 ++- .../delete/delete.auto.go | 4 +-- .../get/get.auto.go | 2 +- .../list/list.auto.go | 4 +-- .../update/update.auto.go | 2 +- pkg/cmd/remoteaccess/remoteaccess.manual.go | 4 +-- .../tests/remoteaccess/configuration_get.yaml | 8 ------ .../remoteaccess/configuration_list.yaml | 8 ------ .../configurations_create-passthrough.yaml} | 12 ++++----- .../configurations_create-telnet.yaml} | 2 +- .../configurations_create-vnc.yaml} | 4 +-- .../configurations_create-webssh.yaml} | 4 +-- .../remoteaccess/configurations_delete.yaml} | 6 ++--- .../remoteaccess/configurations_get.yaml | 8 ++++++ .../remoteaccess/configurations_list.yaml | 8 ++++++ .../remoteaccess/configurations_update.yaml} | 6 ++--- .../Public/Get-RemoteAccessConfiguration.ps1 | 10 +++---- ...et-RemoteAccessConfigurationCollection.ps1 | 12 ++++----- ...w-RemoteAccessPassthroughConfiguration.ps1 | 21 ++++++++++----- .../New-RemoteAccessTelnetConfiguration.ps1 | 11 ++++---- .../New-RemoteAccessVNCConfiguration.ps1 | 11 ++++---- .../New-RemoteAccessWebSSHConfiguration.ps1 | 11 ++++---- .../Remove-RemoteAccessConfiguration.ps1 | 12 ++++----- ...t-RemoteAccessConfiguration.auto.Tests.ps1 | 2 +- ...cessConfigurationCollection.auto.Tests.ps1 | 2 +- ...essPassthroughConfiguration.auto.Tests.ps1 | 4 +-- ...e-RemoteAccessConfiguration.auto.Tests.ps1 | 2 +- 31 files changed, 124 insertions(+), 104 deletions(-) rename pkg/cmd/remoteaccess/{configuration/configuration.auto.go => configurations/configurations.auto.go} (75%) rename pkg/cmd/remoteaccess/{configuration => configurations}/create_passthrough/create_passthrough.auto.go (90%) rename pkg/cmd/remoteaccess/{configuration => configurations}/create_telnet/create_telnet.auto.go (97%) rename pkg/cmd/remoteaccess/{configuration => configurations}/create_vnc/create_vnc.auto.go (97%) rename pkg/cmd/remoteaccess/{configuration => configurations}/create_webssh/create_webssh.auto.go (97%) rename pkg/cmd/remoteaccess/{configuration => configurations}/delete/delete.auto.go (96%) rename pkg/cmd/remoteaccess/{configuration => configurations}/get/get.auto.go (98%) rename pkg/cmd/remoteaccess/{configuration => configurations}/list/list.auto.go (98%) rename pkg/cmd/remoteaccess/{configuration => configurations}/update/update.auto.go (98%) delete mode 100644 tests/auto/remoteaccessconfiguration/tests/remoteaccess/configuration_get.yaml delete mode 100644 tests/auto/remoteaccessconfiguration/tests/remoteaccess/configuration_list.yaml rename tests/auto/{remoteaccessconfiguration/tests/remoteaccess/configuration_create-passthrough.yaml => remoteaccessconfigurations/tests/remoteaccess/configurations_create-passthrough.yaml} (54%) rename tests/auto/{remoteaccessconfiguration/tests/remoteaccess/configuration_create-telnet.yaml => remoteaccessconfigurations/tests/remoteaccess/configurations_create-telnet.yaml} (76%) rename tests/auto/{remoteaccessconfiguration/tests/remoteaccess/configuration_create-vnc.yaml => remoteaccessconfigurations/tests/remoteaccess/configurations_create-vnc.yaml} (73%) rename tests/auto/{remoteaccessconfiguration/tests/remoteaccess/configuration_create-webssh.yaml => remoteaccessconfigurations/tests/remoteaccess/configurations_create-webssh.yaml} (75%) rename tests/auto/{remoteaccessconfiguration/tests/remoteaccess/configuration_delete.yaml => remoteaccessconfigurations/tests/remoteaccess/configurations_delete.yaml} (50%) create mode 100644 tests/auto/remoteaccessconfigurations/tests/remoteaccess/configurations_get.yaml create mode 100644 tests/auto/remoteaccessconfigurations/tests/remoteaccess/configurations_list.yaml rename tests/auto/{remoteaccessconfiguration/tests/remoteaccess/configuration_update.yaml => remoteaccessconfigurations/tests/remoteaccess/configurations_update.yaml} (50%) diff --git a/pkg/cmd/remoteaccess/configuration/configuration.auto.go b/pkg/cmd/remoteaccess/configurations/configurations.auto.go similarity index 75% rename from pkg/cmd/remoteaccess/configuration/configuration.auto.go rename to pkg/cmd/remoteaccess/configurations/configurations.auto.go index 78f2b0dbd..81221e74c 100644 --- a/pkg/cmd/remoteaccess/configuration/configuration.auto.go +++ b/pkg/cmd/remoteaccess/configurations/configurations.auto.go @@ -1,28 +1,28 @@ -package configuration +package configurations import ( - cmdCreatePassthrough "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/remoteaccess/configuration/create_passthrough" - cmdCreateTelnet "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/remoteaccess/configuration/create_telnet" - cmdCreateVnc "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/remoteaccess/configuration/create_vnc" - cmdCreateWebssh "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/remoteaccess/configuration/create_webssh" - cmdDelete "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/remoteaccess/configuration/delete" - cmdGet "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/remoteaccess/configuration/get" - cmdList "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/remoteaccess/configuration/list" - cmdUpdate "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/remoteaccess/configuration/update" + cmdCreatePassthrough "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/remoteaccess/configurations/create_passthrough" + cmdCreateTelnet "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/remoteaccess/configurations/create_telnet" + cmdCreateVnc "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/remoteaccess/configurations/create_vnc" + cmdCreateWebssh "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/remoteaccess/configurations/create_webssh" + cmdDelete "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/remoteaccess/configurations/delete" + cmdGet "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/remoteaccess/configurations/get" + cmdList "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/remoteaccess/configurations/list" + cmdUpdate "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/remoteaccess/configurations/update" "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/subcommand" "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmdutil" "github.com/spf13/cobra" ) -type SubCmdConfiguration struct { +type SubCmdConfigurations struct { *subcommand.SubCommand } -func NewSubCommand(f *cmdutil.Factory) *SubCmdConfiguration { - ccmd := &SubCmdConfiguration{} +func NewSubCommand(f *cmdutil.Factory) *SubCmdConfigurations { + ccmd := &SubCmdConfigurations{} cmd := &cobra.Command{ - Use: "configuration", + Use: "configurations", Short: "Manage Cloud Remote Access configuration", Long: `Cloud Remote Access configuration management`, } diff --git a/pkg/cmd/remoteaccess/configuration/create_passthrough/create_passthrough.auto.go b/pkg/cmd/remoteaccess/configurations/create_passthrough/create_passthrough.auto.go similarity index 90% rename from pkg/cmd/remoteaccess/configuration/create_passthrough/create_passthrough.auto.go rename to pkg/cmd/remoteaccess/configurations/create_passthrough/create_passthrough.auto.go index b7aba8cd3..09837f01d 100644 --- a/pkg/cmd/remoteaccess/configuration/create_passthrough/create_passthrough.auto.go +++ b/pkg/cmd/remoteaccess/configurations/create_passthrough/create_passthrough.auto.go @@ -32,13 +32,20 @@ func NewCreatePassthroughCmd(f *cmdutil.Factory) *CreatePassthroughCmd { cmd := &cobra.Command{ Use: "create-passthrough", Short: "Create passthrough configuration", - Long: `Create passthrough configuration + Long: `Create a passthrough configuration which enables you to connect +directly to the device (via Cumulocity IoT) using a native client such as ssh. + +After a passthrough connection has been added, you can open a proxy to it using +one of the following commands: + + * c8y remoteaccess server + * c8y remoteaccess connect ssh `, Example: heredoc.Doc(` -$ c8y remoteaccess configurations create-passthrough --device mydevice +$ c8y remoteaccess configurations create-passthrough --device device01 Create a SSH passthrough configuration to the localhost -$ c8y remoteaccess configurations create-passthrough --device mydevice --hostname customhost --port 1234 --name "My custom configuration" +$ c8y remoteaccess configurations create-passthrough --device device01 --hostname customhost --port 1234 --name "My custom configuration" Create a SSH passthrough configuration with custom details `), PreRunE: func(cmd *cobra.Command, args []string) error { diff --git a/pkg/cmd/remoteaccess/configuration/create_telnet/create_telnet.auto.go b/pkg/cmd/remoteaccess/configurations/create_telnet/create_telnet.auto.go similarity index 97% rename from pkg/cmd/remoteaccess/configuration/create_telnet/create_telnet.auto.go rename to pkg/cmd/remoteaccess/configurations/create_telnet/create_telnet.auto.go index 017023263..70b46f4ed 100644 --- a/pkg/cmd/remoteaccess/configuration/create_telnet/create_telnet.auto.go +++ b/pkg/cmd/remoteaccess/configurations/create_telnet/create_telnet.auto.go @@ -32,7 +32,8 @@ func NewCreateTelnetCmd(f *cmdutil.Factory) *CreateTelnetCmd { cmd := &cobra.Command{ Use: "create-telnet", Short: "Create telnet configuration", - Long: `Create telnet configuration + Long: `Create a new Telnet configuration. If no arguments are provided +then sensible defaults will be used. `, Example: heredoc.Doc(` $ c8y remoteaccess configurations create-telnet diff --git a/pkg/cmd/remoteaccess/configuration/create_vnc/create_vnc.auto.go b/pkg/cmd/remoteaccess/configurations/create_vnc/create_vnc.auto.go similarity index 97% rename from pkg/cmd/remoteaccess/configuration/create_vnc/create_vnc.auto.go rename to pkg/cmd/remoteaccess/configurations/create_vnc/create_vnc.auto.go index 1e9737f31..fbc7f3ec3 100644 --- a/pkg/cmd/remoteaccess/configuration/create_vnc/create_vnc.auto.go +++ b/pkg/cmd/remoteaccess/configurations/create_vnc/create_vnc.auto.go @@ -32,7 +32,8 @@ func NewCreateVncCmd(f *cmdutil.Factory) *CreateVncCmd { cmd := &cobra.Command{ Use: "create-vnc", Short: "Create vnc configuration", - Long: `Create vnc configuration + Long: `Create a new VNC configuration. If no arguments are provided +then sensible defaults will be used. `, Example: heredoc.Doc(` $ c8y remoteaccess configurations create-vnc diff --git a/pkg/cmd/remoteaccess/configuration/create_webssh/create_webssh.auto.go b/pkg/cmd/remoteaccess/configurations/create_webssh/create_webssh.auto.go similarity index 97% rename from pkg/cmd/remoteaccess/configuration/create_webssh/create_webssh.auto.go rename to pkg/cmd/remoteaccess/configurations/create_webssh/create_webssh.auto.go index fd6871164..cef989488 100644 --- a/pkg/cmd/remoteaccess/configuration/create_webssh/create_webssh.auto.go +++ b/pkg/cmd/remoteaccess/configurations/create_webssh/create_webssh.auto.go @@ -32,7 +32,8 @@ func NewCreateWebsshCmd(f *cmdutil.Factory) *CreateWebsshCmd { cmd := &cobra.Command{ Use: "create-webssh", Short: "Create web ssh configuration", - Long: `Create web ssh configuration + Long: `Create a new WebSSH configuration. If no arguments are provided +then sensible defaults will be used. `, Example: heredoc.Doc(` $ c8y remoteaccess configurations create-webssh diff --git a/pkg/cmd/remoteaccess/configuration/delete/delete.auto.go b/pkg/cmd/remoteaccess/configurations/delete/delete.auto.go similarity index 96% rename from pkg/cmd/remoteaccess/configuration/delete/delete.auto.go rename to pkg/cmd/remoteaccess/configurations/delete/delete.auto.go index 81e62582d..f67b15606 100644 --- a/pkg/cmd/remoteaccess/configuration/delete/delete.auto.go +++ b/pkg/cmd/remoteaccess/configurations/delete/delete.auto.go @@ -32,9 +32,9 @@ func NewDeleteCmd(f *cmdutil.Factory) *DeleteCmd { cmd := &cobra.Command{ Use: "delete", Short: "Delete remote access configuration", - Long: ``, + Long: `Delete an existing remote access configuration`, Example: heredoc.Doc(` -$ c8y remoteaccess configurations delete --device mydevice --id 1 +$ c8y remoteaccess configurations delete --device device01 --id 1 Delete an existing remote access configuration `), PreRunE: func(cmd *cobra.Command, args []string) error { diff --git a/pkg/cmd/remoteaccess/configuration/get/get.auto.go b/pkg/cmd/remoteaccess/configurations/get/get.auto.go similarity index 98% rename from pkg/cmd/remoteaccess/configuration/get/get.auto.go rename to pkg/cmd/remoteaccess/configurations/get/get.auto.go index 64870f0bd..da678e4a0 100644 --- a/pkg/cmd/remoteaccess/configuration/get/get.auto.go +++ b/pkg/cmd/remoteaccess/configurations/get/get.auto.go @@ -35,7 +35,7 @@ func NewGetCmd(f *cmdutil.Factory) *GetCmd { Short: "Get remote access configuration", Long: `Get an existing remote access configuration for a device`, Example: heredoc.Doc(` -$ c8y remoteaccess configurations get --device mydevice --id 1 +$ c8y remoteaccess configurations get --device device01 --id 1 Get existing remote access configuration `), PreRunE: func(cmd *cobra.Command, args []string) error { diff --git a/pkg/cmd/remoteaccess/configuration/list/list.auto.go b/pkg/cmd/remoteaccess/configurations/list/list.auto.go similarity index 98% rename from pkg/cmd/remoteaccess/configuration/list/list.auto.go rename to pkg/cmd/remoteaccess/configurations/list/list.auto.go index 0fc943ddb..ee628fdb3 100644 --- a/pkg/cmd/remoteaccess/configuration/list/list.auto.go +++ b/pkg/cmd/remoteaccess/configurations/list/list.auto.go @@ -33,10 +33,10 @@ func NewListCmd(f *cmdutil.Factory) *ListCmd { cmd := &cobra.Command{ Use: "list", Short: "List remote access configurations", - Long: `List the remote access configurations already configured for the device + Long: `List the remote access configurations already configured for a device `, Example: heredoc.Doc(` -$ c8y remoteaccess configurations list --device mydevice +$ c8y remoteaccess configurations list --device device01 List remote access configurations for a given device `), PreRunE: func(cmd *cobra.Command, args []string) error { diff --git a/pkg/cmd/remoteaccess/configuration/update/update.auto.go b/pkg/cmd/remoteaccess/configurations/update/update.auto.go similarity index 98% rename from pkg/cmd/remoteaccess/configuration/update/update.auto.go rename to pkg/cmd/remoteaccess/configurations/update/update.auto.go index e0ae04d43..e2afe4149 100644 --- a/pkg/cmd/remoteaccess/configuration/update/update.auto.go +++ b/pkg/cmd/remoteaccess/configurations/update/update.auto.go @@ -36,7 +36,7 @@ func NewUpdateCmd(f *cmdutil.Factory) *UpdateCmd { Hidden: true, Example: heredoc.Doc(` -$ c8y remoteaccess configurations update --device mydevice --id 1 +$ c8y remoteaccess configurations update --device device01 --id 1 Update an existing remote access configuration `), PreRunE: func(cmd *cobra.Command, args []string) error { diff --git a/pkg/cmd/remoteaccess/remoteaccess.manual.go b/pkg/cmd/remoteaccess/remoteaccess.manual.go index 6f4618b90..f57baf69e 100644 --- a/pkg/cmd/remoteaccess/remoteaccess.manual.go +++ b/pkg/cmd/remoteaccess/remoteaccess.manual.go @@ -1,7 +1,7 @@ package remoteaccess import ( - "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/remoteaccess/configuration" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/remoteaccess/configurations" "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/remoteaccess/connect" "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/remoteaccess/server" "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/subcommand" @@ -25,7 +25,7 @@ func NewSubCommand(f *cmdutil.Factory) *SubCmdRemoteAccess { // Subcommands cmd.AddCommand(connect.NewSubCommand(f).GetCommand()) cmd.AddCommand(server.NewCmdServer(f).GetCommand()) - cmd.AddCommand(configuration.NewSubCommand(f).GetCommand()) + cmd.AddCommand(configurations.NewSubCommand(f).GetCommand()) ccmd.SubCommand = subcommand.NewSubCommand(cmd) diff --git a/tests/auto/remoteaccessconfiguration/tests/remoteaccess/configuration_get.yaml b/tests/auto/remoteaccessconfiguration/tests/remoteaccess/configuration_get.yaml deleted file mode 100644 index a25289506..000000000 --- a/tests/auto/remoteaccessconfiguration/tests/remoteaccess/configuration_get.yaml +++ /dev/null @@ -1,8 +0,0 @@ -tests: - remoteaccess/configuration_get_Get existing remote access configuration: - command: c8y remoteaccess configurations get --device mydevice --id 1 - exit-code: 0 - stdout: - json: - method: GET - path: /service/remoteaccess/devices/mydevice/configurations/1 diff --git a/tests/auto/remoteaccessconfiguration/tests/remoteaccess/configuration_list.yaml b/tests/auto/remoteaccessconfiguration/tests/remoteaccess/configuration_list.yaml deleted file mode 100644 index ba7831c73..000000000 --- a/tests/auto/remoteaccessconfiguration/tests/remoteaccess/configuration_list.yaml +++ /dev/null @@ -1,8 +0,0 @@ -tests: - remoteaccess/configuration_list_List remote access configurations for a given device: - command: c8y remoteaccess configurations list --device mydevice - exit-code: 0 - stdout: - json: - method: GET - path: /service/remoteaccess/devices/mydevice/configurations diff --git a/tests/auto/remoteaccessconfiguration/tests/remoteaccess/configuration_create-passthrough.yaml b/tests/auto/remoteaccessconfigurations/tests/remoteaccess/configurations_create-passthrough.yaml similarity index 54% rename from tests/auto/remoteaccessconfiguration/tests/remoteaccess/configuration_create-passthrough.yaml rename to tests/auto/remoteaccessconfigurations/tests/remoteaccess/configurations_create-passthrough.yaml index 39dbb2314..9b8464029 100644 --- a/tests/auto/remoteaccessconfiguration/tests/remoteaccess/configuration_create-passthrough.yaml +++ b/tests/auto/remoteaccessconfigurations/tests/remoteaccess/configurations_create-passthrough.yaml @@ -1,15 +1,15 @@ tests: - remoteaccess/configuration_create-passthrough_Create a SSH passthrough configuration to the localhost: + remoteaccess/configurations_create-passthrough_Create a SSH passthrough configuration to the localhost: command: | - c8y remoteaccess configurations create-passthrough --device mydevice + c8y remoteaccess configurations create-passthrough --device device01 exit-code: 0 stdout: json: method: POST - path: /service/remoteaccess/devices/mydevice/configurations - remoteaccess/configuration_create-passthrough_Create a SSH passthrough configuration with custom details: + path: /service/remoteaccess/devices/device01/configurations + remoteaccess/configurations_create-passthrough_Create a SSH passthrough configuration with custom details: command: | - c8y remoteaccess configurations create-passthrough --device mydevice --hostname customhost --port 1234 --name "My custom configuration" + c8y remoteaccess configurations create-passthrough --device device01 --hostname customhost --port 1234 --name "My custom configuration" exit-code: 0 stdout: json: @@ -17,4 +17,4 @@ tests: body.name: My custom configuration body.port: "1234" method: POST - path: /service/remoteaccess/devices/mydevice/configurations + path: /service/remoteaccess/devices/device01/configurations diff --git a/tests/auto/remoteaccessconfiguration/tests/remoteaccess/configuration_create-telnet.yaml b/tests/auto/remoteaccessconfigurations/tests/remoteaccess/configurations_create-telnet.yaml similarity index 76% rename from tests/auto/remoteaccessconfiguration/tests/remoteaccess/configuration_create-telnet.yaml rename to tests/auto/remoteaccessconfigurations/tests/remoteaccess/configurations_create-telnet.yaml index 57d961458..f4eb9fbc4 100644 --- a/tests/auto/remoteaccessconfiguration/tests/remoteaccess/configuration_create-telnet.yaml +++ b/tests/auto/remoteaccessconfigurations/tests/remoteaccess/configurations_create-telnet.yaml @@ -1,5 +1,5 @@ tests: - remoteaccess/configuration_create-telnet_Create a telnet configuration: + remoteaccess/configurations_create-telnet_Create a telnet configuration: command: | c8y remoteaccess configurations create-telnet exit-code: 0 diff --git a/tests/auto/remoteaccessconfiguration/tests/remoteaccess/configuration_create-vnc.yaml b/tests/auto/remoteaccessconfigurations/tests/remoteaccess/configurations_create-vnc.yaml similarity index 73% rename from tests/auto/remoteaccessconfiguration/tests/remoteaccess/configuration_create-vnc.yaml rename to tests/auto/remoteaccessconfigurations/tests/remoteaccess/configurations_create-vnc.yaml index 0b151722e..d6944e056 100644 --- a/tests/auto/remoteaccessconfiguration/tests/remoteaccess/configuration_create-vnc.yaml +++ b/tests/auto/remoteaccessconfigurations/tests/remoteaccess/configurations_create-vnc.yaml @@ -1,5 +1,5 @@ tests: - remoteaccess/configuration_create-vnc_Create a VNC configuration that does not require a password: + remoteaccess/configurations_create-vnc_Create a VNC configuration that does not require a password: command: | c8y remoteaccess configurations create-vnc exit-code: 0 @@ -7,7 +7,7 @@ tests: json: method: POST path: /service/remoteaccess/devices/{device}/configurations - remoteaccess/configuration_create-vnc_Create a VNC configuration that requires a password: + remoteaccess/configurations_create-vnc_Create a VNC configuration that requires a password: command: c8y remoteaccess configurations create-vnc --password 'asd08dcj23dsf{@#9}' exit-code: 0 stdout: diff --git a/tests/auto/remoteaccessconfiguration/tests/remoteaccess/configuration_create-webssh.yaml b/tests/auto/remoteaccessconfigurations/tests/remoteaccess/configurations_create-webssh.yaml similarity index 75% rename from tests/auto/remoteaccessconfiguration/tests/remoteaccess/configuration_create-webssh.yaml rename to tests/auto/remoteaccessconfigurations/tests/remoteaccess/configurations_create-webssh.yaml index 0ff8ce6a8..20262713f 100644 --- a/tests/auto/remoteaccessconfiguration/tests/remoteaccess/configuration_create-webssh.yaml +++ b/tests/auto/remoteaccessconfigurations/tests/remoteaccess/configurations_create-webssh.yaml @@ -1,5 +1,5 @@ tests: - remoteaccess/configuration_create-webssh_Create a webssh configuration: + remoteaccess/configurations_create-webssh_Create a webssh configuration: command: | c8y remoteaccess configurations create-webssh exit-code: 0 @@ -7,7 +7,7 @@ tests: json: method: POST path: /service/remoteaccess/devices/{device}/configurations - remoteaccess/configuration_create-webssh_Create a webssh configuration with a custom hostname and port: + remoteaccess/configurations_create-webssh_Create a webssh configuration with a custom hostname and port: command: c8y remoteaccess configurations create-webssh --hostname 127.0.0.1 --port 2222 exit-code: 0 stdout: diff --git a/tests/auto/remoteaccessconfiguration/tests/remoteaccess/configuration_delete.yaml b/tests/auto/remoteaccessconfigurations/tests/remoteaccess/configurations_delete.yaml similarity index 50% rename from tests/auto/remoteaccessconfiguration/tests/remoteaccess/configuration_delete.yaml rename to tests/auto/remoteaccessconfigurations/tests/remoteaccess/configurations_delete.yaml index 36f6060f6..84a487cd2 100644 --- a/tests/auto/remoteaccessconfiguration/tests/remoteaccess/configuration_delete.yaml +++ b/tests/auto/remoteaccessconfigurations/tests/remoteaccess/configurations_delete.yaml @@ -1,8 +1,8 @@ tests: - remoteaccess/configuration_delete_Delete an existing remote access configuration: - command: c8y remoteaccess configurations delete --device mydevice --id 1 + remoteaccess/configurations_delete_Delete an existing remote access configuration: + command: c8y remoteaccess configurations delete --device device01 --id 1 exit-code: 0 stdout: json: method: DELETE - path: /service/remoteaccess/devices/mydevice/configurations/1 + path: /service/remoteaccess/devices/device01/configurations/1 diff --git a/tests/auto/remoteaccessconfigurations/tests/remoteaccess/configurations_get.yaml b/tests/auto/remoteaccessconfigurations/tests/remoteaccess/configurations_get.yaml new file mode 100644 index 000000000..962ca9d53 --- /dev/null +++ b/tests/auto/remoteaccessconfigurations/tests/remoteaccess/configurations_get.yaml @@ -0,0 +1,8 @@ +tests: + remoteaccess/configurations_get_Get existing remote access configuration: + command: c8y remoteaccess configurations get --device device01 --id 1 + exit-code: 0 + stdout: + json: + method: GET + path: /service/remoteaccess/devices/device01/configurations/1 diff --git a/tests/auto/remoteaccessconfigurations/tests/remoteaccess/configurations_list.yaml b/tests/auto/remoteaccessconfigurations/tests/remoteaccess/configurations_list.yaml new file mode 100644 index 000000000..38c49b8e6 --- /dev/null +++ b/tests/auto/remoteaccessconfigurations/tests/remoteaccess/configurations_list.yaml @@ -0,0 +1,8 @@ +tests: + remoteaccess/configurations_list_List remote access configurations for a given device: + command: c8y remoteaccess configurations list --device device01 + exit-code: 0 + stdout: + json: + method: GET + path: /service/remoteaccess/devices/device01/configurations diff --git a/tests/auto/remoteaccessconfiguration/tests/remoteaccess/configuration_update.yaml b/tests/auto/remoteaccessconfigurations/tests/remoteaccess/configurations_update.yaml similarity index 50% rename from tests/auto/remoteaccessconfiguration/tests/remoteaccess/configuration_update.yaml rename to tests/auto/remoteaccessconfigurations/tests/remoteaccess/configurations_update.yaml index 18033df99..d97cea95f 100644 --- a/tests/auto/remoteaccessconfiguration/tests/remoteaccess/configuration_update.yaml +++ b/tests/auto/remoteaccessconfigurations/tests/remoteaccess/configurations_update.yaml @@ -1,8 +1,8 @@ tests: - remoteaccess/configuration_update_Update an existing remote access configuration: - command: c8y remoteaccess configurations update --device mydevice --id 1 + remoteaccess/configurations_update_Update an existing remote access configuration: + command: c8y remoteaccess configurations update --device device01 --id 1 exit-code: 0 stdout: json: method: PUT - path: /service/remoteaccess/devices/mydevice/configurations/1 + path: /service/remoteaccess/devices/device01/configurations/1 diff --git a/tools/PSc8y/Public/Get-RemoteAccessConfiguration.ps1 b/tools/PSc8y/Public/Get-RemoteAccessConfiguration.ps1 index 0dc901e89..acf5b8d3f 100644 --- a/tools/PSc8y/Public/Get-RemoteAccessConfiguration.ps1 +++ b/tools/PSc8y/Public/Get-RemoteAccessConfiguration.ps1 @@ -8,10 +8,10 @@ Get remote access configuration Get an existing remote access configuration for a device .LINK -https://reubenmiller.github.io/go-c8y-cli/docs/cli/c8y/remoteaccess_configuration_get +https://reubenmiller.github.io/go-c8y-cli/docs/cli/c8y/remoteaccess_configurations_get .EXAMPLE -PS> Get-RemoteAccessConfiguration -Device mydevice -Id 1 +PS> Get-RemoteAccessConfiguration -Device device01 -Id 1 Get existing remote access configuration @@ -44,7 +44,7 @@ Get existing remote access configuration Use-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState } - $c8yargs = New-ClientArgument -Parameters $PSBoundParameters -Command "remoteaccess configuration get" + $c8yargs = New-ClientArgument -Parameters $PSBoundParameters -Command "remoteaccess configurations get" $ClientOptions = Get-ClientOutputOption $PSBoundParameters $TypeOptions = @{ Type = "" @@ -58,13 +58,13 @@ Get existing remote access configuration if ($ClientOptions.ConvertToPS) { $Id ` | Group-ClientRequests ` - | c8y remoteaccess configuration get $c8yargs ` + | c8y remoteaccess configurations get $c8yargs ` | ConvertFrom-ClientOutput @TypeOptions } else { $Id ` | Group-ClientRequests ` - | c8y remoteaccess configuration get $c8yargs + | c8y remoteaccess configurations get $c8yargs } } diff --git a/tools/PSc8y/Public/Get-RemoteAccessConfigurationCollection.ps1 b/tools/PSc8y/Public/Get-RemoteAccessConfigurationCollection.ps1 index 284a34897..e6ce9c686 100644 --- a/tools/PSc8y/Public/Get-RemoteAccessConfigurationCollection.ps1 +++ b/tools/PSc8y/Public/Get-RemoteAccessConfigurationCollection.ps1 @@ -5,14 +5,14 @@ Function Get-RemoteAccessConfigurationCollection { List remote access configurations .DESCRIPTION -List the remote access configurations already configured for the device +List the remote access configurations already configured for a device .LINK -https://reubenmiller.github.io/go-c8y-cli/docs/cli/c8y/remoteaccess_configuration_list +https://reubenmiller.github.io/go-c8y-cli/docs/cli/c8y/remoteaccess_configurations_list .EXAMPLE -PS> Get-RemoteAccessConfigurationCollection -Device mydevice +PS> Get-RemoteAccessConfigurationCollection -Device device01 List remote access configurations for a given device @@ -40,7 +40,7 @@ List remote access configurations for a given device Use-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState } - $c8yargs = New-ClientArgument -Parameters $PSBoundParameters -Command "remoteaccess configuration list" + $c8yargs = New-ClientArgument -Parameters $PSBoundParameters -Command "remoteaccess configurations list" $ClientOptions = Get-ClientOutputOption $PSBoundParameters $TypeOptions = @{ Type = "" @@ -54,13 +54,13 @@ List remote access configurations for a given device if ($ClientOptions.ConvertToPS) { $Device ` | Group-ClientRequests ` - | c8y remoteaccess configuration list $c8yargs ` + | c8y remoteaccess configurations list $c8yargs ` | ConvertFrom-ClientOutput @TypeOptions } else { $Device ` | Group-ClientRequests ` - | c8y remoteaccess configuration list $c8yargs + | c8y remoteaccess configurations list $c8yargs } } diff --git a/tools/PSc8y/Public/New-RemoteAccessPassthroughConfiguration.ps1 b/tools/PSc8y/Public/New-RemoteAccessPassthroughConfiguration.ps1 index 0180c8080..1e9418ada 100644 --- a/tools/PSc8y/Public/New-RemoteAccessPassthroughConfiguration.ps1 +++ b/tools/PSc8y/Public/New-RemoteAccessPassthroughConfiguration.ps1 @@ -5,20 +5,27 @@ Function New-RemoteAccessPassthroughConfiguration { Create passthrough configuration .DESCRIPTION -Create passthrough configuration +Create a passthrough configuration which enables you to connect +directly to the device (via Cumulocity IoT) using a native client such as ssh. + +After a passthrough connection has been added, you can open a proxy to it using +one of the following commands: + + * c8y remoteaccess server + * c8y remoteaccess connect ssh .LINK -https://reubenmiller.github.io/go-c8y-cli/docs/cli/c8y/remoteaccess_configuration_create-passthrough +https://reubenmiller.github.io/go-c8y-cli/docs/cli/c8y/remoteaccess_configurations_create-passthrough .EXAMPLE -PS> New-RemoteAccessPassthroughConfiguration -Device mydevice +PS> New-RemoteAccessPassthroughConfiguration -Device device01 Create a SSH passthrough configuration to the localhost .EXAMPLE -PS> New-RemoteAccessPassthroughConfiguration -Device mydevice -Hostname customhost -Port 1234 -Name "My custom configuration" +PS> New-RemoteAccessPassthroughConfiguration -Device device01 -Hostname customhost -Port 1234 -Name "My custom configuration" Create a SSH passthrough configuration with custom details @@ -68,7 +75,7 @@ Create a SSH passthrough configuration with custom details Use-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState } - $c8yargs = New-ClientArgument -Parameters $PSBoundParameters -Command "remoteaccess configuration create-passthrough" + $c8yargs = New-ClientArgument -Parameters $PSBoundParameters -Command "remoteaccess configurations create-passthrough" $ClientOptions = Get-ClientOutputOption $PSBoundParameters $TypeOptions = @{ Type = "" @@ -82,13 +89,13 @@ Create a SSH passthrough configuration with custom details if ($ClientOptions.ConvertToPS) { $Device ` | Group-ClientRequests ` - | c8y remoteaccess configuration create-passthrough $c8yargs ` + | c8y remoteaccess configurations create-passthrough $c8yargs ` | ConvertFrom-ClientOutput @TypeOptions } else { $Device ` | Group-ClientRequests ` - | c8y remoteaccess configuration create-passthrough $c8yargs + | c8y remoteaccess configurations create-passthrough $c8yargs } } diff --git a/tools/PSc8y/Public/New-RemoteAccessTelnetConfiguration.ps1 b/tools/PSc8y/Public/New-RemoteAccessTelnetConfiguration.ps1 index a060d4207..93ceafca9 100644 --- a/tools/PSc8y/Public/New-RemoteAccessTelnetConfiguration.ps1 +++ b/tools/PSc8y/Public/New-RemoteAccessTelnetConfiguration.ps1 @@ -5,11 +5,12 @@ Function New-RemoteAccessTelnetConfiguration { Create telnet configuration .DESCRIPTION -Create telnet configuration +Create a new Telnet configuration. If no arguments are provided +then sensible defaults will be used. .LINK -https://reubenmiller.github.io/go-c8y-cli/docs/cli/c8y/remoteaccess_configuration_create-telnet +https://reubenmiller.github.io/go-c8y-cli/docs/cli/c8y/remoteaccess_configurations_create-telnet .EXAMPLE PS> New-RemoteAccessTelnetConfiguration @@ -67,7 +68,7 @@ Create a telnet configuration Use-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState } - $c8yargs = New-ClientArgument -Parameters $PSBoundParameters -Command "remoteaccess configuration create-telnet" + $c8yargs = New-ClientArgument -Parameters $PSBoundParameters -Command "remoteaccess configurations create-telnet" $ClientOptions = Get-ClientOutputOption $PSBoundParameters $TypeOptions = @{ Type = "" @@ -81,13 +82,13 @@ Create a telnet configuration if ($ClientOptions.ConvertToPS) { $Device ` | Group-ClientRequests ` - | c8y remoteaccess configuration create-telnet $c8yargs ` + | c8y remoteaccess configurations create-telnet $c8yargs ` | ConvertFrom-ClientOutput @TypeOptions } else { $Device ` | Group-ClientRequests ` - | c8y remoteaccess configuration create-telnet $c8yargs + | c8y remoteaccess configurations create-telnet $c8yargs } } diff --git a/tools/PSc8y/Public/New-RemoteAccessVNCConfiguration.ps1 b/tools/PSc8y/Public/New-RemoteAccessVNCConfiguration.ps1 index 4885841f5..5f5179ef9 100644 --- a/tools/PSc8y/Public/New-RemoteAccessVNCConfiguration.ps1 +++ b/tools/PSc8y/Public/New-RemoteAccessVNCConfiguration.ps1 @@ -5,11 +5,12 @@ Function New-RemoteAccessVNCConfiguration { Create vnc configuration .DESCRIPTION -Create vnc configuration +Create a new VNC configuration. If no arguments are provided +then sensible defaults will be used. .LINK -https://reubenmiller.github.io/go-c8y-cli/docs/cli/c8y/remoteaccess_configuration_create-vnc +https://reubenmiller.github.io/go-c8y-cli/docs/cli/c8y/remoteaccess_configurations_create-vnc .EXAMPLE PS> New-RemoteAccessVNCConfiguration @@ -72,7 +73,7 @@ Create a VNC configuration that requires a password Use-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState } - $c8yargs = New-ClientArgument -Parameters $PSBoundParameters -Command "remoteaccess configuration create-vnc" + $c8yargs = New-ClientArgument -Parameters $PSBoundParameters -Command "remoteaccess configurations create-vnc" $ClientOptions = Get-ClientOutputOption $PSBoundParameters $TypeOptions = @{ Type = "" @@ -86,13 +87,13 @@ Create a VNC configuration that requires a password if ($ClientOptions.ConvertToPS) { $Device ` | Group-ClientRequests ` - | c8y remoteaccess configuration create-vnc $c8yargs ` + | c8y remoteaccess configurations create-vnc $c8yargs ` | ConvertFrom-ClientOutput @TypeOptions } else { $Device ` | Group-ClientRequests ` - | c8y remoteaccess configuration create-vnc $c8yargs + | c8y remoteaccess configurations create-vnc $c8yargs } } diff --git a/tools/PSc8y/Public/New-RemoteAccessWebSSHConfiguration.ps1 b/tools/PSc8y/Public/New-RemoteAccessWebSSHConfiguration.ps1 index 8f526e82c..57a902122 100644 --- a/tools/PSc8y/Public/New-RemoteAccessWebSSHConfiguration.ps1 +++ b/tools/PSc8y/Public/New-RemoteAccessWebSSHConfiguration.ps1 @@ -5,11 +5,12 @@ Function New-RemoteAccessWebSSHConfiguration { Create web ssh configuration .DESCRIPTION -Create web ssh configuration +Create a new WebSSH configuration. If no arguments are provided +then sensible defaults will be used. .LINK -https://reubenmiller.github.io/go-c8y-cli/docs/cli/c8y/remoteaccess_configuration_create-webssh +https://reubenmiller.github.io/go-c8y-cli/docs/cli/c8y/remoteaccess_configurations_create-webssh .EXAMPLE PS> New-RemoteAccessWebSSHConfiguration @@ -93,7 +94,7 @@ Create a webssh configuration with a custom hostname and port Use-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState } - $c8yargs = New-ClientArgument -Parameters $PSBoundParameters -Command "remoteaccess configuration create-webssh" + $c8yargs = New-ClientArgument -Parameters $PSBoundParameters -Command "remoteaccess configurations create-webssh" $ClientOptions = Get-ClientOutputOption $PSBoundParameters $TypeOptions = @{ Type = "" @@ -107,13 +108,13 @@ Create a webssh configuration with a custom hostname and port if ($ClientOptions.ConvertToPS) { $Device ` | Group-ClientRequests ` - | c8y remoteaccess configuration create-webssh $c8yargs ` + | c8y remoteaccess configurations create-webssh $c8yargs ` | ConvertFrom-ClientOutput @TypeOptions } else { $Device ` | Group-ClientRequests ` - | c8y remoteaccess configuration create-webssh $c8yargs + | c8y remoteaccess configurations create-webssh $c8yargs } } diff --git a/tools/PSc8y/Public/Remove-RemoteAccessConfiguration.ps1 b/tools/PSc8y/Public/Remove-RemoteAccessConfiguration.ps1 index 56af72a9f..dcb667c3f 100644 --- a/tools/PSc8y/Public/Remove-RemoteAccessConfiguration.ps1 +++ b/tools/PSc8y/Public/Remove-RemoteAccessConfiguration.ps1 @@ -5,13 +5,13 @@ Function Remove-RemoteAccessConfiguration { Delete remote access configuration .DESCRIPTION -Delete remote access configuration +Delete an existing remote access configuration .LINK -https://reubenmiller.github.io/go-c8y-cli/docs/cli/c8y/remoteaccess_configuration_delete +https://reubenmiller.github.io/go-c8y-cli/docs/cli/c8y/remoteaccess_configurations_delete .EXAMPLE -PS> Remove-RemoteAccessConfiguration -Device mydevice -Id 1 +PS> Remove-RemoteAccessConfiguration -Device device01 -Id 1 Delete an existing remote access configuration @@ -44,7 +44,7 @@ Delete an existing remote access configuration Use-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState } - $c8yargs = New-ClientArgument -Parameters $PSBoundParameters -Command "remoteaccess configuration delete" + $c8yargs = New-ClientArgument -Parameters $PSBoundParameters -Command "remoteaccess configurations delete" $ClientOptions = Get-ClientOutputOption $PSBoundParameters $TypeOptions = @{ Type = "" @@ -58,13 +58,13 @@ Delete an existing remote access configuration if ($ClientOptions.ConvertToPS) { $Id ` | Group-ClientRequests ` - | c8y remoteaccess configuration delete $c8yargs ` + | c8y remoteaccess configurations delete $c8yargs ` | ConvertFrom-ClientOutput @TypeOptions } else { $Id ` | Group-ClientRequests ` - | c8y remoteaccess configuration delete $c8yargs + | c8y remoteaccess configurations delete $c8yargs } } diff --git a/tools/PSc8y/Tests/Get-RemoteAccessConfiguration.auto.Tests.ps1 b/tools/PSc8y/Tests/Get-RemoteAccessConfiguration.auto.Tests.ps1 index 6e730f489..37359bff8 100644 --- a/tools/PSc8y/Tests/Get-RemoteAccessConfiguration.auto.Tests.ps1 +++ b/tools/PSc8y/Tests/Get-RemoteAccessConfiguration.auto.Tests.ps1 @@ -6,7 +6,7 @@ Describe -Name "Get-RemoteAccessConfiguration" { } It "Get existing remote access configuration" { - $Response = PSc8y\Get-RemoteAccessConfiguration -Device mydevice -Id 1 + $Response = PSc8y\Get-RemoteAccessConfiguration -Device device01 -Id 1 $LASTEXITCODE | Should -Be 0 } diff --git a/tools/PSc8y/Tests/Get-RemoteAccessConfigurationCollection.auto.Tests.ps1 b/tools/PSc8y/Tests/Get-RemoteAccessConfigurationCollection.auto.Tests.ps1 index 57645b216..70b424838 100644 --- a/tools/PSc8y/Tests/Get-RemoteAccessConfigurationCollection.auto.Tests.ps1 +++ b/tools/PSc8y/Tests/Get-RemoteAccessConfigurationCollection.auto.Tests.ps1 @@ -6,7 +6,7 @@ Describe -Name "Get-RemoteAccessConfigurationCollection" { } It "List remote access configurations for a given device" { - $Response = PSc8y\Get-RemoteAccessConfigurationCollection -Device mydevice + $Response = PSc8y\Get-RemoteAccessConfigurationCollection -Device device01 $LASTEXITCODE | Should -Be 0 } diff --git a/tools/PSc8y/Tests/New-RemoteAccessPassthroughConfiguration.auto.Tests.ps1 b/tools/PSc8y/Tests/New-RemoteAccessPassthroughConfiguration.auto.Tests.ps1 index 011ca2dce..17214481c 100644 --- a/tools/PSc8y/Tests/New-RemoteAccessPassthroughConfiguration.auto.Tests.ps1 +++ b/tools/PSc8y/Tests/New-RemoteAccessPassthroughConfiguration.auto.Tests.ps1 @@ -6,13 +6,13 @@ Describe -Name "New-RemoteAccessPassthroughConfiguration" { } It "Create a SSH passthrough configuration to the localhost" { - $Response = PSc8y\New-RemoteAccessPassthroughConfiguration -Device mydevice + $Response = PSc8y\New-RemoteAccessPassthroughConfiguration -Device device01 $LASTEXITCODE | Should -Be 0 } It "Create a SSH passthrough configuration with custom details" { - $Response = PSc8y\New-RemoteAccessPassthroughConfiguration -Device mydevice -Hostname customhost -Port 1234 -Name "My custom configuration" + $Response = PSc8y\New-RemoteAccessPassthroughConfiguration -Device device01 -Hostname customhost -Port 1234 -Name "My custom configuration" $LASTEXITCODE | Should -Be 0 } diff --git a/tools/PSc8y/Tests/Remove-RemoteAccessConfiguration.auto.Tests.ps1 b/tools/PSc8y/Tests/Remove-RemoteAccessConfiguration.auto.Tests.ps1 index 4f1c245fe..59b5fd322 100644 --- a/tools/PSc8y/Tests/Remove-RemoteAccessConfiguration.auto.Tests.ps1 +++ b/tools/PSc8y/Tests/Remove-RemoteAccessConfiguration.auto.Tests.ps1 @@ -6,7 +6,7 @@ Describe -Name "Remove-RemoteAccessConfiguration" { } It "Delete an existing remote access configuration" { - $Response = PSc8y\Remove-RemoteAccessConfiguration -Device mydevice -Id 1 + $Response = PSc8y\Remove-RemoteAccessConfiguration -Device device01 -Id 1 $LASTEXITCODE | Should -Be 0 } From e9f4a7729a5a4baba9fe2acd59d2a784cee5f6eb Mon Sep 17 00:00:00 2001 From: Reuben Miller Date: Thu, 16 May 2024 11:25:12 +0200 Subject: [PATCH 11/23] update remote access examples --- api/spec/json/remoteAccessConfigurations.json | 156 +++++++++++++----- api/spec/yaml/remoteAccessConfigurations.yaml | 104 +++++++++--- .../create_telnet/create_telnet.auto.go | 4 +- .../create_vnc/create_vnc.auto.go | 6 +- .../create_webssh/create_webssh.auto.go | 18 +- .../configurations/update/update.auto.go | 2 +- .../configurations_create-passthrough.yaml | 4 +- .../configurations_create-telnet.yaml | 4 +- .../configurations_create-vnc.yaml | 8 +- .../configurations_create-webssh.yaml | 17 +- .../remoteaccess/configurations_delete.yaml | 2 +- .../remoteaccess/configurations_get.yaml | 2 +- .../remoteaccess/configurations_list.yaml | 2 +- .../remoteaccess/configurations_update.yaml | 6 +- .../New-RemoteAccessTelnetConfiguration.ps1 | 4 +- .../New-RemoteAccessVNCConfiguration.ps1 | 6 +- .../New-RemoteAccessWebSSHConfiguration.ps1 | 12 +- ...t-RemoteAccessConfiguration.auto.Tests.ps1 | 2 +- ...cessConfigurationCollection.auto.Tests.ps1 | 2 +- ...essPassthroughConfiguration.auto.Tests.ps1 | 4 +- ...teAccessTelnetConfiguration.auto.Tests.ps1 | 4 +- ...emoteAccessVNCConfiguration.auto.Tests.ps1 | 8 +- ...teAccessWebSSHConfiguration.auto.Tests.ps1 | 8 +- ...e-RemoteAccessConfiguration.auto.Tests.ps1 | 2 +- 24 files changed, 258 insertions(+), 129 deletions(-) diff --git a/api/spec/json/remoteAccessConfigurations.json b/api/spec/json/remoteAccessConfigurations.json index 09e00690e..d1118202e 100644 --- a/api/spec/json/remoteAccessConfigurations.json +++ b/api/spec/json/remoteAccessConfigurations.json @@ -19,13 +19,19 @@ "go": [ { "command": "c8y remoteaccess configurations list --device device01", - "description": "List remote access configurations for a given device" + "description": "List remote access configurations for a given device", + "assertStdOut": { + "json": { + "path": "r//service/remoteaccess/devices/\\d+/configurations$" + } + } } ], "powershell": [ { "command": "Get-RemoteAccessConfigurationCollection -Device device01", - "description": "List remote access configurations for a given device" + "description": "List remote access configurations for a given device", + "skipTest": true } ] }, @@ -53,13 +59,19 @@ "go": [ { "command": "c8y remoteaccess configurations get --device device01 --id 1", - "description": "Get existing remote access configuration" + "description": "Get existing remote access configuration", + "assertStdOut": { + "json": { + "path": "r//service/remoteaccess/devices/\\d+/configurations/1$" + } + } } ], "powershell": [ { "command": "Get-RemoteAccessConfiguration -Device device01 -Id 1", - "description": "Get existing remote access configuration" + "description": "Get existing remote access configuration", + "skipTest": true } ] }, @@ -90,14 +102,21 @@ "examples": { "go": [ { - "command": "c8y remoteaccess configurations update --device device01 --id 1", - "description": "Update an existing remote access configuration" + "command": "c8y remoteaccess configurations update --device device01 --id 1 --name hello", + "description": "Update an existing remote access configuration", + "assertStdOut": { + "json": { + "path": "r//service/remoteaccess/devices/\\d+/configurations/1$" + } + }, + "skipTest": true } ], "powershell": [ { - "command": "Update-RemoteAccessConfiguration -Device device01 -Id 1", - "description": "Update an existing remote access configuration" + "command": "Update-RemoteAccessConfiguration -Device device01 -Id 1 -Name hello", + "description": "Update an existing remote access configuration", + "skipTest": true } ] }, @@ -138,13 +157,19 @@ "go": [ { "command": "c8y remoteaccess configurations delete --device device01 --id 1", - "description": "Delete an existing remote access configuration" + "description": "Delete an existing remote access configuration", + "assertStdOut": { + "json": { + "path": "r//service/remoteaccess/devices/\\d+/configurations/1$" + } + } } ], "powershell": [ { "command": "Remove-RemoteAccessConfiguration -Device device01 -Id 1", - "description": "Delete an existing remote access configuration" + "description": "Delete an existing remote access configuration", + "skipTest": true } ] }, @@ -179,21 +204,33 @@ "go": [ { "command": "c8y remoteaccess configurations create-passthrough --device device01\n", - "description": "Create a SSH passthrough configuration to the localhost" + "description": "Create a SSH passthrough configuration to the localhost", + "assertStdOut": { + "json": { + "path": "r//service/remoteaccess/devices/\\d+/configurations$" + } + } }, { "command": "c8y remoteaccess configurations create-passthrough --device device01 --hostname customhost --port 1234 --name \"My custom configuration\"\n", - "description": "Create a SSH passthrough configuration with custom details" + "description": "Create a SSH passthrough configuration with custom details", + "assertStdOut": { + "json": { + "path": "r//service/remoteaccess/devices/\\d+/configurations$" + } + } } ], "powershell": [ { "command": "New-RemoteAccessPassthroughConfiguration -Device device01\n", - "description": "Create a SSH passthrough configuration to the localhost" + "description": "Create a SSH passthrough configuration to the localhost", + "skipTest": true }, { "command": "New-RemoteAccessPassthroughConfiguration -Device device01 -Hostname customhost -Port 1234 -Name \"My custom configuration\"\n", - "description": "Create a SSH passthrough configuration with custom details" + "description": "Create a SSH passthrough configuration with custom details", + "skipTest": true } ] }, @@ -260,22 +297,34 @@ "examples": { "go": [ { - "command": "c8y remoteaccess configurations create-webssh\n", - "description": "Create a webssh configuration" + "command": "c8y remoteaccess configurations create-webssh --device device01 --username admin --password \"3Xz7cEj%oAmt#dnUMP*N\"\n", + "description": "Create a webssh configuration (with username/password authentication)", + "assertStdOut": { + "json": { + "path": "r//service/remoteaccess/devices/\\d+/configurations$" + } + } }, { - "command": "c8y remoteaccess configurations create-webssh --hostname 127.0.0.1 --port 2222", - "description": "Create a webssh configuration with a custom hostname and port" + "command": "c8y remoteaccess configurations create-webssh --device device01 --hostname 127.0.0.1 --port 2222 --username admin --privateKey \"xxxx\" --publicKey \"yyyyy\"", + "description": "Create a webssh configuration with a custom hostname and port (with ssh key authentication)", + "assertStdOut": { + "json": { + "path": "r//service/remoteaccess/devices/\\d+/configurations$" + } + } } ], "powershell": [ { - "command": "New-RemoteAccessWebSSHConfiguration\n", - "description": "Create a webssh configuration" + "command": "New-RemoteAccessWebSSHConfiguration -Device device01 -Username admin -Password \"3Xz7cEj%oAmt#dnUMP*N\"\n", + "description": "Create a webssh configuration (with username/password authentication)", + "skipTest": true }, { - "command": "New-RemoteAccessWebSSHConfiguration -Hostname 127.0.0.1 -Port 2222", - "description": "Create a webssh configuration with a custom hostname and port" + "command": "New-RemoteAccessWebSSHConfiguration -Device device01 -Hostname 127.0.0.1 -Port 2222 -Username admin -PrivateKey \"xxxx\" -PublicKey \"yyyyy\"", + "description": "Create a webssh configuration with a custom hostname and port (with ssh key authentication)", + "skipTest": true } ] }, @@ -297,12 +346,14 @@ { "name": "hostname", "type": "string", - "description": "Hostname" + "description": "Hostname", + "default": "127.0.0.1" }, { "name": "port", "type": "integer", - "description": "Port" + "description": "Port", + "default": "22" }, { "name": "credentialsType", @@ -310,7 +361,9 @@ "description": "Credentials type", "default": "USER_PASS", "validationSet": [ - "USER_PASS" + "USER_PASS", + "KEY_PAIR", + "CERTIFICATE" ] }, { @@ -338,7 +391,6 @@ "type": "string", "default": "SSH", "validationSet": [ - "PASSTHROUGH", "SSH" ], "description": "Protocol" @@ -348,7 +400,8 @@ "hostname", "port", "protocol", - "name" + "name", + "credentialsType" ] }, { @@ -364,22 +417,34 @@ "examples": { "go": [ { - "command": "c8y remoteaccess configurations create-vnc\n", - "description": "Create a VNC configuration that does not require a password" + "command": "c8y remoteaccess configurations create-vnc --device device01\n", + "description": "Create a VNC configuration that does not require a password", + "assertStdOut": { + "json": { + "path": "r//service/remoteaccess/devices/\\d+/configurations$" + } + } }, { - "command": "c8y remoteaccess configurations create-vnc --password 'asd08dcj23dsf{@#9}'", - "description": "Create a VNC configuration that requires a password" + "command": "c8y remoteaccess configurations create-vnc --device device01 --password 'asd08dcj23dsf{@#9}'", + "description": "Create a VNC configuration that requires a password", + "assertStdOut": { + "json": { + "path": "r//service/remoteaccess/devices/\\d+/configurations$" + } + } } ], "powershell": [ { - "command": "New-RemoteAccessVNCConfiguration\n", - "description": "Create a VNC configuration that does not require a password" + "command": "New-RemoteAccessVNCConfiguration -Device device01\n", + "description": "Create a VNC configuration that does not require a password", + "skipTest": true }, { - "command": "New-RemoteAccessVNCConfiguration -Password 'asd08dcj23dsf{@#9}'", - "description": "Create a VNC configuration that requires a password" + "command": "New-RemoteAccessVNCConfiguration -Device device01 -Password 'asd08dcj23dsf{@#9}'", + "description": "Create a VNC configuration that requires a password", + "skipTest": true } ] }, @@ -420,8 +485,6 @@ "type": "string", "default": "VNC", "validationSet": [ - "PASSTHROUGH", - "SSH", "VNC" ], "description": "Protocol" @@ -454,14 +517,20 @@ "examples": { "go": [ { - "command": "c8y remoteaccess configurations create-telnet\n", - "description": "Create a telnet configuration" + "command": "c8y remoteaccess configurations create-telnet --device device01\n", + "description": "Create a telnet configuration", + "assertStdOut": { + "json": { + "path": "r//service/remoteaccess/devices/\\d+/configurations$" + } + } } ], "powershell": [ { - "command": "New-RemoteAccessTelnetConfiguration\n", - "description": "Create a telnet configuration" + "command": "New-RemoteAccessTelnetConfiguration -Device device01\n", + "description": "Create a telnet configuration", + "skipTest": true } ] }, @@ -504,10 +573,7 @@ "type": "string", "default": "TELNET", "validationSet": [ - "TELNET", - "PASSTHROUGH", - "SSH", - "VNC" + "TELNET" ], "description": "Protocol" } diff --git a/api/spec/yaml/remoteAccessConfigurations.yaml b/api/spec/yaml/remoteAccessConfigurations.yaml index 8e19cb07b..11af89368 100644 --- a/api/spec/yaml/remoteAccessConfigurations.yaml +++ b/api/spec/yaml/remoteAccessConfigurations.yaml @@ -19,9 +19,13 @@ commands: go: - command: c8y remoteaccess configurations list --device device01 description: List remote access configurations for a given device + assertStdOut: + json: + path: r//service/remoteaccess/devices/\d+/configurations$ powershell: - command: Get-RemoteAccessConfigurationCollection -Device device01 description: List remote access configurations for a given device + skipTest: true path: /service/remoteaccess/devices/{device}/configurations pathParameters: @@ -42,9 +46,13 @@ commands: go: - command: c8y remoteaccess configurations get --device device01 --id 1 description: Get existing remote access configuration + assertStdOut: + json: + path: r//service/remoteaccess/devices/\d+/configurations/1$ powershell: - command: Get-RemoteAccessConfiguration -Device device01 -Id 1 description: Get existing remote access configuration + skipTest: true pathParameters: - name: device type: device[] @@ -65,11 +73,16 @@ commands: powershell: Update-RemoteAccessConfiguration examples: go: - - command: c8y remoteaccess configurations update --device device01 --id 1 + - command: c8y remoteaccess configurations update --device device01 --id 1 --name hello description: Update an existing remote access configuration + assertStdOut: + json: + path: r//service/remoteaccess/devices/\d+/configurations/1$ + skipTest: true powershell: - - command: Update-RemoteAccessConfiguration -Device device01 -Id 1 + - command: Update-RemoteAccessConfiguration -Device device01 -Id 1 -Name hello description: Update an existing remote access configuration + skipTest: true method: PUT path: /service/remoteaccess/devices/{device}/configurations/{id} pathParameters: @@ -98,9 +111,13 @@ commands: go: - command: c8y remoteaccess configurations delete --device device01 --id 1 description: Delete an existing remote access configuration + assertStdOut: + json: + path: r//service/remoteaccess/devices/\d+/configurations/1$ powershell: - command: Remove-RemoteAccessConfiguration -Device device01 -Id 1 description: Delete an existing remote access configuration + skipTest: true method: DELETE path: /service/remoteaccess/devices/{device}/configurations/{id} pathParameters: @@ -136,16 +153,25 @@ commands: - command: | c8y remoteaccess configurations create-passthrough --device device01 description: Create a SSH passthrough configuration to the localhost + assertStdOut: + json: + path: r//service/remoteaccess/devices/\d+/configurations$ + - command: | c8y remoteaccess configurations create-passthrough --device device01 --hostname customhost --port 1234 --name "My custom configuration" description: Create a SSH passthrough configuration with custom details + assertStdOut: + json: + path: r//service/remoteaccess/devices/\d+/configurations$ powershell: - command: | New-RemoteAccessPassthroughConfiguration -Device device01 description: Create a SSH passthrough configuration to the localhost + skipTest: true - command: | New-RemoteAccessPassthroughConfiguration -Device device01 -Hostname customhost -Port 1234 -Name "My custom configuration" description: Create a SSH passthrough configuration with custom details + skipTest: true pathParameters: - name: device @@ -198,18 +224,26 @@ commands: examples: go: - command: | - c8y remoteaccess configurations create-webssh - description: Create a webssh configuration - - - command: c8y remoteaccess configurations create-webssh --hostname 127.0.0.1 --port 2222 - description: Create a webssh configuration with a custom hostname and port + c8y remoteaccess configurations create-webssh --device device01 --username admin --password "3Xz7cEj%oAmt#dnUMP*N" + description: Create a webssh configuration (with username/password authentication) + assertStdOut: + json: + path: r//service/remoteaccess/devices/\d+/configurations$ + + - command: c8y remoteaccess configurations create-webssh --device device01 --hostname 127.0.0.1 --port 2222 --username admin --privateKey "xxxx" --publicKey "yyyyy" + description: Create a webssh configuration with a custom hostname and port (with ssh key authentication) + assertStdOut: + json: + path: r//service/remoteaccess/devices/\d+/configurations$ powershell: - command: | - New-RemoteAccessWebSSHConfiguration - description: Create a webssh configuration + New-RemoteAccessWebSSHConfiguration -Device device01 -Username admin -Password "3Xz7cEj%oAmt#dnUMP*N" + description: Create a webssh configuration (with username/password authentication) + skipTest: true - - command: New-RemoteAccessWebSSHConfiguration -Hostname 127.0.0.1 -Port 2222 - description: Create a webssh configuration with a custom hostname and port + - command: New-RemoteAccessWebSSHConfiguration -Device device01 -Hostname 127.0.0.1 -Port 2222 -Username admin -PrivateKey "xxxx" -PublicKey "yyyyy" + description: Create a webssh configuration with a custom hostname and port (with ssh key authentication) + skipTest: true pathParameters: - name: device type: device[] @@ -224,10 +258,12 @@ commands: - name: hostname type: string description: Hostname + default: "127.0.0.1" - name: port type: integer description: Port + default: "22" - name: credentialsType type: string @@ -235,7 +271,9 @@ commands: default: USER_PASS validationSet: - USER_PASS - + - KEY_PAIR + - CERTIFICATE + - name: privateKey type: string description: Private ssh key @@ -256,7 +294,6 @@ commands: type: string default: SSH validationSet: - - PASSTHROUGH - SSH description: Protocol @@ -265,6 +302,18 @@ commands: - port - protocol - name + - credentialsType + # bodyTemplates: + # - type: jsonnet + # template: | + + # { + # local credType = if std.objectHas($, "privateKey") && std.objectHas($, "publicKey") then "USER_PASS" else if std.objectHas($, "password") else, + # local credValue = { + + # }, + # credentialsType: credType + # } - name: create-vnc description: Create vnc configuration @@ -279,18 +328,26 @@ commands: examples: go: - command: | - c8y remoteaccess configurations create-vnc + c8y remoteaccess configurations create-vnc --device device01 description: Create a VNC configuration that does not require a password + assertStdOut: + json: + path: r//service/remoteaccess/devices/\d+/configurations$ - - command: c8y remoteaccess configurations create-vnc --password 'asd08dcj23dsf{@#9}' + - command: c8y remoteaccess configurations create-vnc --device device01 --password 'asd08dcj23dsf{@#9}' description: Create a VNC configuration that requires a password + assertStdOut: + json: + path: r//service/remoteaccess/devices/\d+/configurations$ powershell: - command: | - New-RemoteAccessVNCConfiguration + New-RemoteAccessVNCConfiguration -Device device01 description: Create a VNC configuration that does not require a password + skipTest: true - - command: New-RemoteAccessVNCConfiguration -Password 'asd08dcj23dsf{@#9}' + - command: New-RemoteAccessVNCConfiguration -Device device01 -Password 'asd08dcj23dsf{@#9}' description: Create a VNC configuration that requires a password + skipTest: true pathParameters: - name: device type: device[] @@ -320,8 +377,6 @@ commands: type: string default: VNC validationSet: - - PASSTHROUGH - - SSH - VNC description: Protocol @@ -349,12 +404,16 @@ commands: examples: go: - command: | - c8y remoteaccess configurations create-telnet + c8y remoteaccess configurations create-telnet --device device01 description: Create a telnet configuration + assertStdOut: + json: + path: r//service/remoteaccess/devices/\d+/configurations$ powershell: - command: | - New-RemoteAccessTelnetConfiguration + New-RemoteAccessTelnetConfiguration -Device device01 description: Create a telnet configuration + skipTest: true pathParameters: - name: device @@ -388,9 +447,6 @@ commands: default: TELNET validationSet: - TELNET - - PASSTHROUGH - - SSH - - VNC description: Protocol bodyRequiredKeys: diff --git a/pkg/cmd/remoteaccess/configurations/create_telnet/create_telnet.auto.go b/pkg/cmd/remoteaccess/configurations/create_telnet/create_telnet.auto.go index 70b46f4ed..e8f2e4ea6 100644 --- a/pkg/cmd/remoteaccess/configurations/create_telnet/create_telnet.auto.go +++ b/pkg/cmd/remoteaccess/configurations/create_telnet/create_telnet.auto.go @@ -36,7 +36,7 @@ func NewCreateTelnetCmd(f *cmdutil.Factory) *CreateTelnetCmd { then sensible defaults will be used. `, Example: heredoc.Doc(` -$ c8y remoteaccess configurations create-telnet +$ c8y remoteaccess configurations create-telnet --device device01 Create a telnet configuration `), PreRunE: func(cmd *cobra.Command, args []string) error { @@ -57,7 +57,7 @@ Create a telnet configuration completion.WithOptions( cmd, completion.WithDevice("device", func() (*c8y.Client, error) { return ccmd.factory.Client() }), - completion.WithValidateSet("protocol", "TELNET", "PASSTHROUGH", "SSH", "VNC"), + completion.WithValidateSet("protocol", "TELNET"), ) flags.WithOptions( diff --git a/pkg/cmd/remoteaccess/configurations/create_vnc/create_vnc.auto.go b/pkg/cmd/remoteaccess/configurations/create_vnc/create_vnc.auto.go index fbc7f3ec3..058340d2e 100644 --- a/pkg/cmd/remoteaccess/configurations/create_vnc/create_vnc.auto.go +++ b/pkg/cmd/remoteaccess/configurations/create_vnc/create_vnc.auto.go @@ -36,10 +36,10 @@ func NewCreateVncCmd(f *cmdutil.Factory) *CreateVncCmd { then sensible defaults will be used. `, Example: heredoc.Doc(` -$ c8y remoteaccess configurations create-vnc +$ c8y remoteaccess configurations create-vnc --device device01 Create a VNC configuration that does not require a password -$ c8y remoteaccess configurations create-vnc --password 'asd08dcj23dsf{@#9}' +$ c8y remoteaccess configurations create-vnc --device device01 --password 'asd08dcj23dsf{@#9}' Create a VNC configuration that requires a password `), PreRunE: func(cmd *cobra.Command, args []string) error { @@ -60,7 +60,7 @@ Create a VNC configuration that requires a password completion.WithOptions( cmd, completion.WithDevice("device", func() (*c8y.Client, error) { return ccmd.factory.Client() }), - completion.WithValidateSet("protocol", "PASSTHROUGH", "SSH", "VNC"), + completion.WithValidateSet("protocol", "VNC"), ) flags.WithOptions( diff --git a/pkg/cmd/remoteaccess/configurations/create_webssh/create_webssh.auto.go b/pkg/cmd/remoteaccess/configurations/create_webssh/create_webssh.auto.go index cef989488..7e4d6b33e 100644 --- a/pkg/cmd/remoteaccess/configurations/create_webssh/create_webssh.auto.go +++ b/pkg/cmd/remoteaccess/configurations/create_webssh/create_webssh.auto.go @@ -36,11 +36,11 @@ func NewCreateWebsshCmd(f *cmdutil.Factory) *CreateWebsshCmd { then sensible defaults will be used. `, Example: heredoc.Doc(` -$ c8y remoteaccess configurations create-webssh -Create a webssh configuration +$ c8y remoteaccess configurations create-webssh --device device01 --username admin --password "3Xz7cEj%oAmt#dnUMP*N" +Create a webssh configuration (with username/password authentication) -$ c8y remoteaccess configurations create-webssh --hostname 127.0.0.1 --port 2222 -Create a webssh configuration with a custom hostname and port +$ c8y remoteaccess configurations create-webssh --device device01 --hostname 127.0.0.1 --port 2222 --username admin --privateKey "xxxx" --publicKey "yyyyy" +Create a webssh configuration with a custom hostname and port (with ssh key authentication) `), PreRunE: func(cmd *cobra.Command, args []string) error { return f.CreateModeEnabled() @@ -52,8 +52,8 @@ Create a webssh configuration with a custom hostname and port cmd.Flags().StringSlice("device", []string{""}, "Device (accepts pipeline)") cmd.Flags().String("name", "webssh", "Connection name") - cmd.Flags().String("hostname", "", "Hostname") - cmd.Flags().Int("port", 0, "Port") + cmd.Flags().String("hostname", "127.0.0.1", "Hostname") + cmd.Flags().Int("port", 22, "Port") cmd.Flags().String("credentialsType", "USER_PASS", "Credentials type") cmd.Flags().String("privateKey", "", "Private ssh key") cmd.Flags().String("publicKey", "", "Public ssh key") @@ -64,8 +64,8 @@ Create a webssh configuration with a custom hostname and port completion.WithOptions( cmd, completion.WithDevice("device", func() (*c8y.Client, error) { return ccmd.factory.Client() }), - completion.WithValidateSet("credentialsType", "USER_PASS"), - completion.WithValidateSet("protocol", "PASSTHROUGH", "SSH"), + completion.WithValidateSet("credentialsType", "USER_PASS", "KEY_PAIR", "CERTIFICATE"), + completion.WithValidateSet("protocol", "SSH"), ) flags.WithOptions( @@ -163,7 +163,7 @@ func (n *CreateWebsshCmd) RunE(cmd *cobra.Command, args []string) error { flags.WithStringValue("protocol", "protocol"), cmdutil.WithTemplateValue(n.factory), flags.WithTemplateVariablesValue(), - flags.WithRequiredProperties("hostname", "port", "protocol", "name"), + flags.WithRequiredProperties("hostname", "port", "protocol", "name", "credentialsType"), ) if err != nil { return cmderrors.NewUserError(err) diff --git a/pkg/cmd/remoteaccess/configurations/update/update.auto.go b/pkg/cmd/remoteaccess/configurations/update/update.auto.go index e2afe4149..f8a2644b3 100644 --- a/pkg/cmd/remoteaccess/configurations/update/update.auto.go +++ b/pkg/cmd/remoteaccess/configurations/update/update.auto.go @@ -36,7 +36,7 @@ func NewUpdateCmd(f *cmdutil.Factory) *UpdateCmd { Hidden: true, Example: heredoc.Doc(` -$ c8y remoteaccess configurations update --device device01 --id 1 +$ c8y remoteaccess configurations update --device device01 --id 1 --name hello Update an existing remote access configuration `), PreRunE: func(cmd *cobra.Command, args []string) error { diff --git a/tests/auto/remoteaccessconfigurations/tests/remoteaccess/configurations_create-passthrough.yaml b/tests/auto/remoteaccessconfigurations/tests/remoteaccess/configurations_create-passthrough.yaml index 9b8464029..32a2ae477 100644 --- a/tests/auto/remoteaccessconfigurations/tests/remoteaccess/configurations_create-passthrough.yaml +++ b/tests/auto/remoteaccessconfigurations/tests/remoteaccess/configurations_create-passthrough.yaml @@ -6,7 +6,7 @@ tests: stdout: json: method: POST - path: /service/remoteaccess/devices/device01/configurations + path: r//service/remoteaccess/devices/\d+/configurations$ remoteaccess/configurations_create-passthrough_Create a SSH passthrough configuration with custom details: command: | c8y remoteaccess configurations create-passthrough --device device01 --hostname customhost --port 1234 --name "My custom configuration" @@ -17,4 +17,4 @@ tests: body.name: My custom configuration body.port: "1234" method: POST - path: /service/remoteaccess/devices/device01/configurations + path: r//service/remoteaccess/devices/\d+/configurations$ diff --git a/tests/auto/remoteaccessconfigurations/tests/remoteaccess/configurations_create-telnet.yaml b/tests/auto/remoteaccessconfigurations/tests/remoteaccess/configurations_create-telnet.yaml index f4eb9fbc4..9b7f8b807 100644 --- a/tests/auto/remoteaccessconfigurations/tests/remoteaccess/configurations_create-telnet.yaml +++ b/tests/auto/remoteaccessconfigurations/tests/remoteaccess/configurations_create-telnet.yaml @@ -1,9 +1,9 @@ tests: remoteaccess/configurations_create-telnet_Create a telnet configuration: command: | - c8y remoteaccess configurations create-telnet + c8y remoteaccess configurations create-telnet --device device01 exit-code: 0 stdout: json: method: POST - path: /service/remoteaccess/devices/{device}/configurations + path: r//service/remoteaccess/devices/\d+/configurations$ diff --git a/tests/auto/remoteaccessconfigurations/tests/remoteaccess/configurations_create-vnc.yaml b/tests/auto/remoteaccessconfigurations/tests/remoteaccess/configurations_create-vnc.yaml index d6944e056..71806b70d 100644 --- a/tests/auto/remoteaccessconfigurations/tests/remoteaccess/configurations_create-vnc.yaml +++ b/tests/auto/remoteaccessconfigurations/tests/remoteaccess/configurations_create-vnc.yaml @@ -1,17 +1,17 @@ tests: remoteaccess/configurations_create-vnc_Create a VNC configuration that does not require a password: command: | - c8y remoteaccess configurations create-vnc + c8y remoteaccess configurations create-vnc --device device01 exit-code: 0 stdout: json: method: POST - path: /service/remoteaccess/devices/{device}/configurations + path: r//service/remoteaccess/devices/\d+/configurations$ remoteaccess/configurations_create-vnc_Create a VNC configuration that requires a password: - command: c8y remoteaccess configurations create-vnc --password 'asd08dcj23dsf{@#9}' + command: c8y remoteaccess configurations create-vnc --device device01 --password 'asd08dcj23dsf{@#9}' exit-code: 0 stdout: json: body.password: asd08dcj23dsf{@#9} method: POST - path: /service/remoteaccess/devices/{device}/configurations + path: r//service/remoteaccess/devices/\d+/configurations$ diff --git a/tests/auto/remoteaccessconfigurations/tests/remoteaccess/configurations_create-webssh.yaml b/tests/auto/remoteaccessconfigurations/tests/remoteaccess/configurations_create-webssh.yaml index 20262713f..ebcf5d295 100644 --- a/tests/auto/remoteaccessconfigurations/tests/remoteaccess/configurations_create-webssh.yaml +++ b/tests/auto/remoteaccessconfigurations/tests/remoteaccess/configurations_create-webssh.yaml @@ -1,18 +1,23 @@ tests: - remoteaccess/configurations_create-webssh_Create a webssh configuration: + remoteaccess/configurations_create-webssh_Create a webssh configuration (with username/password authentication): command: | - c8y remoteaccess configurations create-webssh + c8y remoteaccess configurations create-webssh --device device01 --username admin --password "3Xz7cEj%oAmt#dnUMP*N" exit-code: 0 stdout: json: + body.password: 3Xz7cEj%oAmt#dnUMP*N + body.username: admin method: POST - path: /service/remoteaccess/devices/{device}/configurations - remoteaccess/configurations_create-webssh_Create a webssh configuration with a custom hostname and port: - command: c8y remoteaccess configurations create-webssh --hostname 127.0.0.1 --port 2222 + path: r//service/remoteaccess/devices/\d+/configurations$ + ? remoteaccess/configurations_create-webssh_Create a webssh configuration with a custom hostname and port (with ssh key authentication) + : command: c8y remoteaccess configurations create-webssh --device device01 --hostname 127.0.0.1 --port 2222 --username admin --privateKey "xxxx" --publicKey "yyyyy" exit-code: 0 stdout: json: body.hostname: 127.0.0.1 body.port: "2222" + body.privateKey: xxxx + body.publicKey: yyyyy + body.username: admin method: POST - path: /service/remoteaccess/devices/{device}/configurations + path: r//service/remoteaccess/devices/\d+/configurations$ diff --git a/tests/auto/remoteaccessconfigurations/tests/remoteaccess/configurations_delete.yaml b/tests/auto/remoteaccessconfigurations/tests/remoteaccess/configurations_delete.yaml index 84a487cd2..d250e06b4 100644 --- a/tests/auto/remoteaccessconfigurations/tests/remoteaccess/configurations_delete.yaml +++ b/tests/auto/remoteaccessconfigurations/tests/remoteaccess/configurations_delete.yaml @@ -5,4 +5,4 @@ tests: stdout: json: method: DELETE - path: /service/remoteaccess/devices/device01/configurations/1 + path: r//service/remoteaccess/devices/\d+/configurations/1$ diff --git a/tests/auto/remoteaccessconfigurations/tests/remoteaccess/configurations_get.yaml b/tests/auto/remoteaccessconfigurations/tests/remoteaccess/configurations_get.yaml index 962ca9d53..906730817 100644 --- a/tests/auto/remoteaccessconfigurations/tests/remoteaccess/configurations_get.yaml +++ b/tests/auto/remoteaccessconfigurations/tests/remoteaccess/configurations_get.yaml @@ -5,4 +5,4 @@ tests: stdout: json: method: GET - path: /service/remoteaccess/devices/device01/configurations/1 + path: r//service/remoteaccess/devices/\d+/configurations/1$ diff --git a/tests/auto/remoteaccessconfigurations/tests/remoteaccess/configurations_list.yaml b/tests/auto/remoteaccessconfigurations/tests/remoteaccess/configurations_list.yaml index 38c49b8e6..e27aa7e88 100644 --- a/tests/auto/remoteaccessconfigurations/tests/remoteaccess/configurations_list.yaml +++ b/tests/auto/remoteaccessconfigurations/tests/remoteaccess/configurations_list.yaml @@ -5,4 +5,4 @@ tests: stdout: json: method: GET - path: /service/remoteaccess/devices/device01/configurations + path: r//service/remoteaccess/devices/\d+/configurations$ diff --git a/tests/auto/remoteaccessconfigurations/tests/remoteaccess/configurations_update.yaml b/tests/auto/remoteaccessconfigurations/tests/remoteaccess/configurations_update.yaml index d97cea95f..13f48141c 100644 --- a/tests/auto/remoteaccessconfigurations/tests/remoteaccess/configurations_update.yaml +++ b/tests/auto/remoteaccessconfigurations/tests/remoteaccess/configurations_update.yaml @@ -1,8 +1,10 @@ tests: remoteaccess/configurations_update_Update an existing remote access configuration: - command: c8y remoteaccess configurations update --device device01 --id 1 + command: c8y remoteaccess configurations update --device device01 --id 1 --name hello exit-code: 0 + skip: true stdout: json: + body.name: hello method: PUT - path: /service/remoteaccess/devices/device01/configurations/1 + path: r//service/remoteaccess/devices/\d+/configurations/1$ diff --git a/tools/PSc8y/Public/New-RemoteAccessTelnetConfiguration.ps1 b/tools/PSc8y/Public/New-RemoteAccessTelnetConfiguration.ps1 index 93ceafca9..d5241c5dd 100644 --- a/tools/PSc8y/Public/New-RemoteAccessTelnetConfiguration.ps1 +++ b/tools/PSc8y/Public/New-RemoteAccessTelnetConfiguration.ps1 @@ -13,7 +13,7 @@ then sensible defaults will be used. https://reubenmiller.github.io/go-c8y-cli/docs/cli/c8y/remoteaccess_configurations_create-telnet .EXAMPLE -PS> New-RemoteAccessTelnetConfiguration +PS> New-RemoteAccessTelnetConfiguration -Device device01 Create a telnet configuration @@ -53,7 +53,7 @@ Create a telnet configuration # Protocol [Parameter()] - [ValidateSet('TELNET','PASSTHROUGH','SSH','VNC')] + [ValidateSet('TELNET')] [string] $Protocol ) diff --git a/tools/PSc8y/Public/New-RemoteAccessVNCConfiguration.ps1 b/tools/PSc8y/Public/New-RemoteAccessVNCConfiguration.ps1 index 5f5179ef9..739001ba6 100644 --- a/tools/PSc8y/Public/New-RemoteAccessVNCConfiguration.ps1 +++ b/tools/PSc8y/Public/New-RemoteAccessVNCConfiguration.ps1 @@ -13,13 +13,13 @@ then sensible defaults will be used. https://reubenmiller.github.io/go-c8y-cli/docs/cli/c8y/remoteaccess_configurations_create-vnc .EXAMPLE -PS> New-RemoteAccessVNCConfiguration +PS> New-RemoteAccessVNCConfiguration -Device device01 Create a VNC configuration that does not require a password .EXAMPLE -PS> New-RemoteAccessVNCConfiguration -Password 'asd08dcj23dsf{@#9}' +PS> New-RemoteAccessVNCConfiguration -Device device01 -Password 'asd08dcj23dsf{@#9}' Create a VNC configuration that requires a password @@ -58,7 +58,7 @@ Create a VNC configuration that requires a password # Protocol [Parameter()] - [ValidateSet('PASSTHROUGH','SSH','VNC')] + [ValidateSet('VNC')] [string] $Protocol ) diff --git a/tools/PSc8y/Public/New-RemoteAccessWebSSHConfiguration.ps1 b/tools/PSc8y/Public/New-RemoteAccessWebSSHConfiguration.ps1 index 57a902122..5e91a5310 100644 --- a/tools/PSc8y/Public/New-RemoteAccessWebSSHConfiguration.ps1 +++ b/tools/PSc8y/Public/New-RemoteAccessWebSSHConfiguration.ps1 @@ -13,15 +13,15 @@ then sensible defaults will be used. https://reubenmiller.github.io/go-c8y-cli/docs/cli/c8y/remoteaccess_configurations_create-webssh .EXAMPLE -PS> New-RemoteAccessWebSSHConfiguration +PS> New-RemoteAccessWebSSHConfiguration -Device device01 -Username admin -Password "3Xz7cEj%oAmt#dnUMP*N" -Create a webssh configuration +Create a webssh configuration (with username/password authentication) .EXAMPLE -PS> New-RemoteAccessWebSSHConfiguration -Hostname 127.0.0.1 -Port 2222 +PS> New-RemoteAccessWebSSHConfiguration -Device device01 -Hostname 127.0.0.1 -Port 2222 -Username admin -PrivateKey "xxxx" -PublicKey "yyyyy" -Create a webssh configuration with a custom hostname and port +Create a webssh configuration with a custom hostname and port (with ssh key authentication) #> @@ -53,7 +53,7 @@ Create a webssh configuration with a custom hostname and port # Credentials type [Parameter()] - [ValidateSet('USER_PASS')] + [ValidateSet('USER_PASS','KEY_PAIR','CERTIFICATE')] [string] $CredentialsType, @@ -79,7 +79,7 @@ Create a webssh configuration with a custom hostname and port # Protocol [Parameter()] - [ValidateSet('PASSTHROUGH','SSH')] + [ValidateSet('SSH')] [string] $Protocol ) diff --git a/tools/PSc8y/Tests/Get-RemoteAccessConfiguration.auto.Tests.ps1 b/tools/PSc8y/Tests/Get-RemoteAccessConfiguration.auto.Tests.ps1 index 37359bff8..d0528840b 100644 --- a/tools/PSc8y/Tests/Get-RemoteAccessConfiguration.auto.Tests.ps1 +++ b/tools/PSc8y/Tests/Get-RemoteAccessConfiguration.auto.Tests.ps1 @@ -5,7 +5,7 @@ Describe -Name "Get-RemoteAccessConfiguration" { } - It "Get existing remote access configuration" { + It -Skip "Get existing remote access configuration" { $Response = PSc8y\Get-RemoteAccessConfiguration -Device device01 -Id 1 $LASTEXITCODE | Should -Be 0 } diff --git a/tools/PSc8y/Tests/Get-RemoteAccessConfigurationCollection.auto.Tests.ps1 b/tools/PSc8y/Tests/Get-RemoteAccessConfigurationCollection.auto.Tests.ps1 index 70b424838..103c6dd81 100644 --- a/tools/PSc8y/Tests/Get-RemoteAccessConfigurationCollection.auto.Tests.ps1 +++ b/tools/PSc8y/Tests/Get-RemoteAccessConfigurationCollection.auto.Tests.ps1 @@ -5,7 +5,7 @@ Describe -Name "Get-RemoteAccessConfigurationCollection" { } - It "List remote access configurations for a given device" { + It -Skip "List remote access configurations for a given device" { $Response = PSc8y\Get-RemoteAccessConfigurationCollection -Device device01 $LASTEXITCODE | Should -Be 0 } diff --git a/tools/PSc8y/Tests/New-RemoteAccessPassthroughConfiguration.auto.Tests.ps1 b/tools/PSc8y/Tests/New-RemoteAccessPassthroughConfiguration.auto.Tests.ps1 index 17214481c..99b13a7a9 100644 --- a/tools/PSc8y/Tests/New-RemoteAccessPassthroughConfiguration.auto.Tests.ps1 +++ b/tools/PSc8y/Tests/New-RemoteAccessPassthroughConfiguration.auto.Tests.ps1 @@ -5,13 +5,13 @@ Describe -Name "New-RemoteAccessPassthroughConfiguration" { } - It "Create a SSH passthrough configuration to the localhost" { + It -Skip "Create a SSH passthrough configuration to the localhost" { $Response = PSc8y\New-RemoteAccessPassthroughConfiguration -Device device01 $LASTEXITCODE | Should -Be 0 } - It "Create a SSH passthrough configuration with custom details" { + It -Skip "Create a SSH passthrough configuration with custom details" { $Response = PSc8y\New-RemoteAccessPassthroughConfiguration -Device device01 -Hostname customhost -Port 1234 -Name "My custom configuration" $LASTEXITCODE | Should -Be 0 diff --git a/tools/PSc8y/Tests/New-RemoteAccessTelnetConfiguration.auto.Tests.ps1 b/tools/PSc8y/Tests/New-RemoteAccessTelnetConfiguration.auto.Tests.ps1 index d9368a988..f8a6fa68f 100644 --- a/tools/PSc8y/Tests/New-RemoteAccessTelnetConfiguration.auto.Tests.ps1 +++ b/tools/PSc8y/Tests/New-RemoteAccessTelnetConfiguration.auto.Tests.ps1 @@ -5,8 +5,8 @@ Describe -Name "New-RemoteAccessTelnetConfiguration" { } - It "Create a telnet configuration" { - $Response = PSc8y\New-RemoteAccessTelnetConfiguration + It -Skip "Create a telnet configuration" { + $Response = PSc8y\New-RemoteAccessTelnetConfiguration -Device device01 $LASTEXITCODE | Should -Be 0 } diff --git a/tools/PSc8y/Tests/New-RemoteAccessVNCConfiguration.auto.Tests.ps1 b/tools/PSc8y/Tests/New-RemoteAccessVNCConfiguration.auto.Tests.ps1 index 91deb8e8e..af77901f2 100644 --- a/tools/PSc8y/Tests/New-RemoteAccessVNCConfiguration.auto.Tests.ps1 +++ b/tools/PSc8y/Tests/New-RemoteAccessVNCConfiguration.auto.Tests.ps1 @@ -5,14 +5,14 @@ Describe -Name "New-RemoteAccessVNCConfiguration" { } - It "Create a VNC configuration that does not require a password" { - $Response = PSc8y\New-RemoteAccessVNCConfiguration + It -Skip "Create a VNC configuration that does not require a password" { + $Response = PSc8y\New-RemoteAccessVNCConfiguration -Device device01 $LASTEXITCODE | Should -Be 0 } - It "Create a VNC configuration that requires a password" { - $Response = PSc8y\New-RemoteAccessVNCConfiguration -Password 'asd08dcj23dsf{@#9}' + It -Skip "Create a VNC configuration that requires a password" { + $Response = PSc8y\New-RemoteAccessVNCConfiguration -Device device01 -Password 'asd08dcj23dsf{@#9}' $LASTEXITCODE | Should -Be 0 } diff --git a/tools/PSc8y/Tests/New-RemoteAccessWebSSHConfiguration.auto.Tests.ps1 b/tools/PSc8y/Tests/New-RemoteAccessWebSSHConfiguration.auto.Tests.ps1 index 2a40a444b..81af93841 100644 --- a/tools/PSc8y/Tests/New-RemoteAccessWebSSHConfiguration.auto.Tests.ps1 +++ b/tools/PSc8y/Tests/New-RemoteAccessWebSSHConfiguration.auto.Tests.ps1 @@ -5,14 +5,14 @@ Describe -Name "New-RemoteAccessWebSSHConfiguration" { } - It "Create a webssh configuration" { - $Response = PSc8y\New-RemoteAccessWebSSHConfiguration + It -Skip "Create a webssh configuration (with username/password authentication)" { + $Response = PSc8y\New-RemoteAccessWebSSHConfiguration -Device device01 -Username admin -Password "3Xz7cEj%oAmt#dnUMP*N" $LASTEXITCODE | Should -Be 0 } - It "Create a webssh configuration with a custom hostname and port" { - $Response = PSc8y\New-RemoteAccessWebSSHConfiguration -Hostname 127.0.0.1 -Port 2222 + It -Skip "Create a webssh configuration with a custom hostname and port (with ssh key authentication)" { + $Response = PSc8y\New-RemoteAccessWebSSHConfiguration -Device device01 -Hostname 127.0.0.1 -Port 2222 -Username admin -PrivateKey "xxxx" -PublicKey "yyyyy" $LASTEXITCODE | Should -Be 0 } diff --git a/tools/PSc8y/Tests/Remove-RemoteAccessConfiguration.auto.Tests.ps1 b/tools/PSc8y/Tests/Remove-RemoteAccessConfiguration.auto.Tests.ps1 index 59b5fd322..a8079fcf5 100644 --- a/tools/PSc8y/Tests/Remove-RemoteAccessConfiguration.auto.Tests.ps1 +++ b/tools/PSc8y/Tests/Remove-RemoteAccessConfiguration.auto.Tests.ps1 @@ -5,7 +5,7 @@ Describe -Name "Remove-RemoteAccessConfiguration" { } - It "Delete an existing remote access configuration" { + It -Skip "Delete an existing remote access configuration" { $Response = PSc8y\Remove-RemoteAccessConfiguration -Device device01 -Id 1 $LASTEXITCODE | Should -Be 0 } From 0f17a6c686e8e5c938bf5905e403150744c6f919 Mon Sep 17 00:00:00 2001 From: Reuben Miller Date: Fri, 17 May 2024 21:21:58 +0200 Subject: [PATCH 12/23] cleanup code --- go.mod | 2 +- go.sum | 4 +-- .../remoteaccess/connect/ssh/ssh.manual.go | 36 ++++++------------- pkg/cmd/remoteaccess/server/server.manual.go | 25 ++++++------- 4 files changed, 25 insertions(+), 42 deletions(-) diff --git a/go.mod b/go.mod index 5c600c9d3..593ffaf76 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,7 @@ require ( github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0 github.com/pkg/errors v0.9.1 github.com/pquerna/otp v1.4.0 - github.com/reubenmiller/go-c8y v0.15.11-0.20240514185826-c88bf3005666 + github.com/reubenmiller/go-c8y v0.15.11-0.20240517191142-ee60c4663b16 github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 github.com/sergi/go-diff v1.3.1 // indirect github.com/sethvargo/go-password v0.2.0 diff --git a/go.sum b/go.sum index b5c64fcbc..0ec338101 100644 --- a/go.sum +++ b/go.sum @@ -155,8 +155,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pquerna/otp v1.4.0 h1:wZvl1TIVxKRThZIBiwOOHOGP/1+nZyWBil9Y2XNEDzg= github.com/pquerna/otp v1.4.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg= -github.com/reubenmiller/go-c8y v0.15.11-0.20240514185826-c88bf3005666 h1:alyiZwR5MIbbHiefnN/AKZs462+V6TvlmiGQnPa8y/4= -github.com/reubenmiller/go-c8y v0.15.11-0.20240514185826-c88bf3005666/go.mod h1:5F2Z5V0A1dkaSva48iVxLUuDE866rvSe8zi3fAPxZ6Y= +github.com/reubenmiller/go-c8y v0.15.11-0.20240517191142-ee60c4663b16 h1:sVQJbpqkp35U8OJgSKv1dEendhMYk5JgWQ97tHwDP5Q= +github.com/reubenmiller/go-c8y v0.15.11-0.20240517191142-ee60c4663b16/go.mod h1:5F2Z5V0A1dkaSva48iVxLUuDE866rvSe8zi3fAPxZ6Y= github.com/reubenmiller/gojsonq/v2 v2.0.0-20221119213524-0fd921ac20a3 h1:v8Q77ObTxkm0Wj9iAjcc0VMLxqEzKIdAnaTNPzSiw8Q= github.com/reubenmiller/gojsonq/v2 v2.0.0-20221119213524-0fd921ac20a3/go.mod h1:QidmUT4ebNVwyjKXAQgx9VFHxpOxBKWs32EEXaXnEfE= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= diff --git a/pkg/cmd/remoteaccess/connect/ssh/ssh.manual.go b/pkg/cmd/remoteaccess/connect/ssh/ssh.manual.go index 8563d3c51..50b13005f 100644 --- a/pkg/cmd/remoteaccess/connect/ssh/ssh.manual.go +++ b/pkg/cmd/remoteaccess/connect/ssh/ssh.manual.go @@ -23,12 +23,10 @@ import ( ) type CmdSSH struct { - device []string - externalID []string - externalIDType string - listen string - user string - configuration string + device []string + listen string + user string + configuration string *subcommand.SubCommand @@ -51,22 +49,20 @@ func NewCmdSSH(f *cmdutil.Factory) *CmdSSH { interactive, and it will return upon completion of the command. `), Example: heredoc.Doc(` -$ c8y remoteaccess ssh --device 12345 -Start an interactive SSH session on the device + $ c8y remoteaccess ssh --device 12345 + Start an interactive SSH session on the device -$ c8y remoteaccess ssh --device 12345 --user admin -Start an interactive SSH session on the device with a given ssh user + $ c8y remoteaccess ssh --device 12345 --user admin + Start an interactive SSH session on the device with a given ssh user -$ c8y remoteaccess ssh --device 12345 --user admin -- systemctl status -Use a non-interactive session to execute a single command and print the result + $ c8y remoteaccess ssh --device 12345 --user admin -- systemctl status + Use a non-interactive session to execute a single command and print the result `), RunE: ccmd.RunE, } // Flags cmd.Flags().StringSliceVar(&ccmd.device, "device", []string{}, "Device") - // cmd.Flags().StringSliceVar(&ccmd.externalID, "external-id", []string{}, "Device external identity") - // cmd.Flags().StringVar(&ccmd.externalIDType, "external-type", "c8y_Serial", "Device external identity") cmd.Flags().StringVar(&ccmd.listen, "listen", "127.0.0.1:0", "Listener address. unix:///run/example.sock") cmd.Flags().StringVar(&ccmd.user, "user", "", "Default ssh user") cmd.Flags().StringVar(&ccmd.configuration, "configuration", "", "Remote Access Configuration") @@ -81,9 +77,6 @@ Use a non-interactive session to execute a single command and print the result flags.WithExtendedPipelineSupport("device", "device", false, "deviceId", "source.id", "managedObject.id", "id"), ) - // cmd.MarkFlagsMutuallyExclusive("device", "external-id") - // cmd.MarkFlagsMutuallyExclusive("device", "external-type") - ccmd.SubCommand = subcommand.NewSubCommand(cmd) return ccmd @@ -108,7 +101,6 @@ func (n *CmdSSH) RunE(cmd *cobra.Command, args []string) error { return err } - // TODO: This is overly complicated. Refactor once the generic work is done body := mapbuilder.NewInitializedMapBuilder(true) body.SetApplyTemplateOnMarshalPreference(true) err = flags.WithBody( @@ -149,7 +141,7 @@ func (n *CmdSSH) RunE(cmd *cobra.Command, args []string) error { craClient := remoteaccess.NewRemoteAccessClient(client, remoteaccess.RemoteAccessOptions{ ManagedObjectID: device, RemoteAccessID: craConfig.ID, - }, log) + }) // TCP / socket listener if err := craClient.Listen(n.listen); err != nil { @@ -163,7 +155,6 @@ func (n *CmdSSH) RunE(cmd *cobra.Command, args []string) error { } // Start in background - // TODO: Work out how to shut it down cleanly go craClient.Serve() // Build ssh command @@ -185,7 +176,6 @@ func (n *CmdSSH) RunE(cmd *cobra.Command, args []string) error { sshArgs = append(sshArgs, args[dashIdx:]...) } - // TODO: Should output templates be considered here? sshCmd := exec.CommandContext(context.Background(), "ssh", sshArgs...) sshCmd.Stdout = n.factory.IOStreams.Out sshCmd.Stdin = n.factory.IOStreams.In @@ -201,10 +191,6 @@ func (n *CmdSSH) RunE(cmd *cobra.Command, args []string) error { duration := time.Since(start).Truncate(time.Millisecond) fmt.Fprintf(n.factory.IOStreams.ErrOut, "Duration: %s\n", duration) - // err := n.factory.WriteOutput(output, cmdutil.OutputContext{ - // Input: j.Input, - // }, &commonOptions) - // return nil, err return nil, sshErr }) } diff --git a/pkg/cmd/remoteaccess/server/server.manual.go b/pkg/cmd/remoteaccess/server/server.manual.go index e77ff2132..dc8efefe7 100644 --- a/pkg/cmd/remoteaccess/server/server.manual.go +++ b/pkg/cmd/remoteaccess/server/server.manual.go @@ -41,20 +41,20 @@ func NewCmdServer(f *cmdutil.Factory) *CmdServer { Short: "Start a local proxy server", Long: `Start a local proxy server`, Example: heredoc.Doc(` -$ c8y remoteaccess server --device 12345 -Start a local proxy server on a random local port + $ c8y remoteaccess server --device 12345 + Start a local proxy server on a random local port -$ c8y remoteaccess server --device 12345 --listen - -Start a local proxy server reading from stdin and writing to stdout (useful for usage with the ProxyCommand in ssh) + $ c8y remoteaccess server --device 12345 --listen - + Start a local proxy server reading from stdin and writing to stdout (useful for usage with the ProxyCommand in ssh) -$ c8y remoteaccess server --device 12345 --listen unix:///run/example.sock -Start a local proxy using a UNIX socket + $ c8y remoteaccess server --device 12345 --listen unix:///run/example.sock + Start a local proxy using a UNIX socket -$ c8y remoteaccess server --device 12345 --listen 127.0.0.1:22022 -Start a local proxy using a local TCP server on a fixed port 22022 + $ c8y remoteaccess server --device 12345 --listen 127.0.0.1:22022 + Start a local proxy using a local TCP server on a fixed port 22022 -$ c8y remoteaccess server --device 12345 --configuration "*rugpi*" --browser -Start a local proxy and match on the configuration using wildcards, then open the browser to the endpoint + $ c8y remoteaccess server --device 12345 --configuration "*rugpi*" --browser + Start a local proxy and match on the configuration using wildcards, then open the browser to the endpoint `), RunE: ccmd.RunE, } @@ -101,7 +101,6 @@ func (n *CmdServer) RunE(cmd *cobra.Command, args []string) error { return err } - // TODO: This is overly complicated. Refactor once the generic work is done body := mapbuilder.NewInitializedMapBuilder(true) body.SetApplyTemplateOnMarshalPreference(true) err = flags.WithBody( @@ -142,7 +141,7 @@ func (n *CmdServer) RunE(cmd *cobra.Command, args []string) error { craClient := remoteaccess.NewRemoteAccessClient(client, remoteaccess.RemoteAccessOptions{ ManagedObjectID: device, RemoteAccessID: craConfig.ID, - }, log) + }) if n.listen == "-" { log.Debugf("Listening to request from stdin") @@ -196,8 +195,6 @@ func (n *CmdServer) RunE(cmd *cobra.Command, args []string) error { if n.open { go func() { - // TODO: Should it wait for the server to be up? - // time.Sleep(200 * time.Millisecond) targetURL := fmt.Sprintf("http://%s:%s", host, port) if err := n.factory.Browser.Browse(targetURL); err != nil { cfg.Logger.Warnf("%s", err) From 72bb282b0e29ece66c41357aafa84d721f4781a0 Mon Sep 17 00:00:00 2001 From: Reuben Miller Date: Fri, 17 May 2024 22:15:55 +0200 Subject: [PATCH 13/23] add option to disable reading from stdin in code --- pkg/config/cliConfiguration.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pkg/config/cliConfiguration.go b/pkg/config/cliConfiguration.go index 0aac83738..d37a690d9 100644 --- a/pkg/config/cliConfiguration.go +++ b/pkg/config/cliConfiguration.go @@ -1469,6 +1469,11 @@ func (c *Config) DisableStdin() bool { return c.viper.GetBool(SettingsDisableInput) } +// Change the disable stdin value +func (c *Config) SetDisableStdin(v bool) { + c.viper.Set(SettingsDisableInput, v) +} + // AllowEmptyPipe check if empty piped data is allowed func (c *Config) AllowEmptyPipe() bool { return c.viper.GetBool(SettingsAllowEmptyPipe) From 208a14fa73a5297448831ca3d3a516ed9757796f Mon Sep 17 00:00:00 2001 From: Reuben Miller Date: Fri, 17 May 2024 22:29:49 +0200 Subject: [PATCH 14/23] fix stdio mode --- pkg/cmd/remoteaccess/server/server.manual.go | 26 +++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/pkg/cmd/remoteaccess/server/server.manual.go b/pkg/cmd/remoteaccess/server/server.manual.go index dc8efefe7..d9a45ded7 100644 --- a/pkg/cmd/remoteaccess/server/server.manual.go +++ b/pkg/cmd/remoteaccess/server/server.manual.go @@ -39,7 +39,25 @@ func NewCmdServer(f *cmdutil.Factory) *CmdServer { cmd := &cobra.Command{ Use: "server", Short: "Start a local proxy server", - Long: `Start a local proxy server`, + Long: ` + Start a local proxy server + + You can add use the remote access local proxy within your ssh config file, to use it to + connect to your device with ssh without having to manually launch the proxy yourself! + + To do this add the following configuration to your device. + + --- + Host + User + PreferredAuthentications publickey + IdentityFile + ServerAliveInterval 120 + StrictHostKeyChecking no + UserKnownHostsFile /dev/null + ProxyCommand c8y remoteaccess server --device %n --listen - + --- + `, Example: heredoc.Doc(` $ c8y remoteaccess server --device 12345 Start a local proxy server on a random local port @@ -96,6 +114,12 @@ func (n *CmdServer) RunE(cmd *cobra.Command, args []string) error { return err } + // Disable if stdio mode is being used + if n.listen == "-" { + log.Debug("Disabling pipeline stdin parsing when in stdio mode") + cfg.SetDisableStdin(true) + } + inputIterators, err := cmdutil.NewRequestInputIterators(cmd, cfg) if err != nil { return err From af7be5b3b14ebfe17189629886cc21ac0e318472 Mon Sep 17 00:00:00 2001 From: Reuben Miller Date: Fri, 17 May 2024 22:30:19 +0200 Subject: [PATCH 15/23] update auto-gen code --- api/spec/json/remoteAccessConfigurations.json | 11 ++++++----- api/spec/yaml/remoteAccessConfigurations.yaml | 13 ++++++++----- .../configurations/configurations.auto.go | 2 +- pkg/cmd/remoteaccess/configurations/get/get.auto.go | 4 ++-- .../configurations/update/update.auto.go | 5 ++--- pkg/cmd/remoteaccess/connect/connect.manual.go | 4 ++-- .../PSc8y/Public/Get-RemoteAccessConfiguration.ps1 | 2 +- 7 files changed, 22 insertions(+), 19 deletions(-) diff --git a/api/spec/json/remoteAccessConfigurations.json b/api/spec/json/remoteAccessConfigurations.json index d1118202e..c63e0e2fa 100644 --- a/api/spec/json/remoteAccessConfigurations.json +++ b/api/spec/json/remoteAccessConfigurations.json @@ -1,7 +1,7 @@ { "group": { "name": "remoteaccess/configurations", - "description": "Manage Cloud Remote Access configuration", + "description": "Manage remote access configurations", "descriptionLong": "Cloud Remote Access configuration management", "link": "https://cumulocity.com/docs/cloud-remote-access/using-cloud-remote-access/" }, @@ -47,7 +47,7 @@ }, { "name": "get", - "description": "Get remote access configuration", + "description": "Get a remote access configuration", "descriptionLong": "Get an existing remote access configuration for a device", "alias": { "go": "get", @@ -138,10 +138,11 @@ ], "body": [ { - "name": "name", + "name": "newName", "type": "string", - "description": "Profile name", - "required": true + "property": "name", + "required": false, + "description": "New configuration name" } ] }, diff --git a/api/spec/yaml/remoteAccessConfigurations.yaml b/api/spec/yaml/remoteAccessConfigurations.yaml index 11af89368..f9ceae2dd 100644 --- a/api/spec/yaml/remoteAccessConfigurations.yaml +++ b/api/spec/yaml/remoteAccessConfigurations.yaml @@ -2,7 +2,7 @@ --- group: name: remoteaccess/configurations - description: Manage Cloud Remote Access configuration + description: Manage remote access configurations descriptionLong: Cloud Remote Access configuration management link: https://cumulocity.com/docs/cloud-remote-access/using-cloud-remote-access/ @@ -35,7 +35,7 @@ commands: pipeline: true - name: get - description: Get remote access configuration + description: Get a remote access configuration descriptionLong: Get an existing remote access configuration for a device alias: go: get @@ -65,6 +65,8 @@ commands: pipeline: true - name: update + # Hide for now, as updating requires to PUT the entire existing state into the body plus the desired changes + # For power-users this might be manageable but it is just a poor user experience hidden: true description: Update remote access configuration descriptionLong: Update an existing remote access configuration @@ -96,10 +98,11 @@ commands: description: Connection pipeline: true body: - - name: name + - name: newName type: string - description: Profile name - required: true + property: name + required: false + description: New configuration name - name: delete description: Delete remote access configuration diff --git a/pkg/cmd/remoteaccess/configurations/configurations.auto.go b/pkg/cmd/remoteaccess/configurations/configurations.auto.go index 81221e74c..0dbfce029 100644 --- a/pkg/cmd/remoteaccess/configurations/configurations.auto.go +++ b/pkg/cmd/remoteaccess/configurations/configurations.auto.go @@ -23,7 +23,7 @@ func NewSubCommand(f *cmdutil.Factory) *SubCmdConfigurations { cmd := &cobra.Command{ Use: "configurations", - Short: "Manage Cloud Remote Access configuration", + Short: "Manage remote access configurations", Long: `Cloud Remote Access configuration management`, } diff --git a/pkg/cmd/remoteaccess/configurations/get/get.auto.go b/pkg/cmd/remoteaccess/configurations/get/get.auto.go index da678e4a0..cd9c92364 100644 --- a/pkg/cmd/remoteaccess/configurations/get/get.auto.go +++ b/pkg/cmd/remoteaccess/configurations/get/get.auto.go @@ -25,14 +25,14 @@ type GetCmd struct { factory *cmdutil.Factory } -// NewGetCmd creates a command to Get remote access configuration +// NewGetCmd creates a command to Get a remote access configuration func NewGetCmd(f *cmdutil.Factory) *GetCmd { ccmd := &GetCmd{ factory: f, } cmd := &cobra.Command{ Use: "get", - Short: "Get remote access configuration", + Short: "Get a remote access configuration", Long: `Get an existing remote access configuration for a device`, Example: heredoc.Doc(` $ c8y remoteaccess configurations get --device device01 --id 1 diff --git a/pkg/cmd/remoteaccess/configurations/update/update.auto.go b/pkg/cmd/remoteaccess/configurations/update/update.auto.go index f8a2644b3..c01836fa7 100644 --- a/pkg/cmd/remoteaccess/configurations/update/update.auto.go +++ b/pkg/cmd/remoteaccess/configurations/update/update.auto.go @@ -49,7 +49,7 @@ Update an existing remote access configuration cmd.Flags().StringSlice("device", []string{""}, "Device") cmd.Flags().String("id", "", "Connection (accepts pipeline)") - cmd.Flags().String("name", "", "Profile name (required)") + cmd.Flags().String("newName", "", "New configuration name") completion.WithOptions( cmd, @@ -65,7 +65,6 @@ Update an existing remote access configuration ) // Required flags - _ = cmd.MarkFlagRequired("name") ccmd.SubCommand = subcommand.NewSubCommand(cmd) @@ -141,7 +140,7 @@ func (n *UpdateCmd) RunE(cmd *cobra.Command, args []string) error { body, inputIterators, flags.WithDataFlagValue(), - flags.WithStringValue("name", "name"), + flags.WithStringValue("newName", "name"), cmdutil.WithTemplateValue(n.factory), flags.WithTemplateVariablesValue(), ) diff --git a/pkg/cmd/remoteaccess/connect/connect.manual.go b/pkg/cmd/remoteaccess/connect/connect.manual.go index 786fbaab4..f5c5a7445 100644 --- a/pkg/cmd/remoteaccess/connect/connect.manual.go +++ b/pkg/cmd/remoteaccess/connect/connect.manual.go @@ -16,8 +16,8 @@ func NewSubCommand(f *cmdutil.Factory) *SubCmdConnect { cmd := &cobra.Command{ Use: "connect", - Short: "Connect to device via remote access", - Long: `Connect to device via remote access`, + Short: "Connect to a device", + Long: `Connect to a device using the protocol defined in an existing configuration`, } // Subcommands diff --git a/tools/PSc8y/Public/Get-RemoteAccessConfiguration.ps1 b/tools/PSc8y/Public/Get-RemoteAccessConfiguration.ps1 index acf5b8d3f..c1ab144f0 100644 --- a/tools/PSc8y/Public/Get-RemoteAccessConfiguration.ps1 +++ b/tools/PSc8y/Public/Get-RemoteAccessConfiguration.ps1 @@ -2,7 +2,7 @@ Function Get-RemoteAccessConfiguration { <# .SYNOPSIS -Get remote access configuration +Get a remote access configuration .DESCRIPTION Get an existing remote access configuration for a device From c92e50c0d6d25697e16be07ec720cded43716f56 Mon Sep 17 00:00:00 2001 From: Reuben Miller Date: Sat, 18 May 2024 10:49:59 +0200 Subject: [PATCH 16/23] add remote access configuration completion and named lookups --- api/spec/json/remoteAccessConfigurations.json | 21 ++-- api/spec/schema.json | 1 + api/spec/yaml/remoteAccessConfigurations.yaml | 23 ++--- pkg/c8yfetcher/c8yfetcher.go | 23 +++++ pkg/c8yfetcher/remoteaccessFetcher.go | 11 +++ .../configurations/delete/delete.auto.go | 5 +- .../configurations/get/get.auto.go | 5 +- .../configurations/update/update.auto.go | 5 +- .../remoteaccess/connect/ssh/ssh.manual.go | 19 +++- pkg/cmd/remoteaccess/server/server.manual.go | 1 + pkg/cmd/settings/update/update.manual.go | 5 + pkg/cmdparser/cmdparser.go | 5 + pkg/completion/remoteAccessConfiguration.go | 98 +++++++++++++++++++ pkg/config/cliConfiguration.go | 10 ++ scripts/build-cli/New-C8yApiGoCommand.ps1 | 13 +++ .../New-C8yApiGoGetValueFromFlag.ps1 | 3 + .../New-C8yPowershellArguments.ps1 | 1 + tools/schema/extensionCommands.json | 3 +- 18 files changed, 221 insertions(+), 31 deletions(-) create mode 100644 pkg/completion/remoteAccessConfiguration.go diff --git a/api/spec/json/remoteAccessConfigurations.json b/api/spec/json/remoteAccessConfigurations.json index c63e0e2fa..09ae215b1 100644 --- a/api/spec/json/remoteAccessConfigurations.json +++ b/api/spec/json/remoteAccessConfigurations.json @@ -84,9 +84,12 @@ }, { "name": "id", - "type": "string", + "type": "remoteaccessconfiguration", "description": "Connection", - "pipeline": true + "pipeline": true, + "dependsOn": [ + "device" + ] } ] }, @@ -131,9 +134,12 @@ }, { "name": "id", - "type": "string", + "type": "remoteaccessconfiguration", "description": "Connection", - "pipeline": true + "pipeline": true, + "dependsOn": [ + "device" + ] } ], "body": [ @@ -185,9 +191,12 @@ }, { "name": "id", - "type": "string", + "type": "remoteaccessconfiguration", "description": "Connection", - "pipeline": true + "pipeline": true, + "dependsOn": [ + "device" + ] } ] }, diff --git a/api/spec/schema.json b/api/spec/schema.json index 736efeb9b..54e6d4aad 100644 --- a/api/spec/schema.json +++ b/api/spec/schema.json @@ -247,6 +247,7 @@ "devicegroup[]", "usergroup[]", "userself[]", + "remoteaccessconfiguration", "roleself[]", "role[]", "user[]", diff --git a/api/spec/yaml/remoteAccessConfigurations.yaml b/api/spec/yaml/remoteAccessConfigurations.yaml index f9ceae2dd..f6990e191 100644 --- a/api/spec/yaml/remoteAccessConfigurations.yaml +++ b/api/spec/yaml/remoteAccessConfigurations.yaml @@ -60,9 +60,11 @@ commands: pipeline: false - name: id - type: string + type: remoteaccessconfiguration description: Connection pipeline: true + dependsOn: + - device - name: update # Hide for now, as updating requires to PUT the entire existing state into the body plus the desired changes @@ -94,9 +96,11 @@ commands: pipeline: false - name: id - type: string + type: remoteaccessconfiguration description: Connection pipeline: true + dependsOn: + - device body: - name: newName type: string @@ -130,9 +134,11 @@ commands: pipeline: false - name: id - type: string + type: remoteaccessconfiguration description: Connection pipeline: true + dependsOn: + - device - name: create-passthrough description: Create passthrough configuration @@ -306,17 +312,6 @@ commands: - protocol - name - credentialsType - # bodyTemplates: - # - type: jsonnet - # template: | - - # { - # local credType = if std.objectHas($, "privateKey") && std.objectHas($, "publicKey") then "USER_PASS" else if std.objectHas($, "password") else, - # local credValue = { - - # }, - # credentialsType: credType - # } - name: create-vnc description: Create vnc configuration diff --git a/pkg/c8yfetcher/c8yfetcher.go b/pkg/c8yfetcher/c8yfetcher.go index be990947a..d7d5ca8fe 100644 --- a/pkg/c8yfetcher/c8yfetcher.go +++ b/pkg/c8yfetcher/c8yfetcher.go @@ -1068,3 +1068,26 @@ func WithExternalCommandByNameFirstMatch(factory *cmdutil.Factory, args []string return opt(cmd, inputIterators) } } + +// WithRemoteAccessConfigurationFirstMatch returns the first matching remote access configuration for a device +func WithRemoteAccessConfigurationFirstMatch(factory *cmdutil.Factory, flagDevice string, args []string, opts ...string) flags.GetOption { + return func(cmd *cobra.Command, inputIterators *flags.RequestInputIterators) (string, interface{}, error) { + mo_id := "" + // Note: Lookup of device does not work if "device" is piped input + if values, err := cmd.Flags().GetStringSlice(flagDevice); err == nil && len(values) > 0 { + formattedValues, err := lookupEntity(NewDeviceFetcher(factory), values, false, "") + if err != nil { + return "", nil, err + } + if len(formattedValues) > 0 { + mo_id = formattedValues[0].ID + } + } + if mo_id == "" { + return "", nil, fmt.Errorf("device not found") + } + + opt := WithReferenceByNameFirstMatch(factory, NewRemoteAccessFetcher(factory, mo_id), args, opts...) + return opt(cmd, inputIterators) + } +} diff --git a/pkg/c8yfetcher/remoteaccessFetcher.go b/pkg/c8yfetcher/remoteaccessFetcher.go index 973a5c269..90c9301e5 100644 --- a/pkg/c8yfetcher/remoteaccessFetcher.go +++ b/pkg/c8yfetcher/remoteaccessFetcher.go @@ -19,6 +19,17 @@ type RemoteAccessFetcher struct { } func DetectRemoteAccessConfiguration(client *c8y.Client, mo_id string, name string) (*c8y.RemoteAccessConfiguration, error) { + if c8y.IsID(name) { + config, _, err := client.RemoteAccess.GetConfiguration( + c8y.WithDisabledDryRunContext(context.Background()), + mo_id, + name, + ) + if err == nil { + return config, err + } + // Fall through if it could not be found (as the name might just look like an id) + } configs, _, err := client.RemoteAccess.GetConfigurations( c8y.WithDisabledDryRunContext(context.Background()), mo_id, diff --git a/pkg/cmd/remoteaccess/configurations/delete/delete.auto.go b/pkg/cmd/remoteaccess/configurations/delete/delete.auto.go index f67b15606..16b22a2bd 100644 --- a/pkg/cmd/remoteaccess/configurations/delete/delete.auto.go +++ b/pkg/cmd/remoteaccess/configurations/delete/delete.auto.go @@ -46,11 +46,12 @@ Delete an existing remote access configuration cmd.SilenceUsage = true cmd.Flags().StringSlice("device", []string{""}, "Device") - cmd.Flags().String("id", "", "Connection (accepts pipeline)") + cmd.Flags().StringSlice("id", []string{""}, "Connection (accepts pipeline)") completion.WithOptions( cmd, completion.WithDevice("device", func() (*c8y.Client, error) { return ccmd.factory.Client() }), + completion.WithRemoteAccessConfiguration("id", "device", func() (*c8y.Client, error) { return ccmd.factory.Client() }), ) flags.WithOptions( @@ -148,7 +149,7 @@ func (n *DeleteCmd) RunE(cmd *cobra.Command, args []string) error { path, inputIterators, c8yfetcher.WithDeviceByNameFirstMatch(n.factory, args, "device", "device"), - flags.WithStringValue("id", "id"), + c8yfetcher.WithRemoteAccessConfigurationFirstMatch(n.factory, "device", args, "id", "id"), ) if err != nil { return err diff --git a/pkg/cmd/remoteaccess/configurations/get/get.auto.go b/pkg/cmd/remoteaccess/configurations/get/get.auto.go index cd9c92364..6a24fb061 100644 --- a/pkg/cmd/remoteaccess/configurations/get/get.auto.go +++ b/pkg/cmd/remoteaccess/configurations/get/get.auto.go @@ -47,11 +47,12 @@ Get existing remote access configuration cmd.SilenceUsage = true cmd.Flags().StringSlice("device", []string{""}, "Device") - cmd.Flags().String("id", "", "Connection (accepts pipeline)") + cmd.Flags().StringSlice("id", []string{""}, "Connection (accepts pipeline)") completion.WithOptions( cmd, completion.WithDevice("device", func() (*c8y.Client, error) { return ccmd.factory.Client() }), + completion.WithRemoteAccessConfiguration("id", "device", func() (*c8y.Client, error) { return ccmd.factory.Client() }), ) flags.WithOptions( @@ -152,7 +153,7 @@ func (n *GetCmd) RunE(cmd *cobra.Command, args []string) error { path, inputIterators, c8yfetcher.WithDeviceByNameFirstMatch(n.factory, args, "device", "device"), - flags.WithStringValue("id", "id"), + c8yfetcher.WithRemoteAccessConfigurationFirstMatch(n.factory, "device", args, "id", "id"), ) if err != nil { return err diff --git a/pkg/cmd/remoteaccess/configurations/update/update.auto.go b/pkg/cmd/remoteaccess/configurations/update/update.auto.go index c01836fa7..e25a6b0fa 100644 --- a/pkg/cmd/remoteaccess/configurations/update/update.auto.go +++ b/pkg/cmd/remoteaccess/configurations/update/update.auto.go @@ -48,12 +48,13 @@ Update an existing remote access configuration cmd.SilenceUsage = true cmd.Flags().StringSlice("device", []string{""}, "Device") - cmd.Flags().String("id", "", "Connection (accepts pipeline)") + cmd.Flags().StringSlice("id", []string{""}, "Connection (accepts pipeline)") cmd.Flags().String("newName", "", "New configuration name") completion.WithOptions( cmd, completion.WithDevice("device", func() (*c8y.Client, error) { return ccmd.factory.Client() }), + completion.WithRemoteAccessConfiguration("id", "device", func() (*c8y.Client, error) { return ccmd.factory.Client() }), ) flags.WithOptions( @@ -155,7 +156,7 @@ func (n *UpdateCmd) RunE(cmd *cobra.Command, args []string) error { path, inputIterators, c8yfetcher.WithDeviceByNameFirstMatch(n.factory, args, "device", "device"), - flags.WithStringValue("id", "id"), + c8yfetcher.WithRemoteAccessConfigurationFirstMatch(n.factory, "device", args, "id", "id"), ) if err != nil { return err diff --git a/pkg/cmd/remoteaccess/connect/ssh/ssh.manual.go b/pkg/cmd/remoteaccess/connect/ssh/ssh.manual.go index 50b13005f..5239a97f7 100644 --- a/pkg/cmd/remoteaccess/connect/ssh/ssh.manual.go +++ b/pkg/cmd/remoteaccess/connect/ssh/ssh.manual.go @@ -42,11 +42,17 @@ func NewCmdSSH(f *cmdutil.Factory) *CmdSSH { Use: "ssh", Short: "Connect to a device via ssh", Long: heredoc.Doc(` - Connect to a device via ssh + Connect to a device via ssh + + Additional arguments can be passed to the ssh shell by using the "--" convention where everything + after the "--" will be passed untouched to the ssh shell. In this mode, the shell will not be + interactive, and it will return upon completion of the command. + + You can set the default ssh user to use for all ssh connections for your current c8y session file + using: + + c8y settings update remoteaccess.sshuser root - Additional arguments can be passed to the ssh shell by using the "--" convention where everything - after the "--" will be passed untouched to the ssh shell. In this mode, the shell will not be - interactive, and it will return upon completion of the command. `), Example: heredoc.Doc(` $ c8y remoteaccess ssh --device 12345 @@ -70,6 +76,7 @@ func NewCmdSSH(f *cmdutil.Factory) *CmdSSH { completion.WithOptions( cmd, completion.WithDevice("device", func() (*c8y.Client, error) { return ccmd.factory.Client() }), + completion.WithRemoteAccessPassthroughConfiguration("configuration", "device", func() (*c8y.Client, error) { return ccmd.factory.Client() }), ) flags.WithOptions( @@ -165,6 +172,10 @@ func (n *CmdSSH) RunE(cmd *cobra.Command, args []string) error { } sshTarget := host + if n.user == "" { + // Use default user (if set) + n.user = cfg.GetRemoteAccessDefaultSSHUser() + } if n.user != "" { sshTarget = fmt.Sprintf("%s@%s", n.user, host) } diff --git a/pkg/cmd/remoteaccess/server/server.manual.go b/pkg/cmd/remoteaccess/server/server.manual.go index d9a45ded7..fb24a01d6 100644 --- a/pkg/cmd/remoteaccess/server/server.manual.go +++ b/pkg/cmd/remoteaccess/server/server.manual.go @@ -86,6 +86,7 @@ func NewCmdServer(f *cmdutil.Factory) *CmdServer { completion.WithOptions( cmd, completion.WithDevice("device", func() (*c8y.Client, error) { return ccmd.factory.Client() }), + completion.WithRemoteAccessPassthroughConfiguration("configuration", "device", func() (*c8y.Client, error) { return ccmd.factory.Client() }), ) flags.WithOptions( diff --git a/pkg/cmd/settings/update/update.manual.go b/pkg/cmd/settings/update/update.manual.go index 76924da5d..264a1f8b5 100644 --- a/pkg/cmd/settings/update/update.manual.go +++ b/pkg/cmd/settings/update/update.manual.go @@ -395,6 +395,11 @@ var updateSettingsOptions = map[string]argumentHandler{ // extensions "extensions.datadir": {"extensions.datadir", "string", config.SettingsExtensionDataDir, []string{}, nil, cobra.ShellCompDirectiveDefault}, + + // remote access + "remoteaccess.sshuser": {"remoteaccess.sshuser", "string", config.SettingsRemoteAccessDefaultSSHUser, []string{ + "root", + }, nil, cobra.ShellCompDirectiveNoFileComp}, } // NewCmdUpdate returns a new command used to update session settings diff --git a/pkg/cmdparser/cmdparser.go b/pkg/cmdparser/cmdparser.go index 38701b2bd..45e7e3e99 100644 --- a/pkg/cmdparser/cmdparser.go +++ b/pkg/cmdparser/cmdparser.go @@ -226,6 +226,8 @@ func GetCompletionOptions(cmd *CmdOptions, p *models.Parameter, factory *cmdutil return completion.WithNotification2SubscriptionName(p.Name, func() (*c8y.Client, error) { return factory.Client() }) case "subscriptionId": return completion.WithNotification2SubscriptionId(p.Name, func() (*c8y.Client, error) { return factory.Client() }) + case "remoteaccessconfiguration": + return completion.WithRemoteAccessConfiguration(p.Name, p.GetDependentProperty(0, "device"), func() (*c8y.Client, error) { return factory.Client() }) } return nil } @@ -494,6 +496,9 @@ func GetOption(cmd *CmdOptions, p *models.Parameter, factory *cmdutil.Factory, a case "usergroup[]": opts = append(opts, c8yfetcher.WithUserGroupByNameFirstMatch(factory, args, p.Name, targetProp, p.Format)) + + case "remoteaccessconfiguration": + opts = append(opts, c8yfetcher.WithRemoteAccessConfigurationFirstMatch(factory, p.GetDependentProperty(0, "device"), args, p.Name, targetProp)) } return opts diff --git a/pkg/completion/remoteAccessConfiguration.go b/pkg/completion/remoteAccessConfiguration.go new file mode 100644 index 000000000..e09ac88c6 --- /dev/null +++ b/pkg/completion/remoteAccessConfiguration.go @@ -0,0 +1,98 @@ +package completion + +import ( + "context" + "fmt" + "strings" + + "github.com/reubenmiller/go-c8y-cli/v2/pkg/matcher" + "github.com/reubenmiller/go-c8y/pkg/c8y" + "github.com/spf13/cobra" +) + +// WithRemoteAccessConfiguration remote access configuration completion (requires device) +func WithRemoteAccessConfiguration(flagConfiguration string, flagDevice string, clientFunc func() (*c8y.Client, error)) Option { + return findRemoteAccessConfigurations(flagConfiguration, flagDevice, "", clientFunc) +} + +// WithRemoteAccessPassthroughConfiguration complete passthrough remote access completions (requires device) +func WithRemoteAccessPassthroughConfiguration(flagConfiguration string, flagDevice string, clientFunc func() (*c8y.Client, error)) Option { + return findRemoteAccessConfigurations(flagConfiguration, flagDevice, c8y.RemoteAccessProtocolPassthrough, clientFunc) +} + +func findRemoteAccessConfigurations(flagConfiguration string, flagDevice string, configType string, clientFunc func() (*c8y.Client, error)) Option { + return func(cmd *cobra.Command) *cobra.Command { + _ = cmd.RegisterFlagCompletionFunc(flagConfiguration, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + client, err := clientFunc() + if err != nil { + return []string{err.Error()}, cobra.ShellCompDirectiveError + } + + deviceNames, err := GetFlagStringValues(cmd, flagDevice) + if err != nil { + return []string{err.Error()}, cobra.ShellCompDirectiveError + } + + // stop if no device is given otherwise there will be too many service name conflicts + if len(deviceNames) == 0 { + return []string{fmt.Errorf("device is required. %w", ErrNotFound).Error()}, cobra.ShellCompDirectiveError + } + + if deviceNames[0] == "" { + return []string{fmt.Errorf("device is required. %w", ErrNotFound).Error()}, cobra.ShellCompDirectiveError + } + deviceName := deviceNames[0] + + var deviceID string + if c8y.IsID(deviceName) { + // no lookup required + deviceID = deviceName + } else { + // lookup via name + items, _, err := client.Inventory.GetDevicesByName( + c8y.WithDisabledDryRunContext(context.Background()), + deviceName, + c8y.NewPaginationOptions(100), + ) + if err != nil { + return []string{err.Error()}, cobra.ShellCompDirectiveError + } + if len(items.ManagedObjects) == 0 { + return []string{"DeviceNotFound"}, cobra.ShellCompDirectiveError + } + deviceID = items.ManagedObjects[0].ID + // deviceName = items.ManagedObjects[0].Name + } + // technically we could save a request and just use the managed object data structure (.c8y_RemoteAccessList) + // but it is more correct to get the list of connections from the service + configs, _, err := client.RemoteAccess.GetConfigurations( + c8y.WithDisabledDryRunContext(context.Background()), + deviceID, + &c8y.RemoteAccessCollectionOptions{ + PaginationOptions: *c8y.NewPaginationOptions(100), + }, + ) + if err != nil { + values := []string{fmt.Sprintf("error. %s", err)} + return values, cobra.ShellCompDirectiveError + } + + values := []string{} + namePattern, err := matcher.ConvertWildcardToRegex("*" + toComplete + "*") + if err != nil { + return []string{err.Error()}, cobra.ShellCompDirectiveError + } + for _, item := range configs { + if configType != "" && !strings.EqualFold(item.Protocol, configType) { + continue + } + if namePattern.MatchString(item.Name) || strings.HasPrefix(item.ID, toComplete) { + values = append(values, fmt.Sprintf("%s\t%s %s:%d | id: %s", item.Name, item.Protocol, item.Hostname, item.Port, item.ID)) + } + } + + return values, cobra.ShellCompDirectiveNoFileComp + }) + return cmd + } +} diff --git a/pkg/config/cliConfiguration.go b/pkg/config/cliConfiguration.go index d37a690d9..7b01b7cdf 100644 --- a/pkg/config/cliConfiguration.go +++ b/pkg/config/cliConfiguration.go @@ -326,6 +326,12 @@ const ( // SettingsBrowser default browser SettingsBrowser = "settings.browser" + // + // Remote Access preferences + // + // SettingsRemoteAccessDefaultSSHUser the default ssh user + SettingsRemoteAccessDefaultSSHUser = "settings.remoteaccess.sshuser" + // Extensions SettingsExtensionDataDir = "settings.extensions.datadir" SettingsExtensionDefaultHost = "settings.extensions.defaultHost" @@ -1581,6 +1587,10 @@ func (c *Config) ExtensionDefaultUsername() string { return c.viper.GetString(SettingsExtensionDefaultUsername) } +func (c *Config) GetRemoteAccessDefaultSSHUser() string { + return c.viper.GetString(SettingsRemoteAccessDefaultSSHUser) +} + // GetJSONSelect get json properties to be selected from the output. Only the given properties will be returned func (c *Config) GetJSONSelect() []string { // Note: select is stored as an cobra Array String, which add special formating of values. diff --git a/scripts/build-cli/New-C8yApiGoCommand.ps1 b/scripts/build-cli/New-C8yApiGoCommand.ps1 index 9b8d48737..c94da2752 100644 --- a/scripts/build-cli/New-C8yApiGoCommand.ps1 +++ b/scripts/build-cli/New-C8yApiGoCommand.ps1 @@ -221,6 +221,7 @@ "certificate[]" { [void] $CompletionBuilderOptions.AppendLine("completion.WithDeviceCertificate(`"$($iArg.Name)`", func() (*c8y.Client, error) { return ccmd.factory.Client()}),") } "subscriptionName" { [void] $CompletionBuilderOptions.AppendLine("completion.WithNotification2SubscriptionName(`"$($iArg.Name)`", func() (*c8y.Client, error) { return ccmd.factory.Client()}),") } "subscriptionId" { [void] $CompletionBuilderOptions.AppendLine("completion.WithNotification2SubscriptionId(`"$($iArg.Name)`", func() (*c8y.Client, error) { return ccmd.factory.Client()}),") } + "remoteaccessconfiguration" { [void] $CompletionBuilderOptions.AppendLine("completion.WithRemoteAccessConfiguration(`"$($iArg.Name)`", `"$($iArg.dependsOn | Select-Object -First 1)`", func() (*c8y.Client, error) { return ccmd.factory.Client()}),") } } $ArgParams = @{ @@ -1413,6 +1414,18 @@ Function Get-C8yGoArgs { } } + "remoteaccessconfiguration" { + $SetFlag = if ($UseOption) { + "cmd.Flags().StringSlice(`"${Name}`", `"${OptionName}`", []string{`"${Default}`"}, `"${Description}`")" + } else { + "cmd.Flags().StringSlice(`"${Name}`", []string{`"${Default}`"}, `"${Description}`")" + } + + @{ + SetFlag = $SetFlag + } + } + default { Write-Warning "Unknown flag type [$_]" } diff --git a/scripts/build-cli/New-C8yApiGoGetValueFromFlag.ps1 b/scripts/build-cli/New-C8yApiGoGetValueFromFlag.ps1 index fe91c8d04..764a1523e 100644 --- a/scripts/build-cli/New-C8yApiGoGetValueFromFlag.ps1 +++ b/scripts/build-cli/New-C8yApiGoGetValueFromFlag.ps1 @@ -217,6 +217,9 @@ # user group array "usergroup[]" = "c8yfetcher.WithUserGroupByNameFirstMatch(n.factory, args, `"${prop}`", `"${queryParam}`"$FormatValue)," + + # remote access configuration + "remoteaccessconfiguration" = "c8yfetcher.WithRemoteAccessConfigurationFirstMatch(n.factory, `"device`", args, `"${prop}`", `"${queryParam}`")," } diff --git a/scripts/build-powershell/New-C8yPowershellArguments.ps1 b/scripts/build-powershell/New-C8yPowershellArguments.ps1 index 8bab3a2d7..d13802d20 100644 --- a/scripts/build-powershell/New-C8yPowershellArguments.ps1 +++ b/scripts/build-powershell/New-C8yPowershellArguments.ps1 @@ -106,6 +106,7 @@ "microserviceinstance" { "string"; break } "microservicename" { "object[]"; break } "optional_fragment" { "switch"; break } + "remoteaccessconfiguration" { "object[]"; break } "set" { "object[]"; break } "softwareName" { "object[]"; break } "softwareversionName" { "object[]"; break } diff --git a/tools/schema/extensionCommands.json b/tools/schema/extensionCommands.json index 43fd483a4..3d22bb1cd 100644 --- a/tools/schema/extensionCommands.json +++ b/tools/schema/extensionCommands.json @@ -299,7 +299,8 @@ "uipluginversion", "user[]", "usergroup[]", - "userself[]" + "userself[]", + "remoteaccessconfiguration" ] }, "pipelineAliases": { From af574ea8d386be8f3a1e1ca1d6beca8fc9b770d0 Mon Sep 17 00:00:00 2001 From: Reuben Miller Date: Sat, 18 May 2024 11:04:25 +0200 Subject: [PATCH 17/23] fix ssh connect examples --- pkg/cmd/remoteaccess/connect/ssh/ssh.manual.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/cmd/remoteaccess/connect/ssh/ssh.manual.go b/pkg/cmd/remoteaccess/connect/ssh/ssh.manual.go index 5239a97f7..bf57ab729 100644 --- a/pkg/cmd/remoteaccess/connect/ssh/ssh.manual.go +++ b/pkg/cmd/remoteaccess/connect/ssh/ssh.manual.go @@ -55,13 +55,13 @@ func NewCmdSSH(f *cmdutil.Factory) *CmdSSH { `), Example: heredoc.Doc(` - $ c8y remoteaccess ssh --device 12345 + $ c8y remoteaccess connect ssh --device 12345 Start an interactive SSH session on the device - $ c8y remoteaccess ssh --device 12345 --user admin + $ c8y remoteaccess connect ssh --device 12345 --user admin Start an interactive SSH session on the device with a given ssh user - $ c8y remoteaccess ssh --device 12345 --user admin -- systemctl status + $ c8y remoteaccess connect ssh --device 12345 --user admin -- systemctl status Use a non-interactive session to execute a single command and print the result `), RunE: ccmd.RunE, From ec9defe6a1dd6033af6438b9fabff91e972087f6 Mon Sep 17 00:00:00 2001 From: Reuben Miller Date: Sat, 18 May 2024 11:43:33 +0200 Subject: [PATCH 18/23] add custom local proxy run command --- .../remoteaccess/connect/connect.manual.go | 2 + .../remoteaccess/connect/run/run.manual.go | 213 ++++++++++++++++++ .../remoteaccess/connect/ssh/ssh.manual.go | 2 +- 3 files changed, 216 insertions(+), 1 deletion(-) create mode 100644 pkg/cmd/remoteaccess/connect/run/run.manual.go diff --git a/pkg/cmd/remoteaccess/connect/connect.manual.go b/pkg/cmd/remoteaccess/connect/connect.manual.go index f5c5a7445..74f7594b5 100644 --- a/pkg/cmd/remoteaccess/connect/connect.manual.go +++ b/pkg/cmd/remoteaccess/connect/connect.manual.go @@ -1,6 +1,7 @@ package connect import ( + "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/remoteaccess/connect/run" "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/remoteaccess/connect/ssh" "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/subcommand" "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmdutil" @@ -22,6 +23,7 @@ func NewSubCommand(f *cmdutil.Factory) *SubCmdConnect { // Subcommands cmd.AddCommand(ssh.NewCmdSSH(f).GetCommand()) + cmd.AddCommand(run.NewCmdRun(f).GetCommand()) ccmd.SubCommand = subcommand.NewSubCommand(cmd) diff --git a/pkg/cmd/remoteaccess/connect/run/run.manual.go b/pkg/cmd/remoteaccess/connect/run/run.manual.go new file mode 100644 index 000000000..bd27a8db6 --- /dev/null +++ b/pkg/cmd/remoteaccess/connect/run/run.manual.go @@ -0,0 +1,213 @@ +package run + +import ( + "context" + "fmt" + "os/exec" + "strings" + "time" + + "github.com/MakeNowJust/heredoc/v2" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/c8yfetcher" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/subcommand" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmderrors" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmdutil" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/completion" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/flags" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/iterator" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/mapbuilder" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/worker" + "github.com/reubenmiller/go-c8y/pkg/c8y" + "github.com/reubenmiller/go-c8y/pkg/remoteaccess" + "github.com/spf13/cobra" + "github.com/tidwall/gjson" +) + +type CmdRun struct { + device []string + listen string + configuration string + + *subcommand.SubCommand + + factory *cmdutil.Factory +} + +func NewCmdRun(f *cmdutil.Factory) *CmdRun { + ccmd := &CmdRun{ + factory: f, + } + + cmd := &cobra.Command{ + Use: "run", + Short: "Connect to a device and run a custom command", + Long: heredoc.Doc(` + Connect to a device using the local proxy, then run a given command which makes use of the proxy. + + You can run any command you want by providing all arguments after the "--", for example: + + c8y remoteaccess connect run --device mydevice -- ./run-something.sh %h %p + + The local proxy server port and target can be accessed either by using variable references: + * Target - Use '%h' or the environment variable '$TARGET' + * Port - Use '%p' or the environment variable '$PORT' + + It is recommended to use the "%h" (target) "%p" (port) to refer to the local proxy server + as this avoid potential problems with variable expansions and removes the need to have to + escape variable etc. (e.g. \$HOST). Below shows an example using a custom ssh command: + + c8y remoteaccess connect run --device mydevice -- ssh -p %p root@%h + + Or you can use a shell to do something more complex and use environment variable references, though not the + single quotes around the shell command to prevent the variable expansion in your current shell. + + c8y remoteaccess connect run --device mydevice -- sh -c 'ssh -p $PORT root@$TARGET' + `), + Example: heredoc.Doc(` + $ c8y remoteaccess connect run --device 12345 -- ssh -p %p root@%h + Start an interactive SSH session on the device with a given ssh user + `), + RunE: ccmd.RunE, + } + + // Flags + cmd.Flags().StringSliceVar(&ccmd.device, "device", []string{}, "Device") + cmd.Flags().StringVar(&ccmd.listen, "listen", "127.0.0.1:0", "Listener address. unix:///run/example.sock") + cmd.Flags().StringVar(&ccmd.configuration, "configuration", "", "Remote Access Configuration") + + completion.WithOptions( + cmd, + completion.WithDevice("device", func() (*c8y.Client, error) { return ccmd.factory.Client() }), + completion.WithRemoteAccessPassthroughConfiguration("configuration", "device", func() (*c8y.Client, error) { return ccmd.factory.Client() }), + ) + + flags.WithOptions( + cmd, + flags.WithExtendedPipelineSupport("device", "device", false, "deviceId", "source.id", "managedObject.id", "id"), + ) + + ccmd.SubCommand = subcommand.NewSubCommand(cmd) + + return ccmd +} + +func (n *CmdRun) RunE(cmd *cobra.Command, args []string) error { + client, err := n.factory.Client() + if err != nil { + return err + } + cfg, err := n.factory.Config() + if err != nil { + return err + } + log, err := n.factory.Logger() + if err != nil { + return err + } + + inputIterators, err := cmdutil.NewRequestInputIterators(cmd, cfg) + if err != nil { + return err + } + + body := mapbuilder.NewInitializedMapBuilder(true) + body.SetApplyTemplateOnMarshalPreference(true) + err = flags.WithBody( + cmd, + body, + inputIterators, + c8yfetcher.WithDeviceByNameFirstMatch(n.factory, args, "device", "device"), + ) + if err != nil { + return err + } + + var iter iterator.Iterator + if inputIterators.Total > 0 { + iter = mapbuilder.NewMapBuilderIterator(body) + } else { + iter = iterator.NewBoundIterator(mapbuilder.NewMapBuilderIterator(body), 1) + } + + commonOptions, err := cfg.GetOutputCommonOptions(cmd) + if err != nil { + return err + } + commonOptions.DisableResultPropertyDetection() + + return n.factory.RunWithGenericWorkers(cmd, inputIterators, iter, func(j worker.Job) (any, error) { + item := gjson.ParseBytes(j.Value.([]byte)) + device := item.Get("device").String() + + craConfig, err := c8yfetcher.DetectRemoteAccessConfiguration(client, device, n.configuration) + if err != nil { + return nil, err + } + + log.Debugf("Using remote access configuration: id=%s, name=%s", craConfig.ID, craConfig.Name) + + // Lookup configuration + craClient := remoteaccess.NewRemoteAccessClient(client, remoteaccess.RemoteAccessOptions{ + ManagedObjectID: device, + RemoteAccessID: craConfig.ID, + }) + + // TCP / socket listener + if err := craClient.Listen(n.listen); err != nil { + return nil, err + } + + localAddress := craClient.GetListenerAddress() + host, port, _ := strings.Cut(localAddress, ":") + if host == "" { + host = "127.0.0.1" + } + + // Start in background + go craClient.Serve() + + // Build ssh command + run := "" + runArgs := []string{} + + dashIdx := cmd.ArgsLenAtDash() + if dashIdx > -1 { + run = args[dashIdx] + if dashIdx+1 < len(args) { + // Allow users to access port via variables (similar to what ssh does) + for _, v := range args[dashIdx+1:] { + expandedValue := v + expandedValue = strings.ReplaceAll(expandedValue, "%h", host) + expandedValue = strings.ReplaceAll(expandedValue, "%p", port) + runArgs = append(runArgs, expandedValue) + } + } + } + + if run == "" { + return nil, cmderrors.NewUserError("Missing run command") + } + + runCmd := exec.CommandContext(context.Background(), run, runArgs...) + + // Add target and port to the environment variables so it can be easily accessed from more + // complex scripts + runCmd.Env = append(runCmd.Env, fmt.Sprintf("PORT=%s", port)) + runCmd.Env = append(runCmd.Env, fmt.Sprintf("TARGET=%s", host)) + runCmd.Stdout = n.factory.IOStreams.Out + runCmd.Stdin = n.factory.IOStreams.In + runCmd.Stderr = n.factory.IOStreams.ErrOut + + log.Infof("Executing command: %s %s\n", run, strings.Join(runArgs, " ")) + + cs := n.factory.IOStreams.ColorScheme() + fmt.Fprintln(n.factory.IOStreams.ErrOut, cs.Green(fmt.Sprintf("Starting external command on %s (%s)\n", device, strings.TrimRight(client.BaseURL.String(), "/")))) + + start := time.Now() + sshErr := runCmd.Run() + duration := time.Since(start).Truncate(time.Millisecond) + fmt.Fprintf(n.factory.IOStreams.ErrOut, "Duration: %s\n", duration) + + return nil, sshErr + }) +} diff --git a/pkg/cmd/remoteaccess/connect/ssh/ssh.manual.go b/pkg/cmd/remoteaccess/connect/ssh/ssh.manual.go index bf57ab729..5b05277e6 100644 --- a/pkg/cmd/remoteaccess/connect/ssh/ssh.manual.go +++ b/pkg/cmd/remoteaccess/connect/ssh/ssh.manual.go @@ -192,7 +192,7 @@ func (n *CmdSSH) RunE(cmd *cobra.Command, args []string) error { sshCmd.Stdin = n.factory.IOStreams.In sshCmd.Stderr = n.factory.IOStreams.ErrOut - log.Debugf("Executing command: ssh %s\n", strings.Join(sshArgs, " ")) + log.Infof("Executing command: ssh %s\n", strings.Join(sshArgs, " ")) cs := n.factory.IOStreams.ColorScheme() fmt.Fprintln(n.factory.IOStreams.ErrOut, cs.Green(fmt.Sprintf("Starting interactive ssh session with %s (%s)\n", device, strings.TrimRight(client.BaseURL.String(), "/")))) From 7805207ec48779e792c7598c295e6192435c339e Mon Sep 17 00:00:00 2001 From: Reuben Miller Date: Sat, 18 May 2024 12:48:49 +0200 Subject: [PATCH 19/23] use latest go-c8y version --- go.mod | 22 +++++++++++----------- go.sum | 40 ++++++++++++++++++++-------------------- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/go.mod b/go.mod index 593ffaf76..4e6115dd4 100644 --- a/go.mod +++ b/go.mod @@ -24,21 +24,21 @@ require ( github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0 github.com/pkg/errors v0.9.1 github.com/pquerna/otp v1.4.0 - github.com/reubenmiller/go-c8y v0.15.11-0.20240517191142-ee60c4663b16 + github.com/reubenmiller/go-c8y v0.16.0 github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 github.com/sergi/go-diff v1.3.1 // indirect - github.com/sethvargo/go-password v0.2.0 + github.com/sethvargo/go-password v0.3.0 github.com/spf13/cobra v1.8.0 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.18.2 github.com/stretchr/testify v1.8.4 - github.com/tidwall/gjson v1.17.0 + github.com/tidwall/gjson v1.17.1 github.com/tidwall/pretty v1.2.1 github.com/tidwall/sjson v1.2.5 github.com/vbauerster/mpb/v6 v6.0.4 - go.uber.org/zap v1.26.0 - golang.org/x/crypto v0.18.0 - golang.org/x/term v0.16.0 + go.uber.org/zap v1.27.0 + golang.org/x/crypto v0.23.0 + golang.org/x/term v0.20.0 gopkg.in/yaml.v3 v3.0.1 moul.io/http2curl/v2 v2.3.0 ) @@ -95,15 +95,15 @@ require ( github.com/yuin/goldmark-emoji v1.0.2 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/exp v0.0.0-20231226003508-02704c960a9b // indirect - golang.org/x/net v0.20.0 // indirect - golang.org/x/sys v0.16.0 // indirect - golang.org/x/text v0.14.0 // indirect + golang.org/x/net v0.25.0 // indirect + golang.org/x/sys v0.20.0 // indirect + golang.org/x/text v0.15.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637 // indirect rsc.io/qr v0.2.0 // indirect sigs.k8s.io/yaml v1.4.0 // indirect ) -go 1.21 +go 1.22 -toolchain go1.21.5 +toolchain go1.22.3 diff --git a/go.sum b/go.sum index 0ec338101..b0e436fe4 100644 --- a/go.sum +++ b/go.sum @@ -155,8 +155,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pquerna/otp v1.4.0 h1:wZvl1TIVxKRThZIBiwOOHOGP/1+nZyWBil9Y2XNEDzg= github.com/pquerna/otp v1.4.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg= -github.com/reubenmiller/go-c8y v0.15.11-0.20240517191142-ee60c4663b16 h1:sVQJbpqkp35U8OJgSKv1dEendhMYk5JgWQ97tHwDP5Q= -github.com/reubenmiller/go-c8y v0.15.11-0.20240517191142-ee60c4663b16/go.mod h1:5F2Z5V0A1dkaSva48iVxLUuDE866rvSe8zi3fAPxZ6Y= +github.com/reubenmiller/go-c8y v0.16.0 h1:z/aZ46kzzsOsR/bKkq9psOFa7DINkVXo0J8RBfnPnGI= +github.com/reubenmiller/go-c8y v0.16.0/go.mod h1:l62OddfIaFxv+OosDiPTKu54B2qKgiQ9MRr2xM8WKTI= github.com/reubenmiller/gojsonq/v2 v2.0.0-20221119213524-0fd921ac20a3 h1:v8Q77ObTxkm0Wj9iAjcc0VMLxqEzKIdAnaTNPzSiw8Q= github.com/reubenmiller/gojsonq/v2 v2.0.0-20221119213524-0fd921ac20a3/go.mod h1:QidmUT4ebNVwyjKXAQgx9VFHxpOxBKWs32EEXaXnEfE= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= @@ -177,8 +177,8 @@ github.com/scylladb/termtables v0.0.0-20191203121021-c4c0b6d42ff4/go.mod h1:C1a7 github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= -github.com/sethvargo/go-password v0.2.0 h1:BTDl4CC/gjf/axHMaDQtw507ogrXLci6XRiLc7i/UHI= -github.com/sethvargo/go-password v0.2.0/go.mod h1:Ym4Mr9JXLBycr02MFuVQ/0JHidNetSgbzutTr3zsYXE= +github.com/sethvargo/go-password v0.3.0 h1:OLFHZ91Z7NiNP3dnaPxLxCDXlb6TBuxFzMvv6bu+Ptw= +github.com/sethvargo/go-password v0.3.0/go.mod h1:p6we8DZ0eyYXof9pon7Cqrw98N4KTaYiadDml1dUEEw= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= @@ -207,8 +207,8 @@ github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8 github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/tailscale/depaware v0.0.0-20210622194025-720c4b409502/go.mod h1:p9lPsd+cx33L3H9nNoecRRxPssFKUwwI50I3pZ0yT+8= github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= -github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM= -github.com/tidwall/gjson v1.17.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.17.1 h1:wlYEnwqAHgzmhNUFfw7Xalt2JzQvsMx2Se4PcoFCT/U= +github.com/tidwall/gjson v1.17.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= @@ -227,18 +227,18 @@ github.com/yuin/goldmark v1.6.0/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5ta github.com/yuin/goldmark-emoji v1.0.1/go.mod h1:2w1E6FEWLcDQkoTE+7HU6QF1F6SLlNGjRIBbIZQFqkQ= github.com/yuin/goldmark-emoji v1.0.2 h1:c/RgTShNgHTtc6xdz2KKI74jJr6rWi7FPgnP9GAsO5s= github.com/yuin/goldmark-emoji v1.0.2/go.mod h1:RhP/RWpexdp+KHs7ghKnifRoIs/Bq4nDS7tRbCkOwKY= -go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= -go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= -go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= -golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/exp v0.0.0-20231226003508-02704c960a9b h1:kLiC65FbiHWFAOu+lxwNPujcsl8VYyTYYEZnsOO1WK4= golang.org/x/exp v0.0.0-20231226003508-02704c960a9b/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -250,8 +250,8 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= -golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -269,18 +269,18 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= -golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= -golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= +golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20201211185031-d93e913c1a58/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= From 837c5d767df85f8aee42e78a7c1e52e5c3d93621 Mon Sep 17 00:00:00 2001 From: Reuben Miller Date: Sat, 18 May 2024 12:49:02 +0200 Subject: [PATCH 20/23] expose env variables to WSL --- pkg/cmd/remoteaccess/connect/run/run.manual.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pkg/cmd/remoteaccess/connect/run/run.manual.go b/pkg/cmd/remoteaccess/connect/run/run.manual.go index bd27a8db6..4f1a31cc1 100644 --- a/pkg/cmd/remoteaccess/connect/run/run.manual.go +++ b/pkg/cmd/remoteaccess/connect/run/run.manual.go @@ -194,6 +194,11 @@ func (n *CmdRun) RunE(cmd *cobra.Command, args []string) error { // complex scripts runCmd.Env = append(runCmd.Env, fmt.Sprintf("PORT=%s", port)) runCmd.Env = append(runCmd.Env, fmt.Sprintf("TARGET=%s", host)) + runCmd.Env = append(runCmd.Env, fmt.Sprintf("DEVICE=%s", device)) + + // Support WSL environments and expose variables to WSL + runCmd.Env = append(runCmd.Env, "WSLENV=PORT/u:TARGET/u:DEVICE/u:C8Y_HOST/u") + runCmd.Stdout = n.factory.IOStreams.Out runCmd.Stdin = n.factory.IOStreams.In runCmd.Stderr = n.factory.IOStreams.ErrOut From fab94db29b46fa8bf1f98c1664112b05fad58010 Mon Sep 17 00:00:00 2001 From: Reuben Miller Date: Sat, 18 May 2024 13:46:23 +0200 Subject: [PATCH 21/23] use exit code returned by the subcommand --- pkg/cmd/remoteaccess/connect/run/run.manual.go | 14 ++++++++++++-- pkg/cmd/remoteaccess/connect/ssh/ssh.manual.go | 15 ++++++++++++++- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/pkg/cmd/remoteaccess/connect/run/run.manual.go b/pkg/cmd/remoteaccess/connect/run/run.manual.go index 4f1a31cc1..25d3fcf0d 100644 --- a/pkg/cmd/remoteaccess/connect/run/run.manual.go +++ b/pkg/cmd/remoteaccess/connect/run/run.manual.go @@ -209,10 +209,20 @@ func (n *CmdRun) RunE(cmd *cobra.Command, args []string) error { fmt.Fprintln(n.factory.IOStreams.ErrOut, cs.Green(fmt.Sprintf("Starting external command on %s (%s)\n", device, strings.TrimRight(client.BaseURL.String(), "/")))) start := time.Now() - sshErr := runCmd.Run() + runErr := runCmd.Run() duration := time.Since(start).Truncate(time.Millisecond) fmt.Fprintf(n.factory.IOStreams.ErrOut, "Duration: %s\n", duration) - return nil, sshErr + // Use exit code from the command + if runCmd.ProcessState != nil { + if runCmd.ProcessState.ExitCode() != 0 { + return nil, cmderrors.NewErrorWithExitCode( + cmderrors.ExitCode(runCmd.ProcessState.ExitCode()), + runErr, + ) + } + } + + return nil, runErr }) } diff --git a/pkg/cmd/remoteaccess/connect/ssh/ssh.manual.go b/pkg/cmd/remoteaccess/connect/ssh/ssh.manual.go index 5b05277e6..a57b4151e 100644 --- a/pkg/cmd/remoteaccess/connect/ssh/ssh.manual.go +++ b/pkg/cmd/remoteaccess/connect/ssh/ssh.manual.go @@ -10,6 +10,7 @@ import ( "github.com/MakeNowJust/heredoc/v2" "github.com/reubenmiller/go-c8y-cli/v2/pkg/c8yfetcher" "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/subcommand" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmderrors" "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmdutil" "github.com/reubenmiller/go-c8y-cli/v2/pkg/completion" "github.com/reubenmiller/go-c8y-cli/v2/pkg/flags" @@ -63,6 +64,9 @@ func NewCmdSSH(f *cmdutil.Factory) *CmdSSH { $ c8y remoteaccess connect ssh --device 12345 --user admin -- systemctl status Use a non-interactive session to execute a single command and print the result + + $ c8y remoteaccess connect ssh --device 12345 --user admin -- "sh -c 'cat /etc/os-release'" + use a non-interactive session to execute a custom shell command (notice the surrounding double quotes on the command!) `), RunE: ccmd.RunE, } @@ -191,7 +195,6 @@ func (n *CmdSSH) RunE(cmd *cobra.Command, args []string) error { sshCmd.Stdout = n.factory.IOStreams.Out sshCmd.Stdin = n.factory.IOStreams.In sshCmd.Stderr = n.factory.IOStreams.ErrOut - log.Infof("Executing command: ssh %s\n", strings.Join(sshArgs, " ")) cs := n.factory.IOStreams.ColorScheme() @@ -202,6 +205,16 @@ func (n *CmdSSH) RunE(cmd *cobra.Command, args []string) error { duration := time.Since(start).Truncate(time.Millisecond) fmt.Fprintf(n.factory.IOStreams.ErrOut, "Duration: %s\n", duration) + // Use exit code from the command + if sshCmd.ProcessState != nil { + if sshCmd.ProcessState.ExitCode() != 0 { + return nil, cmderrors.NewErrorWithExitCode( + cmderrors.ExitCode(sshCmd.ProcessState.ExitCode()), + sshErr, + ) + } + } + return nil, sshErr }) } From a6fe19890f58770b2edbf5e88ee5b5ee0be3075b Mon Sep 17 00:00:00 2001 From: Reuben Miller Date: Sat, 18 May 2024 13:46:38 +0200 Subject: [PATCH 22/23] add on-demand test script used for manual validation --- .../remoteaccess/test-remote-access.sh | 129 ++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100755 tests/on-demand/remoteaccess/test-remote-access.sh diff --git a/tests/on-demand/remoteaccess/test-remote-access.sh b/tests/on-demand/remoteaccess/test-remote-access.sh new file mode 100755 index 000000000..43b52ba58 --- /dev/null +++ b/tests/on-demand/remoteaccess/test-remote-access.sh @@ -0,0 +1,129 @@ +#!/usr/bin/env bash +set -ex + +# +# On demand test script to check the remoteaccess feature +# Due to dependencies it is hard to automate this, however this may change in the future +# by using thin-edge.io as a test client to automate again in a containerized environment. +# + +if [ $# -lt 1 ]; then + fail "Missing required argument" +fi + +DEVICE="$1" + +SSH_USER=root +if [ $# -gt 1 ]; then + SSH_USER="$2" +fi + +fail() { + echo "FAILED: $*" >&2 + exit 1 +} + +test_configuration() { + echo "Running tests: c8y remoteaccess configurations" >&2 + + OUTPUT=$(c8y remoteaccess configurations list --device "$DEVICE" --select id,name,protocol -o csv) + [ -n "$OUTPUT" ] || fail "Expected device to have at least 1 configuration" + + # + # completions + # + # list + LINE_COUNT=$(c8y __complete remoteaccess configurations list --device "" | wc -l | xargs) + [ "$LINE_COUNT" -gt 2 ] || fail "Expected completion to have greater than 2 lines. got=$LINE_COUNT" + + # get + LINE_COUNT=$(c8y __complete remoteaccess configurations get --device "" | wc -l | xargs) + [ "$LINE_COUNT" -gt 2 ] || fail "Expected completion to have greater than 2 lines. got=$LINE_COUNT" + + LINE_COUNT=$(c8y __complete remoteaccess configurations get --device "$DEVICE" --id "" | wc -l | xargs) + [ "$LINE_COUNT" -gt 2 ] || fail "Expected completion to have greater than 2 lines. got=$LINE_COUNT" + + # delete + LINE_COUNT=$(c8y __complete remoteaccess configurations delete --device "" | wc -l | xargs) + [ "$LINE_COUNT" -gt 2 ] || fail "Expected completion to have greater than 2 lines. got=$LINE_COUNT" + + LINE_COUNT=$(c8y __complete remoteaccess configurations delete --device "$DEVICE" --id "" | wc -l | xargs) + [ "$LINE_COUNT" -gt 2 ] || fail "Expected completion to have greater than 2 lines. got=$LINE_COUNT" + + # create-* + LINE_COUNT=$(c8y __complete remoteaccess configurations create-passthrough --device "" | wc -l | xargs) + [ "$LINE_COUNT" -gt 2 ] || fail "Expected completion to have greater than 2 lines. got=$LINE_COUNT" + + LINE_COUNT=$(c8y __complete remoteaccess configurations create-telnet --device "" | wc -l | xargs) + [ "$LINE_COUNT" -gt 2 ] || fail "Expected completion to have greater than 2 lines. got=$LINE_COUNT" + + LINE_COUNT=$(c8y __complete remoteaccess configurations create-vnc --device "" | wc -l | xargs) + [ "$LINE_COUNT" -gt 2 ] || fail "Expected completion to have greater than 2 lines. got=$LINE_COUNT" + + LINE_COUNT=$(c8y __complete remoteaccess configurations create-webssh --device "" | wc -l | xargs) + [ "$LINE_COUNT" -gt 2 ] || fail "Expected completion to have greater than 2 lines. got=$LINE_COUNT" +} + +test_connect_ssh() { + echo "Running tests: c8y remoteaccess connect ssh" >&2 + set +e + OUTPUT=$(c8y remoteaccess connect ssh --device "$DEVICE" --user "$SSH_USER" -- exit 32) + LAST_EXIT_CODE="$?" + [[ "$LAST_EXIT_CODE" -eq 32 ]] || fail "Exit code did not match. got=$LAST_EXIT_CODE, expected=32" + set -e + + # Only messages from the ssh command are included in stdout + OUTPUT=$(c8y remoteaccess connect ssh --device "$DEVICE" --user "$SSH_USER" -- echo hello world) + [[ "$OUTPUT" = "hello world" ]] || fail "Output did not match. got=$OUTPUT, expected='hello world'" + + # stderr is not returned by default + OUTPUT=$(c8y remoteaccess connect ssh --device "$DEVICE" --user "$SSH_USER" -- "sh -c 'echo hello world >&2'") + [[ -z "$OUTPUT" ]] || fail "Output did not match. got=$OUTPUT, expected=" + + # stderr can be redirected to stdout + OUTPUT=$(c8y remoteaccess connect ssh --device "$DEVICE" --user "$SSH_USER" -- "sh -c 'echo hello world >&2'" 2>&1) + if ! echo "$OUTPUT" | grep "hello world"; then + fail "Expected output to contain 'hello world'. got=$OUTPUT" + fi + + # The default user can be set via env variables + eval "$(c8y settings update remoteaccess.sshuser root --shell auto)" + c8y remoteaccess connect ssh --device "$DEVICE" -- exit 0 + unset C8Y_SETTINGS_REMOTEACCESS_SSHUSER +} + +test_connect_run() { + echo "Running tests: c8y remoteaccess connect run" >&2 + # + # Run a custom command + # + OUTPUT=$(c8y remoteaccess connect run --device "$DEVICE" -- echo host=%h,port=%p) + [[ "$OUTPUT" =~ ^host=127.0.0.1,port=[0-9]+$ ]] || fail "Did not match host/port pattern" + + # shellcheck disable=SC2016 + OUTPUT=$(c8y remoteaccess connect run --device "$DEVICE" -- sh -c 'echo host=$TARGET,port=$PORT') + [[ "$OUTPUT" =~ ^host=127.0.0.1,port=[0-9]+$ ]] || fail "Did not match host/port pattern" + + # Run custom ssh command which also runs another command on the device + OUTPUT=$(c8y remoteaccess connect run --device "$DEVICE" -- ssh -p %p "$SSH_USER@%h" -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -- cat /etc/os-release) + [ -n "$OUTPUT" ] || fail "Empty ssh response" + + # Check if exit code is propogated + set +e + OUTPUT=$(c8y remoteaccess connect run --device "$DEVICE" -- sh -c 'exit 31') + LAST_EXIT_CODE="$?" + [[ "$LAST_EXIT_CODE" -eq 31 ]] || fail "Exit code did not match. got=$LAST_EXIT_CODE, expected=31" + set -e + + # Exit code 0 does not produce an error + c8y remoteaccess connect run --device "$DEVICE" -- sh -c 'exit 0' +} + + +main() { + test_configuration + test_connect_ssh + test_connect_run +} + +main From e21c8e8c07d99086bd4251183bc79dd99e91378b Mon Sep 17 00:00:00 2001 From: Reuben Miller Date: Sat, 18 May 2024 21:31:44 +0200 Subject: [PATCH 23/23] add remote access docs --- .../cli/c8y/remoteaccess/c8y_remoteaccess.md | 75 ++++ .../remoteaccess/c8y_remoteaccess_server.md | 121 ++++++ .../c8y_remoteaccess_configurations.md | 75 ++++ ...ccess_configurations_create-passthrough.md | 104 +++++ ...moteaccess_configurations_create-telnet.md | 95 +++++ ..._remoteaccess_configurations_create-vnc.md | 99 +++++ ...moteaccess_configurations_create-webssh.md | 103 +++++ .../c8y_remoteaccess_configurations_delete.md | 90 +++++ .../c8y_remoteaccess_configurations_get.md | 89 +++++ .../c8y_remoteaccess_configurations_list.md | 89 +++++ .../connect/c8y_remoteaccess_connect.md | 75 ++++ .../connect/c8y_remoteaccess_connect_run.md | 110 ++++++ .../connect/c8y_remoteaccess_connect_ssh.md | 111 ++++++ docs/go-c8y-cli/docs/examples/remoteaccess.md | 360 ++++++++++++++++++ 14 files changed, 1596 insertions(+) create mode 100644 docs/go-c8y-cli/docs/cli/c8y/remoteaccess/c8y_remoteaccess.md create mode 100644 docs/go-c8y-cli/docs/cli/c8y/remoteaccess/c8y_remoteaccess_server.md create mode 100644 docs/go-c8y-cli/docs/cli/c8y/remoteaccess/configurations/c8y_remoteaccess_configurations.md create mode 100644 docs/go-c8y-cli/docs/cli/c8y/remoteaccess/configurations/c8y_remoteaccess_configurations_create-passthrough.md create mode 100644 docs/go-c8y-cli/docs/cli/c8y/remoteaccess/configurations/c8y_remoteaccess_configurations_create-telnet.md create mode 100644 docs/go-c8y-cli/docs/cli/c8y/remoteaccess/configurations/c8y_remoteaccess_configurations_create-vnc.md create mode 100644 docs/go-c8y-cli/docs/cli/c8y/remoteaccess/configurations/c8y_remoteaccess_configurations_create-webssh.md create mode 100644 docs/go-c8y-cli/docs/cli/c8y/remoteaccess/configurations/c8y_remoteaccess_configurations_delete.md create mode 100644 docs/go-c8y-cli/docs/cli/c8y/remoteaccess/configurations/c8y_remoteaccess_configurations_get.md create mode 100644 docs/go-c8y-cli/docs/cli/c8y/remoteaccess/configurations/c8y_remoteaccess_configurations_list.md create mode 100644 docs/go-c8y-cli/docs/cli/c8y/remoteaccess/connect/c8y_remoteaccess_connect.md create mode 100644 docs/go-c8y-cli/docs/cli/c8y/remoteaccess/connect/c8y_remoteaccess_connect_run.md create mode 100644 docs/go-c8y-cli/docs/cli/c8y/remoteaccess/connect/c8y_remoteaccess_connect_ssh.md create mode 100644 docs/go-c8y-cli/docs/examples/remoteaccess.md diff --git a/docs/go-c8y-cli/docs/cli/c8y/remoteaccess/c8y_remoteaccess.md b/docs/go-c8y-cli/docs/cli/c8y/remoteaccess/c8y_remoteaccess.md new file mode 100644 index 000000000..529a7a75f --- /dev/null +++ b/docs/go-c8y-cli/docs/cli/c8y/remoteaccess/c8y_remoteaccess.md @@ -0,0 +1,75 @@ +--- +category: remoteaccess +title: c8y remoteaccess +--- +Cumulocity remoteaccess management + +### Synopsis + +Cumulocity remoteaccess management + +### Options + +``` + -h, --help help for remoteaccess +``` + +### Options inherited from parent commands + +``` + --abortOnErrors int Abort batch when reaching specified number of errors (default 10) + --allowEmptyPipe Don't fail when piped input is empty (stdin) + --cache Enable cached responses + --cacheBodyPaths strings Cache should limit hashing of selected paths in the json body. Empty indicates all values + --cacheTTL string Cache time-to-live (TTL) as a duration, i.e. 60s, 2m (default "60s") + -c, --compact Compact instead of pretty-printed output when using json output. Pretty print is the default if output is the terminal + --confirm Prompt for confirmation + --confirmText string Custom confirmation text + --currentPage int Current page which should be returned + --customQueryParam strings add custom URL query parameters. i.e. --customQueryParam 'withCustomOption=true,myOtherOption=myvalue' + --debug Set very verbose log messages + --delay string delay after each request, i.e. 5ms, 1.2s (default "0ms") + --delayBefore string delay before each request, i.e. 5ms, 1.2s (default "0ms") + --dry Dry run. Don't send any data to the server + --dryFormat string Dry run output format. i.e. json, dump, markdown or curl (default "markdown") + --examples Show examples for the current command + --filter stringArray Apply a client side filter to response before returning it to the user + --flatten flatten json output by replacing nested json properties with properties where their names are represented by dot notation + -f, --force Do not prompt for confirmation. Ignored when using --confirm + -H, --header strings custom headers. i.e. --header "Accept: value, AnotherHeader: myvalue" + --includeAll Include all results by iterating through each page + -k, --insecure Allow insecure server connections when using SSL + -l, --logMessage string Add custom message to the activity log + --maxJobs int Maximum number of jobs. 0 = unlimited (use with caution!) + --noAccept Ignore Accept header will remove the Accept header from requests, however PUT and POST requests will only see the effect + --noCache Force disabling of cached responses (overwrites cache setting) + -M, --noColor Don't use colors when displaying log entries on the console + --noLog Disables the activity log for the current command + --noProgress Disable progress bars + --noProxy Ignore the proxy settings + -n, --nullInput Don't read the input (stdin). Useful if using in shell for/while loops + -o, --output string Output format i.e. table, json, csv, csvheader (default "table") + --outputFile string Save JSON output to file (after select/view) + --outputFileRaw string Save raw response to file (before select/view) + --outputTemplate string jsonnet template to apply to the output + -p, --pageSize int Maximum results per page (default 5) + --progress Show progress bar. This will also disable any other verbose output + --proxy string Proxy setting, i.e. http://10.0.0.1:8080 + -r, --raw Show raw response. This mode will force output=json and view=off + --retries int Max number of attempts when a failed http call is encountered (default 3) + --select stringArray Comma separated list of properties to return. wildcards and globstar accepted, i.e. --select 'id,name,type,**.serialNumber' + --session string Session configuration + -P, --sessionPassword string Override session password + -U, --sessionUsername string Override session username. i.e. peter or t1234/peter (with tenant) + --silentExit Silent status codes do not affect the exit code + --silentStatusCodes string Status codes which will not print out an error message + --timeout string Request timeout duration, i.e. 60s, 2m (default "60s") + --totalPages int Total number of pages to get + -v, --verbose Verbose logging + --view string Use views when displaying data on the terminal. Disable using --view off (default "auto") + --withError Errors will be printed on stdout instead of stderr + --withTotalElements Request Cumulocity to include the total elements in the response statistics under .statistics.totalElements (introduced in 10.13) + -t, --withTotalPages Request Cumulocity to include the total pages in the response statistics under .statistics.totalPages + --workers int Number of workers (default 1) +``` + diff --git a/docs/go-c8y-cli/docs/cli/c8y/remoteaccess/c8y_remoteaccess_server.md b/docs/go-c8y-cli/docs/cli/c8y/remoteaccess/c8y_remoteaccess_server.md new file mode 100644 index 000000000..b427478a8 --- /dev/null +++ b/docs/go-c8y-cli/docs/cli/c8y/remoteaccess/c8y_remoteaccess_server.md @@ -0,0 +1,121 @@ +--- +category: remoteaccess +title: c8y remoteaccess server +--- +Start a local proxy server + +### Synopsis + + + Start a local proxy server + + You can add use the remote access local proxy within your ssh config file, to use it to + connect to your device with ssh without having to manually launch the proxy yourself! + + To do this add the following configuration to your device. + + --- + Host + User + PreferredAuthentications publickey + IdentityFile + ServerAliveInterval 120 + StrictHostKeyChecking no + UserKnownHostsFile /dev/null + ProxyCommand c8y remoteaccess server --device %n --listen - + --- + + +``` +c8y remoteaccess server [flags] +``` + +### Examples + +``` +$ c8y remoteaccess server --device 12345 +Start a local proxy server on a random local port + +$ c8y remoteaccess server --device 12345 --listen - +Start a local proxy server reading from stdin and writing to stdout (useful for usage with the ProxyCommand in ssh) + +$ c8y remoteaccess server --device 12345 --listen unix:///run/example.sock +Start a local proxy using a UNIX socket + +$ c8y remoteaccess server --device 12345 --listen 127.0.0.1:22022 +Start a local proxy using a local TCP server on a fixed port 22022 + +$ c8y remoteaccess server --device 12345 --configuration "*rugpi*" --browser +Start a local proxy and match on the configuration using wildcards, then open the browser to the endpoint + +``` + +### Options + +``` + --browser Open the endpoint in a browser (if available) + --configuration string Remote Access Configuration. Accepts wildcards + --device strings Device + -h, --help help for server + --listen string Listen. unix:///run/example.sock (default "127.0.0.1:0") +``` + +### Options inherited from parent commands + +``` + --abortOnErrors int Abort batch when reaching specified number of errors (default 10) + --allowEmptyPipe Don't fail when piped input is empty (stdin) + --cache Enable cached responses + --cacheBodyPaths strings Cache should limit hashing of selected paths in the json body. Empty indicates all values + --cacheTTL string Cache time-to-live (TTL) as a duration, i.e. 60s, 2m (default "60s") + -c, --compact Compact instead of pretty-printed output when using json output. Pretty print is the default if output is the terminal + --confirm Prompt for confirmation + --confirmText string Custom confirmation text + --currentPage int Current page which should be returned + --customQueryParam strings add custom URL query parameters. i.e. --customQueryParam 'withCustomOption=true,myOtherOption=myvalue' + --debug Set very verbose log messages + --delay string delay after each request, i.e. 5ms, 1.2s (default "0ms") + --delayBefore string delay before each request, i.e. 5ms, 1.2s (default "0ms") + --dry Dry run. Don't send any data to the server + --dryFormat string Dry run output format. i.e. json, dump, markdown or curl (default "markdown") + --examples Show examples for the current command + --filter stringArray Apply a client side filter to response before returning it to the user + --flatten flatten json output by replacing nested json properties with properties where their names are represented by dot notation + -f, --force Do not prompt for confirmation. Ignored when using --confirm + -H, --header strings custom headers. i.e. --header "Accept: value, AnotherHeader: myvalue" + --includeAll Include all results by iterating through each page + -k, --insecure Allow insecure server connections when using SSL + -l, --logMessage string Add custom message to the activity log + --maxJobs int Maximum number of jobs. 0 = unlimited (use with caution!) + --noAccept Ignore Accept header will remove the Accept header from requests, however PUT and POST requests will only see the effect + --noCache Force disabling of cached responses (overwrites cache setting) + -M, --noColor Don't use colors when displaying log entries on the console + --noLog Disables the activity log for the current command + --noProgress Disable progress bars + --noProxy Ignore the proxy settings + -n, --nullInput Don't read the input (stdin). Useful if using in shell for/while loops + -o, --output string Output format i.e. table, json, csv, csvheader (default "table") + --outputFile string Save JSON output to file (after select/view) + --outputFileRaw string Save raw response to file (before select/view) + --outputTemplate string jsonnet template to apply to the output + -p, --pageSize int Maximum results per page (default 5) + --progress Show progress bar. This will also disable any other verbose output + --proxy string Proxy setting, i.e. http://10.0.0.1:8080 + -r, --raw Show raw response. This mode will force output=json and view=off + --retries int Max number of attempts when a failed http call is encountered (default 3) + --select stringArray Comma separated list of properties to return. wildcards and globstar accepted, i.e. --select 'id,name,type,**.serialNumber' + --session string Session configuration + -P, --sessionPassword string Override session password + -U, --sessionUsername string Override session username. i.e. peter or t1234/peter (with tenant) + --silentExit Silent status codes do not affect the exit code + --silentStatusCodes string Status codes which will not print out an error message + --timeout string Request timeout duration, i.e. 60s, 2m (default "60s") + --totalPages int Total number of pages to get + -v, --verbose Verbose logging + --view string Use views when displaying data on the terminal. Disable using --view off (default "auto") + --withError Errors will be printed on stdout instead of stderr + --withTotalElements Request Cumulocity to include the total elements in the response statistics under .statistics.totalElements (introduced in 10.13) + -t, --withTotalPages Request Cumulocity to include the total pages in the response statistics under .statistics.totalPages + --workers int Number of workers (default 1) +``` + diff --git a/docs/go-c8y-cli/docs/cli/c8y/remoteaccess/configurations/c8y_remoteaccess_configurations.md b/docs/go-c8y-cli/docs/cli/c8y/remoteaccess/configurations/c8y_remoteaccess_configurations.md new file mode 100644 index 000000000..88e40bbed --- /dev/null +++ b/docs/go-c8y-cli/docs/cli/c8y/remoteaccess/configurations/c8y_remoteaccess_configurations.md @@ -0,0 +1,75 @@ +--- +category: configurations +title: c8y remoteaccess configurations +--- +Manage remote access configurations + +### Synopsis + +Cloud Remote Access configuration management + +### Options + +``` + -h, --help help for configurations +``` + +### Options inherited from parent commands + +``` + --abortOnErrors int Abort batch when reaching specified number of errors (default 10) + --allowEmptyPipe Don't fail when piped input is empty (stdin) + --cache Enable cached responses + --cacheBodyPaths strings Cache should limit hashing of selected paths in the json body. Empty indicates all values + --cacheTTL string Cache time-to-live (TTL) as a duration, i.e. 60s, 2m (default "60s") + -c, --compact Compact instead of pretty-printed output when using json output. Pretty print is the default if output is the terminal + --confirm Prompt for confirmation + --confirmText string Custom confirmation text + --currentPage int Current page which should be returned + --customQueryParam strings add custom URL query parameters. i.e. --customQueryParam 'withCustomOption=true,myOtherOption=myvalue' + --debug Set very verbose log messages + --delay string delay after each request, i.e. 5ms, 1.2s (default "0ms") + --delayBefore string delay before each request, i.e. 5ms, 1.2s (default "0ms") + --dry Dry run. Don't send any data to the server + --dryFormat string Dry run output format. i.e. json, dump, markdown or curl (default "markdown") + --examples Show examples for the current command + --filter stringArray Apply a client side filter to response before returning it to the user + --flatten flatten json output by replacing nested json properties with properties where their names are represented by dot notation + -f, --force Do not prompt for confirmation. Ignored when using --confirm + -H, --header strings custom headers. i.e. --header "Accept: value, AnotherHeader: myvalue" + --includeAll Include all results by iterating through each page + -k, --insecure Allow insecure server connections when using SSL + -l, --logMessage string Add custom message to the activity log + --maxJobs int Maximum number of jobs. 0 = unlimited (use with caution!) + --noAccept Ignore Accept header will remove the Accept header from requests, however PUT and POST requests will only see the effect + --noCache Force disabling of cached responses (overwrites cache setting) + -M, --noColor Don't use colors when displaying log entries on the console + --noLog Disables the activity log for the current command + --noProgress Disable progress bars + --noProxy Ignore the proxy settings + -n, --nullInput Don't read the input (stdin). Useful if using in shell for/while loops + -o, --output string Output format i.e. table, json, csv, csvheader (default "table") + --outputFile string Save JSON output to file (after select/view) + --outputFileRaw string Save raw response to file (before select/view) + --outputTemplate string jsonnet template to apply to the output + -p, --pageSize int Maximum results per page (default 5) + --progress Show progress bar. This will also disable any other verbose output + --proxy string Proxy setting, i.e. http://10.0.0.1:8080 + -r, --raw Show raw response. This mode will force output=json and view=off + --retries int Max number of attempts when a failed http call is encountered (default 3) + --select stringArray Comma separated list of properties to return. wildcards and globstar accepted, i.e. --select 'id,name,type,**.serialNumber' + --session string Session configuration + -P, --sessionPassword string Override session password + -U, --sessionUsername string Override session username. i.e. peter or t1234/peter (with tenant) + --silentExit Silent status codes do not affect the exit code + --silentStatusCodes string Status codes which will not print out an error message + --timeout string Request timeout duration, i.e. 60s, 2m (default "60s") + --totalPages int Total number of pages to get + -v, --verbose Verbose logging + --view string Use views when displaying data on the terminal. Disable using --view off (default "auto") + --withError Errors will be printed on stdout instead of stderr + --withTotalElements Request Cumulocity to include the total elements in the response statistics under .statistics.totalElements (introduced in 10.13) + -t, --withTotalPages Request Cumulocity to include the total pages in the response statistics under .statistics.totalPages + --workers int Number of workers (default 1) +``` + diff --git a/docs/go-c8y-cli/docs/cli/c8y/remoteaccess/configurations/c8y_remoteaccess_configurations_create-passthrough.md b/docs/go-c8y-cli/docs/cli/c8y/remoteaccess/configurations/c8y_remoteaccess_configurations_create-passthrough.md new file mode 100644 index 000000000..9438fed86 --- /dev/null +++ b/docs/go-c8y-cli/docs/cli/c8y/remoteaccess/configurations/c8y_remoteaccess_configurations_create-passthrough.md @@ -0,0 +1,104 @@ +--- +category: configurations +title: c8y remoteaccess configurations create-passthrough +--- +Create passthrough configuration + +### Synopsis + +Create a passthrough configuration which enables you to connect +directly to the device (via Cumulocity IoT) using a native client such as ssh. + +After a passthrough connection has been added, you can open a proxy to it using +one of the following commands: + + * c8y remoteaccess server + * c8y remoteaccess connect ssh + + +``` +c8y remoteaccess configurations create-passthrough [flags] +``` + +### Examples + +``` +$ c8y remoteaccess configurations create-passthrough --device device01 +Create a SSH passthrough configuration to the localhost + +$ c8y remoteaccess configurations create-passthrough --device device01 --hostname customhost --port 1234 --name "My custom configuration" +Create a SSH passthrough configuration with custom details + +``` + +### Options + +``` + --device strings Device (accepts pipeline) + -h, --help help for create-passthrough + --hostname string Hostname (default "127.0.0.1") + --name string Connection name (default "passthrough") + --port int Port (default 22) + --processingMode string Cumulocity processing mode + --protocol string Protocol (default "PASSTHROUGH") +``` + +### Options inherited from parent commands + +``` + --abortOnErrors int Abort batch when reaching specified number of errors (default 10) + --allowEmptyPipe Don't fail when piped input is empty (stdin) + --cache Enable cached responses + --cacheBodyPaths strings Cache should limit hashing of selected paths in the json body. Empty indicates all values + --cacheTTL string Cache time-to-live (TTL) as a duration, i.e. 60s, 2m (default "60s") + -c, --compact Compact instead of pretty-printed output when using json output. Pretty print is the default if output is the terminal + --confirm Prompt for confirmation + --confirmText string Custom confirmation text + --currentPage int Current page which should be returned + --customQueryParam strings add custom URL query parameters. i.e. --customQueryParam 'withCustomOption=true,myOtherOption=myvalue' + --debug Set very verbose log messages + --delay string delay after each request, i.e. 5ms, 1.2s (default "0ms") + --delayBefore string delay before each request, i.e. 5ms, 1.2s (default "0ms") + --dry Dry run. Don't send any data to the server + --dryFormat string Dry run output format. i.e. json, dump, markdown or curl (default "markdown") + --examples Show examples for the current command + --filter stringArray Apply a client side filter to response before returning it to the user + --flatten flatten json output by replacing nested json properties with properties where their names are represented by dot notation + -f, --force Do not prompt for confirmation. Ignored when using --confirm + -H, --header strings custom headers. i.e. --header "Accept: value, AnotherHeader: myvalue" + --includeAll Include all results by iterating through each page + -k, --insecure Allow insecure server connections when using SSL + -l, --logMessage string Add custom message to the activity log + --maxJobs int Maximum number of jobs. 0 = unlimited (use with caution!) + --noAccept Ignore Accept header will remove the Accept header from requests, however PUT and POST requests will only see the effect + --noCache Force disabling of cached responses (overwrites cache setting) + -M, --noColor Don't use colors when displaying log entries on the console + --noLog Disables the activity log for the current command + --noProgress Disable progress bars + --noProxy Ignore the proxy settings + -n, --nullInput Don't read the input (stdin). Useful if using in shell for/while loops + -o, --output string Output format i.e. table, json, csv, csvheader (default "table") + --outputFile string Save JSON output to file (after select/view) + --outputFileRaw string Save raw response to file (before select/view) + --outputTemplate string jsonnet template to apply to the output + -p, --pageSize int Maximum results per page (default 5) + --progress Show progress bar. This will also disable any other verbose output + --proxy string Proxy setting, i.e. http://10.0.0.1:8080 + -r, --raw Show raw response. This mode will force output=json and view=off + --retries int Max number of attempts when a failed http call is encountered (default 3) + --select stringArray Comma separated list of properties to return. wildcards and globstar accepted, i.e. --select 'id,name,type,**.serialNumber' + --session string Session configuration + -P, --sessionPassword string Override session password + -U, --sessionUsername string Override session username. i.e. peter or t1234/peter (with tenant) + --silentExit Silent status codes do not affect the exit code + --silentStatusCodes string Status codes which will not print out an error message + --timeout string Request timeout duration, i.e. 60s, 2m (default "60s") + --totalPages int Total number of pages to get + -v, --verbose Verbose logging + --view string Use views when displaying data on the terminal. Disable using --view off (default "auto") + --withError Errors will be printed on stdout instead of stderr + --withTotalElements Request Cumulocity to include the total elements in the response statistics under .statistics.totalElements (introduced in 10.13) + -t, --withTotalPages Request Cumulocity to include the total pages in the response statistics under .statistics.totalPages + --workers int Number of workers (default 1) +``` + diff --git a/docs/go-c8y-cli/docs/cli/c8y/remoteaccess/configurations/c8y_remoteaccess_configurations_create-telnet.md b/docs/go-c8y-cli/docs/cli/c8y/remoteaccess/configurations/c8y_remoteaccess_configurations_create-telnet.md new file mode 100644 index 000000000..c1eb1c98c --- /dev/null +++ b/docs/go-c8y-cli/docs/cli/c8y/remoteaccess/configurations/c8y_remoteaccess_configurations_create-telnet.md @@ -0,0 +1,95 @@ +--- +category: configurations +title: c8y remoteaccess configurations create-telnet +--- +Create telnet configuration + +### Synopsis + +Create a new Telnet configuration. If no arguments are provided +then sensible defaults will be used. + + +``` +c8y remoteaccess configurations create-telnet [flags] +``` + +### Examples + +``` +$ c8y remoteaccess configurations create-telnet --device device01 +Create a telnet configuration + +``` + +### Options + +``` + --device strings Device (accepts pipeline) + -h, --help help for create-telnet + --hostname string Hostname (default "127.0.0.1") + --name string Connection name (default "telnet") + --port int Port (default 23) + --processingMode string Cumulocity processing mode + --protocol string Protocol (default "TELNET") +``` + +### Options inherited from parent commands + +``` + --abortOnErrors int Abort batch when reaching specified number of errors (default 10) + --allowEmptyPipe Don't fail when piped input is empty (stdin) + --cache Enable cached responses + --cacheBodyPaths strings Cache should limit hashing of selected paths in the json body. Empty indicates all values + --cacheTTL string Cache time-to-live (TTL) as a duration, i.e. 60s, 2m (default "60s") + -c, --compact Compact instead of pretty-printed output when using json output. Pretty print is the default if output is the terminal + --confirm Prompt for confirmation + --confirmText string Custom confirmation text + --currentPage int Current page which should be returned + --customQueryParam strings add custom URL query parameters. i.e. --customQueryParam 'withCustomOption=true,myOtherOption=myvalue' + --debug Set very verbose log messages + --delay string delay after each request, i.e. 5ms, 1.2s (default "0ms") + --delayBefore string delay before each request, i.e. 5ms, 1.2s (default "0ms") + --dry Dry run. Don't send any data to the server + --dryFormat string Dry run output format. i.e. json, dump, markdown or curl (default "markdown") + --examples Show examples for the current command + --filter stringArray Apply a client side filter to response before returning it to the user + --flatten flatten json output by replacing nested json properties with properties where their names are represented by dot notation + -f, --force Do not prompt for confirmation. Ignored when using --confirm + -H, --header strings custom headers. i.e. --header "Accept: value, AnotherHeader: myvalue" + --includeAll Include all results by iterating through each page + -k, --insecure Allow insecure server connections when using SSL + -l, --logMessage string Add custom message to the activity log + --maxJobs int Maximum number of jobs. 0 = unlimited (use with caution!) + --noAccept Ignore Accept header will remove the Accept header from requests, however PUT and POST requests will only see the effect + --noCache Force disabling of cached responses (overwrites cache setting) + -M, --noColor Don't use colors when displaying log entries on the console + --noLog Disables the activity log for the current command + --noProgress Disable progress bars + --noProxy Ignore the proxy settings + -n, --nullInput Don't read the input (stdin). Useful if using in shell for/while loops + -o, --output string Output format i.e. table, json, csv, csvheader (default "table") + --outputFile string Save JSON output to file (after select/view) + --outputFileRaw string Save raw response to file (before select/view) + --outputTemplate string jsonnet template to apply to the output + -p, --pageSize int Maximum results per page (default 5) + --progress Show progress bar. This will also disable any other verbose output + --proxy string Proxy setting, i.e. http://10.0.0.1:8080 + -r, --raw Show raw response. This mode will force output=json and view=off + --retries int Max number of attempts when a failed http call is encountered (default 3) + --select stringArray Comma separated list of properties to return. wildcards and globstar accepted, i.e. --select 'id,name,type,**.serialNumber' + --session string Session configuration + -P, --sessionPassword string Override session password + -U, --sessionUsername string Override session username. i.e. peter or t1234/peter (with tenant) + --silentExit Silent status codes do not affect the exit code + --silentStatusCodes string Status codes which will not print out an error message + --timeout string Request timeout duration, i.e. 60s, 2m (default "60s") + --totalPages int Total number of pages to get + -v, --verbose Verbose logging + --view string Use views when displaying data on the terminal. Disable using --view off (default "auto") + --withError Errors will be printed on stdout instead of stderr + --withTotalElements Request Cumulocity to include the total elements in the response statistics under .statistics.totalElements (introduced in 10.13) + -t, --withTotalPages Request Cumulocity to include the total pages in the response statistics under .statistics.totalPages + --workers int Number of workers (default 1) +``` + diff --git a/docs/go-c8y-cli/docs/cli/c8y/remoteaccess/configurations/c8y_remoteaccess_configurations_create-vnc.md b/docs/go-c8y-cli/docs/cli/c8y/remoteaccess/configurations/c8y_remoteaccess_configurations_create-vnc.md new file mode 100644 index 000000000..6a56bb762 --- /dev/null +++ b/docs/go-c8y-cli/docs/cli/c8y/remoteaccess/configurations/c8y_remoteaccess_configurations_create-vnc.md @@ -0,0 +1,99 @@ +--- +category: configurations +title: c8y remoteaccess configurations create-vnc +--- +Create vnc configuration + +### Synopsis + +Create a new VNC configuration. If no arguments are provided +then sensible defaults will be used. + + +``` +c8y remoteaccess configurations create-vnc [flags] +``` + +### Examples + +``` +$ c8y remoteaccess configurations create-vnc --device device01 +Create a VNC configuration that does not require a password + +$ c8y remoteaccess configurations create-vnc --device device01 --password 'asd08dcj23dsf{@#9}' +Create a VNC configuration that requires a password + +``` + +### Options + +``` + --device strings Device (accepts pipeline) + -h, --help help for create-vnc + --hostname string Hostname (default "127.0.0.1") + --name string Connection name (default "webssh") + --password string VNC Password + --port int Port (default 5900) + --processingMode string Cumulocity processing mode + --protocol string Protocol (default "VNC") +``` + +### Options inherited from parent commands + +``` + --abortOnErrors int Abort batch when reaching specified number of errors (default 10) + --allowEmptyPipe Don't fail when piped input is empty (stdin) + --cache Enable cached responses + --cacheBodyPaths strings Cache should limit hashing of selected paths in the json body. Empty indicates all values + --cacheTTL string Cache time-to-live (TTL) as a duration, i.e. 60s, 2m (default "60s") + -c, --compact Compact instead of pretty-printed output when using json output. Pretty print is the default if output is the terminal + --confirm Prompt for confirmation + --confirmText string Custom confirmation text + --currentPage int Current page which should be returned + --customQueryParam strings add custom URL query parameters. i.e. --customQueryParam 'withCustomOption=true,myOtherOption=myvalue' + --debug Set very verbose log messages + --delay string delay after each request, i.e. 5ms, 1.2s (default "0ms") + --delayBefore string delay before each request, i.e. 5ms, 1.2s (default "0ms") + --dry Dry run. Don't send any data to the server + --dryFormat string Dry run output format. i.e. json, dump, markdown or curl (default "markdown") + --examples Show examples for the current command + --filter stringArray Apply a client side filter to response before returning it to the user + --flatten flatten json output by replacing nested json properties with properties where their names are represented by dot notation + -f, --force Do not prompt for confirmation. Ignored when using --confirm + -H, --header strings custom headers. i.e. --header "Accept: value, AnotherHeader: myvalue" + --includeAll Include all results by iterating through each page + -k, --insecure Allow insecure server connections when using SSL + -l, --logMessage string Add custom message to the activity log + --maxJobs int Maximum number of jobs. 0 = unlimited (use with caution!) + --noAccept Ignore Accept header will remove the Accept header from requests, however PUT and POST requests will only see the effect + --noCache Force disabling of cached responses (overwrites cache setting) + -M, --noColor Don't use colors when displaying log entries on the console + --noLog Disables the activity log for the current command + --noProgress Disable progress bars + --noProxy Ignore the proxy settings + -n, --nullInput Don't read the input (stdin). Useful if using in shell for/while loops + -o, --output string Output format i.e. table, json, csv, csvheader (default "table") + --outputFile string Save JSON output to file (after select/view) + --outputFileRaw string Save raw response to file (before select/view) + --outputTemplate string jsonnet template to apply to the output + -p, --pageSize int Maximum results per page (default 5) + --progress Show progress bar. This will also disable any other verbose output + --proxy string Proxy setting, i.e. http://10.0.0.1:8080 + -r, --raw Show raw response. This mode will force output=json and view=off + --retries int Max number of attempts when a failed http call is encountered (default 3) + --select stringArray Comma separated list of properties to return. wildcards and globstar accepted, i.e. --select 'id,name,type,**.serialNumber' + --session string Session configuration + -P, --sessionPassword string Override session password + -U, --sessionUsername string Override session username. i.e. peter or t1234/peter (with tenant) + --silentExit Silent status codes do not affect the exit code + --silentStatusCodes string Status codes which will not print out an error message + --timeout string Request timeout duration, i.e. 60s, 2m (default "60s") + --totalPages int Total number of pages to get + -v, --verbose Verbose logging + --view string Use views when displaying data on the terminal. Disable using --view off (default "auto") + --withError Errors will be printed on stdout instead of stderr + --withTotalElements Request Cumulocity to include the total elements in the response statistics under .statistics.totalElements (introduced in 10.13) + -t, --withTotalPages Request Cumulocity to include the total pages in the response statistics under .statistics.totalPages + --workers int Number of workers (default 1) +``` + diff --git a/docs/go-c8y-cli/docs/cli/c8y/remoteaccess/configurations/c8y_remoteaccess_configurations_create-webssh.md b/docs/go-c8y-cli/docs/cli/c8y/remoteaccess/configurations/c8y_remoteaccess_configurations_create-webssh.md new file mode 100644 index 000000000..3d73581df --- /dev/null +++ b/docs/go-c8y-cli/docs/cli/c8y/remoteaccess/configurations/c8y_remoteaccess_configurations_create-webssh.md @@ -0,0 +1,103 @@ +--- +category: configurations +title: c8y remoteaccess configurations create-webssh +--- +Create web ssh configuration + +### Synopsis + +Create a new WebSSH configuration. If no arguments are provided +then sensible defaults will be used. + + +``` +c8y remoteaccess configurations create-webssh [flags] +``` + +### Examples + +``` +$ c8y remoteaccess configurations create-webssh --device device01 --username admin --password "3Xz7cEj%oAmt#dnUMP*N" +Create a webssh configuration (with username/password authentication) + +$ c8y remoteaccess configurations create-webssh --device device01 --hostname 127.0.0.1 --port 2222 --username admin --privateKey "xxxx" --publicKey "yyyyy" +Create a webssh configuration with a custom hostname and port (with ssh key authentication) + +``` + +### Options + +``` + --credentialsType string Credentials type (default "USER_PASS") + --device strings Device (accepts pipeline) + -h, --help help for create-webssh + --hostname string Hostname (default "127.0.0.1") + --name string Connection name (default "webssh") + --password string Username + --port int Port (default 22) + --privateKey string Private ssh key + --processingMode string Cumulocity processing mode + --protocol string Protocol (default "SSH") + --publicKey string Public ssh key + --username string Username +``` + +### Options inherited from parent commands + +``` + --abortOnErrors int Abort batch when reaching specified number of errors (default 10) + --allowEmptyPipe Don't fail when piped input is empty (stdin) + --cache Enable cached responses + --cacheBodyPaths strings Cache should limit hashing of selected paths in the json body. Empty indicates all values + --cacheTTL string Cache time-to-live (TTL) as a duration, i.e. 60s, 2m (default "60s") + -c, --compact Compact instead of pretty-printed output when using json output. Pretty print is the default if output is the terminal + --confirm Prompt for confirmation + --confirmText string Custom confirmation text + --currentPage int Current page which should be returned + --customQueryParam strings add custom URL query parameters. i.e. --customQueryParam 'withCustomOption=true,myOtherOption=myvalue' + --debug Set very verbose log messages + --delay string delay after each request, i.e. 5ms, 1.2s (default "0ms") + --delayBefore string delay before each request, i.e. 5ms, 1.2s (default "0ms") + --dry Dry run. Don't send any data to the server + --dryFormat string Dry run output format. i.e. json, dump, markdown or curl (default "markdown") + --examples Show examples for the current command + --filter stringArray Apply a client side filter to response before returning it to the user + --flatten flatten json output by replacing nested json properties with properties where their names are represented by dot notation + -f, --force Do not prompt for confirmation. Ignored when using --confirm + -H, --header strings custom headers. i.e. --header "Accept: value, AnotherHeader: myvalue" + --includeAll Include all results by iterating through each page + -k, --insecure Allow insecure server connections when using SSL + -l, --logMessage string Add custom message to the activity log + --maxJobs int Maximum number of jobs. 0 = unlimited (use with caution!) + --noAccept Ignore Accept header will remove the Accept header from requests, however PUT and POST requests will only see the effect + --noCache Force disabling of cached responses (overwrites cache setting) + -M, --noColor Don't use colors when displaying log entries on the console + --noLog Disables the activity log for the current command + --noProgress Disable progress bars + --noProxy Ignore the proxy settings + -n, --nullInput Don't read the input (stdin). Useful if using in shell for/while loops + -o, --output string Output format i.e. table, json, csv, csvheader (default "table") + --outputFile string Save JSON output to file (after select/view) + --outputFileRaw string Save raw response to file (before select/view) + --outputTemplate string jsonnet template to apply to the output + -p, --pageSize int Maximum results per page (default 5) + --progress Show progress bar. This will also disable any other verbose output + --proxy string Proxy setting, i.e. http://10.0.0.1:8080 + -r, --raw Show raw response. This mode will force output=json and view=off + --retries int Max number of attempts when a failed http call is encountered (default 3) + --select stringArray Comma separated list of properties to return. wildcards and globstar accepted, i.e. --select 'id,name,type,**.serialNumber' + --session string Session configuration + -P, --sessionPassword string Override session password + -U, --sessionUsername string Override session username. i.e. peter or t1234/peter (with tenant) + --silentExit Silent status codes do not affect the exit code + --silentStatusCodes string Status codes which will not print out an error message + --timeout string Request timeout duration, i.e. 60s, 2m (default "60s") + --totalPages int Total number of pages to get + -v, --verbose Verbose logging + --view string Use views when displaying data on the terminal. Disable using --view off (default "auto") + --withError Errors will be printed on stdout instead of stderr + --withTotalElements Request Cumulocity to include the total elements in the response statistics under .statistics.totalElements (introduced in 10.13) + -t, --withTotalPages Request Cumulocity to include the total pages in the response statistics under .statistics.totalPages + --workers int Number of workers (default 1) +``` + diff --git a/docs/go-c8y-cli/docs/cli/c8y/remoteaccess/configurations/c8y_remoteaccess_configurations_delete.md b/docs/go-c8y-cli/docs/cli/c8y/remoteaccess/configurations/c8y_remoteaccess_configurations_delete.md new file mode 100644 index 000000000..6094f686b --- /dev/null +++ b/docs/go-c8y-cli/docs/cli/c8y/remoteaccess/configurations/c8y_remoteaccess_configurations_delete.md @@ -0,0 +1,90 @@ +--- +category: configurations +title: c8y remoteaccess configurations delete +--- +Delete remote access configuration + +### Synopsis + +Delete an existing remote access configuration + +``` +c8y remoteaccess configurations delete [flags] +``` + +### Examples + +``` +$ c8y remoteaccess configurations delete --device device01 --id 1 +Delete an existing remote access configuration + +``` + +### Options + +``` + --device strings Device + -h, --help help for delete + --id strings Connection (accepts pipeline) + --processingMode string Cumulocity processing mode +``` + +### Options inherited from parent commands + +``` + --abortOnErrors int Abort batch when reaching specified number of errors (default 10) + --allowEmptyPipe Don't fail when piped input is empty (stdin) + --cache Enable cached responses + --cacheBodyPaths strings Cache should limit hashing of selected paths in the json body. Empty indicates all values + --cacheTTL string Cache time-to-live (TTL) as a duration, i.e. 60s, 2m (default "60s") + -c, --compact Compact instead of pretty-printed output when using json output. Pretty print is the default if output is the terminal + --confirm Prompt for confirmation + --confirmText string Custom confirmation text + --currentPage int Current page which should be returned + --customQueryParam strings add custom URL query parameters. i.e. --customQueryParam 'withCustomOption=true,myOtherOption=myvalue' + --debug Set very verbose log messages + --delay string delay after each request, i.e. 5ms, 1.2s (default "0ms") + --delayBefore string delay before each request, i.e. 5ms, 1.2s (default "0ms") + --dry Dry run. Don't send any data to the server + --dryFormat string Dry run output format. i.e. json, dump, markdown or curl (default "markdown") + --examples Show examples for the current command + --filter stringArray Apply a client side filter to response before returning it to the user + --flatten flatten json output by replacing nested json properties with properties where their names are represented by dot notation + -f, --force Do not prompt for confirmation. Ignored when using --confirm + -H, --header strings custom headers. i.e. --header "Accept: value, AnotherHeader: myvalue" + --includeAll Include all results by iterating through each page + -k, --insecure Allow insecure server connections when using SSL + -l, --logMessage string Add custom message to the activity log + --maxJobs int Maximum number of jobs. 0 = unlimited (use with caution!) + --noAccept Ignore Accept header will remove the Accept header from requests, however PUT and POST requests will only see the effect + --noCache Force disabling of cached responses (overwrites cache setting) + -M, --noColor Don't use colors when displaying log entries on the console + --noLog Disables the activity log for the current command + --noProgress Disable progress bars + --noProxy Ignore the proxy settings + -n, --nullInput Don't read the input (stdin). Useful if using in shell for/while loops + -o, --output string Output format i.e. table, json, csv, csvheader (default "table") + --outputFile string Save JSON output to file (after select/view) + --outputFileRaw string Save raw response to file (before select/view) + --outputTemplate string jsonnet template to apply to the output + -p, --pageSize int Maximum results per page (default 5) + --progress Show progress bar. This will also disable any other verbose output + --proxy string Proxy setting, i.e. http://10.0.0.1:8080 + -r, --raw Show raw response. This mode will force output=json and view=off + --retries int Max number of attempts when a failed http call is encountered (default 3) + --select stringArray Comma separated list of properties to return. wildcards and globstar accepted, i.e. --select 'id,name,type,**.serialNumber' + --session string Session configuration + -P, --sessionPassword string Override session password + -U, --sessionUsername string Override session username. i.e. peter or t1234/peter (with tenant) + --silentExit Silent status codes do not affect the exit code + --silentStatusCodes string Status codes which will not print out an error message + --timeout string Request timeout duration, i.e. 60s, 2m (default "60s") + --totalPages int Total number of pages to get + -v, --verbose Verbose logging + --view string Use views when displaying data on the terminal. Disable using --view off (default "auto") + --withError Errors will be printed on stdout instead of stderr + --withTotalElements Request Cumulocity to include the total elements in the response statistics under .statistics.totalElements (introduced in 10.13) + -t, --withTotalPages Request Cumulocity to include the total pages in the response statistics under .statistics.totalPages + --workers int Number of workers (default 1) +``` + diff --git a/docs/go-c8y-cli/docs/cli/c8y/remoteaccess/configurations/c8y_remoteaccess_configurations_get.md b/docs/go-c8y-cli/docs/cli/c8y/remoteaccess/configurations/c8y_remoteaccess_configurations_get.md new file mode 100644 index 000000000..a9d460f38 --- /dev/null +++ b/docs/go-c8y-cli/docs/cli/c8y/remoteaccess/configurations/c8y_remoteaccess_configurations_get.md @@ -0,0 +1,89 @@ +--- +category: configurations +title: c8y remoteaccess configurations get +--- +Get a remote access configuration + +### Synopsis + +Get an existing remote access configuration for a device + +``` +c8y remoteaccess configurations get [flags] +``` + +### Examples + +``` +$ c8y remoteaccess configurations get --device device01 --id 1 +Get existing remote access configuration + +``` + +### Options + +``` + --device strings Device + -h, --help help for get + --id strings Connection (accepts pipeline) +``` + +### Options inherited from parent commands + +``` + --abortOnErrors int Abort batch when reaching specified number of errors (default 10) + --allowEmptyPipe Don't fail when piped input is empty (stdin) + --cache Enable cached responses + --cacheBodyPaths strings Cache should limit hashing of selected paths in the json body. Empty indicates all values + --cacheTTL string Cache time-to-live (TTL) as a duration, i.e. 60s, 2m (default "60s") + -c, --compact Compact instead of pretty-printed output when using json output. Pretty print is the default if output is the terminal + --confirm Prompt for confirmation + --confirmText string Custom confirmation text + --currentPage int Current page which should be returned + --customQueryParam strings add custom URL query parameters. i.e. --customQueryParam 'withCustomOption=true,myOtherOption=myvalue' + --debug Set very verbose log messages + --delay string delay after each request, i.e. 5ms, 1.2s (default "0ms") + --delayBefore string delay before each request, i.e. 5ms, 1.2s (default "0ms") + --dry Dry run. Don't send any data to the server + --dryFormat string Dry run output format. i.e. json, dump, markdown or curl (default "markdown") + --examples Show examples for the current command + --filter stringArray Apply a client side filter to response before returning it to the user + --flatten flatten json output by replacing nested json properties with properties where their names are represented by dot notation + -f, --force Do not prompt for confirmation. Ignored when using --confirm + -H, --header strings custom headers. i.e. --header "Accept: value, AnotherHeader: myvalue" + --includeAll Include all results by iterating through each page + -k, --insecure Allow insecure server connections when using SSL + -l, --logMessage string Add custom message to the activity log + --maxJobs int Maximum number of jobs. 0 = unlimited (use with caution!) + --noAccept Ignore Accept header will remove the Accept header from requests, however PUT and POST requests will only see the effect + --noCache Force disabling of cached responses (overwrites cache setting) + -M, --noColor Don't use colors when displaying log entries on the console + --noLog Disables the activity log for the current command + --noProgress Disable progress bars + --noProxy Ignore the proxy settings + -n, --nullInput Don't read the input (stdin). Useful if using in shell for/while loops + -o, --output string Output format i.e. table, json, csv, csvheader (default "table") + --outputFile string Save JSON output to file (after select/view) + --outputFileRaw string Save raw response to file (before select/view) + --outputTemplate string jsonnet template to apply to the output + -p, --pageSize int Maximum results per page (default 5) + --progress Show progress bar. This will also disable any other verbose output + --proxy string Proxy setting, i.e. http://10.0.0.1:8080 + -r, --raw Show raw response. This mode will force output=json and view=off + --retries int Max number of attempts when a failed http call is encountered (default 3) + --select stringArray Comma separated list of properties to return. wildcards and globstar accepted, i.e. --select 'id,name,type,**.serialNumber' + --session string Session configuration + -P, --sessionPassword string Override session password + -U, --sessionUsername string Override session username. i.e. peter or t1234/peter (with tenant) + --silentExit Silent status codes do not affect the exit code + --silentStatusCodes string Status codes which will not print out an error message + --timeout string Request timeout duration, i.e. 60s, 2m (default "60s") + --totalPages int Total number of pages to get + -v, --verbose Verbose logging + --view string Use views when displaying data on the terminal. Disable using --view off (default "auto") + --withError Errors will be printed on stdout instead of stderr + --withTotalElements Request Cumulocity to include the total elements in the response statistics under .statistics.totalElements (introduced in 10.13) + -t, --withTotalPages Request Cumulocity to include the total pages in the response statistics under .statistics.totalPages + --workers int Number of workers (default 1) +``` + diff --git a/docs/go-c8y-cli/docs/cli/c8y/remoteaccess/configurations/c8y_remoteaccess_configurations_list.md b/docs/go-c8y-cli/docs/cli/c8y/remoteaccess/configurations/c8y_remoteaccess_configurations_list.md new file mode 100644 index 000000000..f6229cda5 --- /dev/null +++ b/docs/go-c8y-cli/docs/cli/c8y/remoteaccess/configurations/c8y_remoteaccess_configurations_list.md @@ -0,0 +1,89 @@ +--- +category: configurations +title: c8y remoteaccess configurations list +--- +List remote access configurations + +### Synopsis + +List the remote access configurations already configured for a device + + +``` +c8y remoteaccess configurations list [flags] +``` + +### Examples + +``` +$ c8y remoteaccess configurations list --device device01 +List remote access configurations for a given device + +``` + +### Options + +``` + --device strings Device (accepts pipeline) + -h, --help help for list +``` + +### Options inherited from parent commands + +``` + --abortOnErrors int Abort batch when reaching specified number of errors (default 10) + --allowEmptyPipe Don't fail when piped input is empty (stdin) + --cache Enable cached responses + --cacheBodyPaths strings Cache should limit hashing of selected paths in the json body. Empty indicates all values + --cacheTTL string Cache time-to-live (TTL) as a duration, i.e. 60s, 2m (default "60s") + -c, --compact Compact instead of pretty-printed output when using json output. Pretty print is the default if output is the terminal + --confirm Prompt for confirmation + --confirmText string Custom confirmation text + --currentPage int Current page which should be returned + --customQueryParam strings add custom URL query parameters. i.e. --customQueryParam 'withCustomOption=true,myOtherOption=myvalue' + --debug Set very verbose log messages + --delay string delay after each request, i.e. 5ms, 1.2s (default "0ms") + --delayBefore string delay before each request, i.e. 5ms, 1.2s (default "0ms") + --dry Dry run. Don't send any data to the server + --dryFormat string Dry run output format. i.e. json, dump, markdown or curl (default "markdown") + --examples Show examples for the current command + --filter stringArray Apply a client side filter to response before returning it to the user + --flatten flatten json output by replacing nested json properties with properties where their names are represented by dot notation + -f, --force Do not prompt for confirmation. Ignored when using --confirm + -H, --header strings custom headers. i.e. --header "Accept: value, AnotherHeader: myvalue" + --includeAll Include all results by iterating through each page + -k, --insecure Allow insecure server connections when using SSL + -l, --logMessage string Add custom message to the activity log + --maxJobs int Maximum number of jobs. 0 = unlimited (use with caution!) + --noAccept Ignore Accept header will remove the Accept header from requests, however PUT and POST requests will only see the effect + --noCache Force disabling of cached responses (overwrites cache setting) + -M, --noColor Don't use colors when displaying log entries on the console + --noLog Disables the activity log for the current command + --noProgress Disable progress bars + --noProxy Ignore the proxy settings + -n, --nullInput Don't read the input (stdin). Useful if using in shell for/while loops + -o, --output string Output format i.e. table, json, csv, csvheader (default "table") + --outputFile string Save JSON output to file (after select/view) + --outputFileRaw string Save raw response to file (before select/view) + --outputTemplate string jsonnet template to apply to the output + -p, --pageSize int Maximum results per page (default 5) + --progress Show progress bar. This will also disable any other verbose output + --proxy string Proxy setting, i.e. http://10.0.0.1:8080 + -r, --raw Show raw response. This mode will force output=json and view=off + --retries int Max number of attempts when a failed http call is encountered (default 3) + --select stringArray Comma separated list of properties to return. wildcards and globstar accepted, i.e. --select 'id,name,type,**.serialNumber' + --session string Session configuration + -P, --sessionPassword string Override session password + -U, --sessionUsername string Override session username. i.e. peter or t1234/peter (with tenant) + --silentExit Silent status codes do not affect the exit code + --silentStatusCodes string Status codes which will not print out an error message + --timeout string Request timeout duration, i.e. 60s, 2m (default "60s") + --totalPages int Total number of pages to get + -v, --verbose Verbose logging + --view string Use views when displaying data on the terminal. Disable using --view off (default "auto") + --withError Errors will be printed on stdout instead of stderr + --withTotalElements Request Cumulocity to include the total elements in the response statistics under .statistics.totalElements (introduced in 10.13) + -t, --withTotalPages Request Cumulocity to include the total pages in the response statistics under .statistics.totalPages + --workers int Number of workers (default 1) +``` + diff --git a/docs/go-c8y-cli/docs/cli/c8y/remoteaccess/connect/c8y_remoteaccess_connect.md b/docs/go-c8y-cli/docs/cli/c8y/remoteaccess/connect/c8y_remoteaccess_connect.md new file mode 100644 index 000000000..eb596d1f0 --- /dev/null +++ b/docs/go-c8y-cli/docs/cli/c8y/remoteaccess/connect/c8y_remoteaccess_connect.md @@ -0,0 +1,75 @@ +--- +category: connect +title: c8y remoteaccess connect +--- +Connect to a device + +### Synopsis + +Connect to a device using the protocol defined in an existing configuration + +### Options + +``` + -h, --help help for connect +``` + +### Options inherited from parent commands + +``` + --abortOnErrors int Abort batch when reaching specified number of errors (default 10) + --allowEmptyPipe Don't fail when piped input is empty (stdin) + --cache Enable cached responses + --cacheBodyPaths strings Cache should limit hashing of selected paths in the json body. Empty indicates all values + --cacheTTL string Cache time-to-live (TTL) as a duration, i.e. 60s, 2m (default "60s") + -c, --compact Compact instead of pretty-printed output when using json output. Pretty print is the default if output is the terminal + --confirm Prompt for confirmation + --confirmText string Custom confirmation text + --currentPage int Current page which should be returned + --customQueryParam strings add custom URL query parameters. i.e. --customQueryParam 'withCustomOption=true,myOtherOption=myvalue' + --debug Set very verbose log messages + --delay string delay after each request, i.e. 5ms, 1.2s (default "0ms") + --delayBefore string delay before each request, i.e. 5ms, 1.2s (default "0ms") + --dry Dry run. Don't send any data to the server + --dryFormat string Dry run output format. i.e. json, dump, markdown or curl (default "markdown") + --examples Show examples for the current command + --filter stringArray Apply a client side filter to response before returning it to the user + --flatten flatten json output by replacing nested json properties with properties where their names are represented by dot notation + -f, --force Do not prompt for confirmation. Ignored when using --confirm + -H, --header strings custom headers. i.e. --header "Accept: value, AnotherHeader: myvalue" + --includeAll Include all results by iterating through each page + -k, --insecure Allow insecure server connections when using SSL + -l, --logMessage string Add custom message to the activity log + --maxJobs int Maximum number of jobs. 0 = unlimited (use with caution!) + --noAccept Ignore Accept header will remove the Accept header from requests, however PUT and POST requests will only see the effect + --noCache Force disabling of cached responses (overwrites cache setting) + -M, --noColor Don't use colors when displaying log entries on the console + --noLog Disables the activity log for the current command + --noProgress Disable progress bars + --noProxy Ignore the proxy settings + -n, --nullInput Don't read the input (stdin). Useful if using in shell for/while loops + -o, --output string Output format i.e. table, json, csv, csvheader (default "table") + --outputFile string Save JSON output to file (after select/view) + --outputFileRaw string Save raw response to file (before select/view) + --outputTemplate string jsonnet template to apply to the output + -p, --pageSize int Maximum results per page (default 5) + --progress Show progress bar. This will also disable any other verbose output + --proxy string Proxy setting, i.e. http://10.0.0.1:8080 + -r, --raw Show raw response. This mode will force output=json and view=off + --retries int Max number of attempts when a failed http call is encountered (default 3) + --select stringArray Comma separated list of properties to return. wildcards and globstar accepted, i.e. --select 'id,name,type,**.serialNumber' + --session string Session configuration + -P, --sessionPassword string Override session password + -U, --sessionUsername string Override session username. i.e. peter or t1234/peter (with tenant) + --silentExit Silent status codes do not affect the exit code + --silentStatusCodes string Status codes which will not print out an error message + --timeout string Request timeout duration, i.e. 60s, 2m (default "60s") + --totalPages int Total number of pages to get + -v, --verbose Verbose logging + --view string Use views when displaying data on the terminal. Disable using --view off (default "auto") + --withError Errors will be printed on stdout instead of stderr + --withTotalElements Request Cumulocity to include the total elements in the response statistics under .statistics.totalElements (introduced in 10.13) + -t, --withTotalPages Request Cumulocity to include the total pages in the response statistics under .statistics.totalPages + --workers int Number of workers (default 1) +``` + diff --git a/docs/go-c8y-cli/docs/cli/c8y/remoteaccess/connect/c8y_remoteaccess_connect_run.md b/docs/go-c8y-cli/docs/cli/c8y/remoteaccess/connect/c8y_remoteaccess_connect_run.md new file mode 100644 index 000000000..12e803847 --- /dev/null +++ b/docs/go-c8y-cli/docs/cli/c8y/remoteaccess/connect/c8y_remoteaccess_connect_run.md @@ -0,0 +1,110 @@ +--- +category: connect +title: c8y remoteaccess connect run +--- +Connect to a device and run a custom command + +### Synopsis + +Connect to a device using the local proxy, then run a given command which makes use of the proxy. + +You can run any command you want by providing all arguments after the "--", for example: + + c8y remoteaccess connect run --device mydevice -- ./run-something.sh %h %p + +The local proxy server port and target can be accessed either by using variable references: + * Target - Use '%h' or the environment variable '$TARGET' + * Port - Use '%p' or the environment variable '$PORT' + +It is recommended to use the "%h" (target) "%p" (port) to refer to the local proxy server +as this avoid potential problems with variable expansions and removes the need to have to +escape variable etc. (e.g. \$HOST). Below shows an example using a custom ssh command: + + c8y remoteaccess connect run --device mydevice -- ssh -p %p root@%h + +Or you can use a shell to do something more complex and use environment variable references, though not the +single quotes around the shell command to prevent the variable expansion in your current shell. + + c8y remoteaccess connect run --device mydevice -- sh -c 'ssh -p $PORT root@$TARGET' + + +``` +c8y remoteaccess connect run [flags] +``` + +### Examples + +``` +$ c8y remoteaccess connect run --device 12345 -- ssh -p %p root@%h +Start an interactive SSH session on the device with a given ssh user + +``` + +### Options + +``` + --configuration string Remote Access Configuration + --device strings Device + -h, --help help for run + --listen string Listener address. unix:///run/example.sock (default "127.0.0.1:0") +``` + +### Options inherited from parent commands + +``` + --abortOnErrors int Abort batch when reaching specified number of errors (default 10) + --allowEmptyPipe Don't fail when piped input is empty (stdin) + --cache Enable cached responses + --cacheBodyPaths strings Cache should limit hashing of selected paths in the json body. Empty indicates all values + --cacheTTL string Cache time-to-live (TTL) as a duration, i.e. 60s, 2m (default "60s") + -c, --compact Compact instead of pretty-printed output when using json output. Pretty print is the default if output is the terminal + --confirm Prompt for confirmation + --confirmText string Custom confirmation text + --currentPage int Current page which should be returned + --customQueryParam strings add custom URL query parameters. i.e. --customQueryParam 'withCustomOption=true,myOtherOption=myvalue' + --debug Set very verbose log messages + --delay string delay after each request, i.e. 5ms, 1.2s (default "0ms") + --delayBefore string delay before each request, i.e. 5ms, 1.2s (default "0ms") + --dry Dry run. Don't send any data to the server + --dryFormat string Dry run output format. i.e. json, dump, markdown or curl (default "markdown") + --examples Show examples for the current command + --filter stringArray Apply a client side filter to response before returning it to the user + --flatten flatten json output by replacing nested json properties with properties where their names are represented by dot notation + -f, --force Do not prompt for confirmation. Ignored when using --confirm + -H, --header strings custom headers. i.e. --header "Accept: value, AnotherHeader: myvalue" + --includeAll Include all results by iterating through each page + -k, --insecure Allow insecure server connections when using SSL + -l, --logMessage string Add custom message to the activity log + --maxJobs int Maximum number of jobs. 0 = unlimited (use with caution!) + --noAccept Ignore Accept header will remove the Accept header from requests, however PUT and POST requests will only see the effect + --noCache Force disabling of cached responses (overwrites cache setting) + -M, --noColor Don't use colors when displaying log entries on the console + --noLog Disables the activity log for the current command + --noProgress Disable progress bars + --noProxy Ignore the proxy settings + -n, --nullInput Don't read the input (stdin). Useful if using in shell for/while loops + -o, --output string Output format i.e. table, json, csv, csvheader (default "table") + --outputFile string Save JSON output to file (after select/view) + --outputFileRaw string Save raw response to file (before select/view) + --outputTemplate string jsonnet template to apply to the output + -p, --pageSize int Maximum results per page (default 5) + --progress Show progress bar. This will also disable any other verbose output + --proxy string Proxy setting, i.e. http://10.0.0.1:8080 + -r, --raw Show raw response. This mode will force output=json and view=off + --retries int Max number of attempts when a failed http call is encountered (default 3) + --select stringArray Comma separated list of properties to return. wildcards and globstar accepted, i.e. --select 'id,name,type,**.serialNumber' + --session string Session configuration + -P, --sessionPassword string Override session password + -U, --sessionUsername string Override session username. i.e. peter or t1234/peter (with tenant) + --silentExit Silent status codes do not affect the exit code + --silentStatusCodes string Status codes which will not print out an error message + --timeout string Request timeout duration, i.e. 60s, 2m (default "60s") + --totalPages int Total number of pages to get + -v, --verbose Verbose logging + --view string Use views when displaying data on the terminal. Disable using --view off (default "auto") + --withError Errors will be printed on stdout instead of stderr + --withTotalElements Request Cumulocity to include the total elements in the response statistics under .statistics.totalElements (introduced in 10.13) + -t, --withTotalPages Request Cumulocity to include the total pages in the response statistics under .statistics.totalPages + --workers int Number of workers (default 1) +``` + diff --git a/docs/go-c8y-cli/docs/cli/c8y/remoteaccess/connect/c8y_remoteaccess_connect_ssh.md b/docs/go-c8y-cli/docs/cli/c8y/remoteaccess/connect/c8y_remoteaccess_connect_ssh.md new file mode 100644 index 000000000..f6c98098f --- /dev/null +++ b/docs/go-c8y-cli/docs/cli/c8y/remoteaccess/connect/c8y_remoteaccess_connect_ssh.md @@ -0,0 +1,111 @@ +--- +category: connect +title: c8y remoteaccess connect ssh +--- +Connect to a device via ssh + +### Synopsis + +Connect to a device via ssh + +Additional arguments can be passed to the ssh shell by using the "--" convention where everything +after the "--" will be passed untouched to the ssh shell. In this mode, the shell will not be +interactive, and it will return upon completion of the command. + +You can set the default ssh user to use for all ssh connections for your current c8y session file +using: + + c8y settings update remoteaccess.sshuser root + + + +``` +c8y remoteaccess connect ssh [flags] +``` + +### Examples + +``` +$ c8y remoteaccess connect ssh --device 12345 +Start an interactive SSH session on the device + +$ c8y remoteaccess connect ssh --device 12345 --user admin +Start an interactive SSH session on the device with a given ssh user + +$ c8y remoteaccess connect ssh --device 12345 --user admin -- systemctl status +Use a non-interactive session to execute a single command and print the result + +$ c8y remoteaccess connect ssh --device 12345 --user admin -- "sh -c 'cat /etc/os-release'" +use a non-interactive session to execute a custom shell command (notice the surrounding double quotes on the command!) + +``` + +### Options + +``` + --configuration string Remote Access Configuration + --device strings Device + -h, --help help for ssh + --listen string Listener address. unix:///run/example.sock (default "127.0.0.1:0") + --user string Default ssh user +``` + +### Options inherited from parent commands + +``` + --abortOnErrors int Abort batch when reaching specified number of errors (default 10) + --allowEmptyPipe Don't fail when piped input is empty (stdin) + --cache Enable cached responses + --cacheBodyPaths strings Cache should limit hashing of selected paths in the json body. Empty indicates all values + --cacheTTL string Cache time-to-live (TTL) as a duration, i.e. 60s, 2m (default "60s") + -c, --compact Compact instead of pretty-printed output when using json output. Pretty print is the default if output is the terminal + --confirm Prompt for confirmation + --confirmText string Custom confirmation text + --currentPage int Current page which should be returned + --customQueryParam strings add custom URL query parameters. i.e. --customQueryParam 'withCustomOption=true,myOtherOption=myvalue' + --debug Set very verbose log messages + --delay string delay after each request, i.e. 5ms, 1.2s (default "0ms") + --delayBefore string delay before each request, i.e. 5ms, 1.2s (default "0ms") + --dry Dry run. Don't send any data to the server + --dryFormat string Dry run output format. i.e. json, dump, markdown or curl (default "markdown") + --examples Show examples for the current command + --filter stringArray Apply a client side filter to response before returning it to the user + --flatten flatten json output by replacing nested json properties with properties where their names are represented by dot notation + -f, --force Do not prompt for confirmation. Ignored when using --confirm + -H, --header strings custom headers. i.e. --header "Accept: value, AnotherHeader: myvalue" + --includeAll Include all results by iterating through each page + -k, --insecure Allow insecure server connections when using SSL + -l, --logMessage string Add custom message to the activity log + --maxJobs int Maximum number of jobs. 0 = unlimited (use with caution!) + --noAccept Ignore Accept header will remove the Accept header from requests, however PUT and POST requests will only see the effect + --noCache Force disabling of cached responses (overwrites cache setting) + -M, --noColor Don't use colors when displaying log entries on the console + --noLog Disables the activity log for the current command + --noProgress Disable progress bars + --noProxy Ignore the proxy settings + -n, --nullInput Don't read the input (stdin). Useful if using in shell for/while loops + -o, --output string Output format i.e. table, json, csv, csvheader (default "table") + --outputFile string Save JSON output to file (after select/view) + --outputFileRaw string Save raw response to file (before select/view) + --outputTemplate string jsonnet template to apply to the output + -p, --pageSize int Maximum results per page (default 5) + --progress Show progress bar. This will also disable any other verbose output + --proxy string Proxy setting, i.e. http://10.0.0.1:8080 + -r, --raw Show raw response. This mode will force output=json and view=off + --retries int Max number of attempts when a failed http call is encountered (default 3) + --select stringArray Comma separated list of properties to return. wildcards and globstar accepted, i.e. --select 'id,name,type,**.serialNumber' + --session string Session configuration + -P, --sessionPassword string Override session password + -U, --sessionUsername string Override session username. i.e. peter or t1234/peter (with tenant) + --silentExit Silent status codes do not affect the exit code + --silentStatusCodes string Status codes which will not print out an error message + --timeout string Request timeout duration, i.e. 60s, 2m (default "60s") + --totalPages int Total number of pages to get + -v, --verbose Verbose logging + --view string Use views when displaying data on the terminal. Disable using --view off (default "auto") + --withError Errors will be printed on stdout instead of stderr + --withTotalElements Request Cumulocity to include the total elements in the response statistics under .statistics.totalElements (introduced in 10.13) + -t, --withTotalPages Request Cumulocity to include the total pages in the response statistics under .statistics.totalPages + --workers int Number of workers (default 1) +``` + diff --git a/docs/go-c8y-cli/docs/examples/remoteaccess.md b/docs/go-c8y-cli/docs/examples/remoteaccess.md new file mode 100644 index 000000000..cc1316a91 --- /dev/null +++ b/docs/go-c8y-cli/docs/examples/remoteaccess.md @@ -0,0 +1,360 @@ +--- +title: Remote Access +--- + +import CodeExample from '@site/src/components/CodeExample'; + +:::tip +The `remoteacccess` subcommand is only available from go-c8y-cli >= 2.41 +::: + +The [Cumulocity IoT Cloud Remote Access ](https://cumulocity.com/docs/cloud-remote-access/cra-general-aspects/) features allows you to connect to your devices using a number of different protocols like SSH, VNC, Telnet etc. In addition, it also supports a **PASSTHROUGH** mode which allows you to use any TCP based protocol. + +Please refer to the [official Cumulocity IoT documentation](https://cumulocity.com/docs/cloud-remote-access/cra-general-aspects/) for more details. + +**Common use-cases** + +Below are some common use-cases for the Cloud Remote Access Feature: + +* Access a local webpage from the device (such as Node-RED, or tools like monit) +* Connect to the device using SSH (via the cloud - local network access is not required) +* Connect a debugger to local control software to troubleshoot complex control logic + +**What it is not designed for** + +The Cloud Remote Access feature is designed for adhoc connections to provide access to services running locally on a device in a secure manner. For instance, if you need to perform some troubleshooting (e.g. root-cause analysis) on individual devices, in order to develop a fix that can be deployed to the rest of the device fleet using the other Cumulocity IoT device management features like [Firmware](https://cumulocity.com/docs/device-management-application/managing-device-data/#managing-firmware) and [Software Updates](https://cumulocity.com/docs/device-management-application/managing-device-data/#managing-software). + +The feature (in my opinion) is not designed for "always-on" connections and a large number of parallel connections. You should definitely NOT be trying to abuse the feature by performing some kind of Ansible deployment to devices via SSH. If you have this use-case, it is highly recommended looking into [thin-edge.io](https://thin-edge.io) which can fulfill all your device management requirements in a more scalable manner. + +**Background on the PASSTHROUGH feature** + +The **PASSTHROUGH** is relatively new and there doesn't seem to be much documentation out there about it. The feature is insanely useful as it allows you to create secure adhoc connections to your device using existing technologies such as SSH (using the standard ssh client). Cumulocity IoT facilitates the connection between a client on your local machine, and a component on the device, and routes traffic bi-directionally between the two. Since Cumulocity IoT is just passing bytes between the two clients, any TCP based protocol can be used. + +There are a few moving parts in this scenario, as there needs to be a client on both sides of the connection; one on your machine, which is interfacing with the local protocol, and a client on the device which routes the traffic between the cloud and a local service (such as the SSH daemon/service, or a local HTTP server). + +The good news is that there are existing open source projects which can be used to take advantage of the Remote Access feature; these components are: + +* [go-c8y-cli](https://goc8ycli.netlify.app/docs/introduction/) (on your machine) +* [thin-edge.io](https://thin-edge.io) - a Rust based agent that has out-of-the-box support for the Cumulocity IoT Cloud Remote Access feature (on the device) + +### Prerequisites + +Before you can use this feature you need to have the "Cloud Remote Access" feature enabled in your tenant. Please consult the [official docs](https://cumulocity.com/docs/cloud-remote-access/using-cloud-remote-access/) on how to do this. + +#### Granting permission to use Cloud Remote Access + +By default, the Cumulocity IoT `ROLE_REMOTE_ACCESS_ADMIN` permission is not assigned to any user group or user. This means that you'll need to add it before you will even be able to add any configurations and even see it in the Cumulocity IoT Device Management application. + +One option is to add the permission to the existing "admins" user group, and then assign the "admins" group to your user. You can do this via the Cumulocity IoT Administration app, or using the following command: + + + +```sh +c8y userroles addRoleToGroup --group admins --role ROLE_REMOTE_ACCESS_ADMIN +``` + + + +Alternatively, you can create a new custom user group, and only assign the permission to it. This user group can be used to grant fine grain access to this feature ensure that the user does not inherit any additional permissions that are provided by the "admins" user group. Below shows the cli commands used to create a new user group called "devmgmt-powerusers": + + + +```sh +c8y usergroups create --name "devmgmt-powerusers" +c8y userroles addRoleToGroup --group "devmgmt-powerusers" --role ROLE_REMOTE_ACCESS_ADMIN +``` + + + +You can add users to the user group to grant them access to this feature using the following command: + + + + +```sh +c8y userreferences addUserToGroup --group devmgmt-powerusers --user "myuser@example.com" +``` + + + + +### Using ssh config to launch proxy command automatically + +The remote access feature works very well with the ssh `ProxyCommand` which provides the most "native" ssh experience as you don't have to manually call the `c8y` command when you want to start your ssh session. + +For example, with specific configuration, you can then connect to your device (via Cumulocity IoT) using a plain ssh command. + +```sh +ssh my_device +``` + +To achieve this, you need to create an entry in your ssh client configuration file (e.g. `~/.ssh/config`) which specifies the name of the device you want to connect to and add the `ProxyCommand` line which calls the `c8y remoteaccess server` command. + +Below shows the format of the ssh client configuration entry that you need to add: + +```text title="file: ~/.ssh/config" +Host + User + PreferredAuthentications publickey + IdentityFile + ServerAliveInterval 120 + StrictHostKeyChecking no + UserKnownHostsFile /dev/null + ProxyCommand c8y remoteaccess server --device %n --listen - +``` + +Using the above format, if you want to connect to a device called `my_device`, then your ssh client config entry would like this (remember to update your `IdentityFile` to match your private ssh key): + +```text title="file: ~/.ssh/config" +Host my_device + User admin + PreferredAuthentications publickey + IdentityFile ~/.ssh/id_rsa + ServerAliveInterval 120 + StrictHostKeyChecking no + UserKnownHostsFile /dev/null + ProxyCommand c8y remoteaccess server --device %n --listen - +``` + +Afterwards, when you run the `ssh my_device` command, the ssh client will automatically run the `c8y remoteaccess server` command. The `--listen -` option tells the local proxy server to use Standard Input/Output instead of using a unix socket, or TCP server. + +## Examples + +### Connect using ssh + +This example walks you through connecting to your device using ssh. The ssh connection will go through a local proxy (which will be started automatically by the c8y command), this will trigger an operation which is sent to the device, where thin-edge.io will pass the connection through to the local SSH daemon (service). + +Before we can connect to the device with a native ssh session, you will need to ensure that a **PASSTHROUGH** configuration has been added to the device. This configuration tells the Remote Access service which port the traffic should be routed to on the target device. + + +#### Step 1: Create the passthrough configuration in Cumulocity IoT + +You can create the required remote access configuration using the following command: + + + +```sh +c8y remoteaccess configurations create-passthrough --device my_device --name native-ssh --port 22 +``` + + + +:::tip +If you want to check if a configuration already exists, then you can also list the existing configuration using. + + + +```sh +c8y settings update remoteaccess.sshuser admin +``` + + + +Afterwards, you don't need to provide the `--user ` flag when connecting to the device. +::: + + +#### Step 2: Add your public key to the device + +Connecting to any machine via ssh needs some authentication to be already setup. Some common SSH authentications mechanisms are as follows: + +* Username/Password (not recommended) +* Public Key (via `~/.ssh/authorized_keys`) +* Certificate based authentication + +:::info +**c8y remoteaccess** is not responsible for any part of the ssh authentication, this is handled entirely by the ssh client. You might find it helpful to read up on SSH authentication methods and how to configure an ssh-agent on your machine so that you don't have to constantly enter the password to your private ssh key (if you're using Public key authentication etc.). +::: + +**Example: Adding your public key to the authorized_keys file** + +If you're using Public Key authentication, you can add your public ssh key to the device using the following commands (assuming you have access to the device locally, or it is part of your base image): + +```sh +mkdir -p ~/.ssh +chmod 700 ~/.ssh +echo "ssh-ed25519 AAAAC3NzbC1lZDi1NTE5AAAdIDxm81Bbp+bQfMM0jgoMmUD7nXpBCclEq+NqVxUf5D65 myuser@example.com" >> ~/.ssh/authorized_keys +chmod 600 ~/.ssh/authorized_keys +``` + +:::note +If the above information means nothing to you, then I would recommend reading up on SSH authentication or contacting whomever is responsible for the administration of the device your are trying to connect to. +::: + +#### Step 3: Connect to the device via ssh + +On your local machine, you can then connect to the device via ssh using the following command, where `--user` is the SSH user on the target device. + + + +```sh +c8y remoteaccess connect ssh --device my_device --user admin +``` + + + +If your fleet of devices always has the same SSH user, then you can configure the default ssh user + + + +```sh +c8y settings update remoteaccess connect ssh --device my_device --user admin +``` + + + +### Running a custom command + +```sh +#!/usr/bin/env bash +echo "TARGET: $TARGET" +echo "PORT: $PORT" +``` + +```sh +c8y remoteaccess run --device my_device -- +``` + +### Create local proxy server + +In some cases you don't want to launch the services immediately, but install you first want to start the local server, and then allow clients to connect to it. + +A local proxy server can be configured to listen on one of the following mediums: + +* Unix socket +* TCP server +* Standard Input/Output (stdio) + +**Local host with a randomized port** + +Use port `0` if you want a randomized/free port. The used port will be printed to the console. + + + +```sh +c8y remoteaccess server --listen "127.0.0.1:0" +``` + + + +Alternatively, you can explicitly set the port using: + + + +```sh +c8y remoteaccess server --listen "127.0.0.1:9000" +``` + + + + +**Unix Socket** + +Unix sockets can also be used by using the `unix://` prefix in the `listen` flag. + + + +```sh +c8y remoteaccess server --listen "unix:///tmp/example.sock" +``` + + + +Clients can then communicate with the local proxy server instance via given unix socket, `unix:///tmp/example.sock`. + +## Migrating from c8ylp + +**go-c8y-cli** can now be used to fully replace the previous Python based tool, [c8ylp](https://github.com/SoftwareAG/cumulocity-remote-access-local-proxy). + +The **c8y remoteaccess** commands can be used to provide the same functionality, though maybe slightly less configurable (e.g. you can't currently change the buffer size). If you feel that there are some additional settings that should be exposed, then please create an issue on the [Github project](https://github.com/reubenmiller/go-c8y-cli/issues/new). + +The following are the main differences between **c8ylp** and the **c8y remoteaccess** commands: + +* The `--env-file` flag is not supported as **go-c8y-cli** uses session files/env variables to managed the Cumulocity IoT tenant/credentials + +* **c8ylp** only supported referring to a device by its external identity (with the `c8y_Serial` type). The **go-c8y-cli** version uses [reference by name](/docs/concepts/reference-by-name/) which allows supports both a named lookup and providing the explicit external id. The new way is more consistent with existing behaviour where you can also benefit from tab completion, something that is not possible when using external identities. In the future, **go-c8y-cli** will support additional lookup methods, however for now if you need to do a lookup via the external identity, then you can use an alias which can include an identity lookup. See the [migrating from c8ylp](#migrating-from-c8ylp) section for more details. + +To help with the migration, the following sections show how to transition from [c8ylp](https://github.com/SoftwareAG/cumulocity-remote-access-local-proxy). + + +#### Starting local (tcp) proxy server on a random port + +**Before** + +```sh +c8ylp server "" --env-file .env +``` + +**After** + +```sh +c8y remoteaccess server --device "" +``` + +### Start interactive ssh session + +**Before** + +```sh +c8ylp connect ssh "" --ssh-user "" --env-file .env +``` + +**After** + +```sh +c8y remoteaccess server --device "" --user "" +``` + + +### Starting a local proxy server using a unix socket + +**Before** + +```sh +c8ylp server --env-file .env --socket-path /tmp/device.socket +ssh -o 'ProxyCommand=socat - UNIX-CLIENT:/tmp/device.socket' @localhost +``` + +**After** + +```sh +c8y remoteaccess server --device --listen unix:///tmp/device.socket +ssh -o 'ProxyCommand=socat - UNIX-CLIENT:/tmp/device.socket' @localhost +``` + +### Starting a local proxy using stdio + +If you've previously used the ssh client config with the `ProxyCommand` property, you were probably using the `--stdio` mode. This mode is also supported in `c8y remoteaccess`, and you can activate the mode by setting the `--listen` flag to `-`, where `-` means StandardInput. + +**Before** + +```sh +ProxyCommand c8ylp server %n --stdio --env-file .env +``` + +**After** + +```sh +ProxyCommand c8y remoteaccess server --device %n --listen - +``` + +### Connect to a device using the external identity + +MacOS/Linux/WSL users can use an alias which uses the `c8y identity get` to do the external identity lookup and passes the result to the `--device` flag. To make the one-liner more accessible, it can be assigned to an alias using the following command: + +```sh +c8y alias set ssh 'c8y remoteaccess connect ssh --device "$(c8y identity get --name "$1" --select managedObject.id -o csv)" --user $2' --shell +``` + +The alias expects 2 arguments, the external identity, then the ssh user name. An example showing the alias usage is shown below: + +```sh +c8y ssh rpi4-abc632486800 root +``` + +```sh title="Output" +Starting interactive ssh session with 4550259 (https://example.cumulocity.com) + +Warning: Permanently added '[127.0.0.1]:52480' (RSA) to the list of known hosts. +root@rpi4-abc632486800:~# +```