From e6e0381f97057ea1a8169e5e246600710f31c990 Mon Sep 17 00:00:00 2001 From: gberl002 Date: Mon, 7 Mar 2022 13:38:56 -0500 Subject: [PATCH 01/11] Fixing the changelog, incorrectly reporting a bug fix on a previous version. --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9bcdf2abf..ca96429ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ +# Release 0.25.0 +* Bug fix: Fixed an issue with the ziti CLI quickstart routine which also affected router and controller config generation leaving many config fields blank or incorrect. + * Note: This fix was previously reported to have been fixed in 0.24.13 but the fix was actually applied to this release. + # Release 0.24.13 -* Bug fix: Fixed an issue with the ziti CLI quickstart routine which also affected router and controller config generation leaving many config fields blank or incorrect. * Enhancement: Added new `noTraversal` field to routers. Configures if a router should allow/disallow traversal. Required on create/update commands. * Enhancement: `ziti edge update edge-router` now supports either `--no-traversal` flag which will allow/disallow a given router from being used to traverse. * Enhancement: `ziti fabric list routers` and `ziti edge list routers` will now display the noTraversal flag of associated routers. From 3d6a85bd0e84a2f7408959295db4361325a33d07 Mon Sep 17 00:00:00 2001 From: Paul Lorenz Date: Thu, 24 Feb 2022 15:41:00 -0500 Subject: [PATCH 02/11] Update changelog and deps for link management refactor --- CHANGELOG.md | 77 ++++++++++++++++++++++++++++- go.mod | 11 +++-- go.sum | 27 +++++----- version | 2 +- ziti/cmd/ziti/cmd/fabric/inspect.go | 6 ++- ziti/cmd/ziti/cmd/fabric/list.go | 19 +++++-- ziti/cmd/ziti/cmd/ps.go | 11 +---- 7 files changed, 119 insertions(+), 34 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ca96429ef..a72901345 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,81 @@ # Release 0.25.0 + +## Breaking Changes +Routers with version 0.25.0 or greater must be used with a controller that is also v0.25 or greater. +Controllers will continue to work with older routers. Router of this version should also continue to interoperate with older routers. + +NOTE: You may be used to seeing two links between routers, if they both have link listeners. Starting with v0.25 expect to see only +a single link between routers, unless you use the new link types feature. + +## What's New + * Bug fix: Fixed an issue with the ziti CLI quickstart routine which also affected router and controller config generation leaving many config fields blank or incorrect. - * Note: This fix was previously reported to have been fixed in 0.24.13 but the fix was actually applied to this release. + * Note: This fix was previously reported to have been fixed in 0.24.13 but the fix was actually applied to this release. +* Enhancement: Router Link Refactor + * Support for multiple link types + * Existing link notifications + * Link heartbeats/latency have changed + * Inspect and ps upport for links + * Router version dissemination + * Distributed control preparation +* Enhancement: `ziti fabric list routers` now includes the link listener types and advertise addresses + +## Router Link Refactor + +### Multiple Link Types +Routers can now configure multiple link listeners. Listeners now support an option 'type' attribute. If no type is provided, the link type will be derived from the address. For example, given the following configuration: + +``` +link: + dialers: + - binding: transport + listeners: + - binding: transport + bind: tls:127.0.0.1:7878 + advertise: tls:127.0.0.1:7878 + + - binding: transport + bind: tls:127.0.0.1:5876 + advertise: tls:127.0.0.1:5876 + type: cellular +``` + +The first listener will have a type of `tls` and the second listener will have a type of `cellular`. + +Routers will now try to maintain one link of each type available on the target router. + +When using `ziti fabric list links` the link type will now be shown. + +### Existing link notifications +As the controller doesn't persist links, when the controller restarts or loses connection it loses all information about router links. Routers can now notify the controller about existing links when they reconnect. If they receive a link dial request for a link that they already have (based on the target router and link type), they can now report back the existing link. This should prevent the number of links to remain relatively constant. + +### Link Heartbeats + +Because we are now limiting the number of links it is even more vital to ensure that links are healthy, and to respond quickly when links become unresponsive. To that end links now use heartbeats. As data flows across the link, heartbeat headers will be added periodically. Heartbeat responses will be added to return messages. If the link is currently quiet, explicit heartbeat messages will be sent. Heartbeats will also be used to measure latency. If heartbeats are unreturned for a certain amount of time, the link will be considered bad and torn down, so a new one can be established. + +The link.latency metric now is calculated starting when the message is about to be sent. It may have a few extra milliseconds time, as the response waits briefly to see if there's an existing message that the response can piggyback on. + +Previously link.latency include both queue and network time. Now that it only has network time, there's a new metrics, `link.queue_time` which tracks how long it takes messages to get from send requested to just before send. + +### Inspect and ps support for links + +`ziti fabric inspect .* links` can now be used to see what links each router knows about. This can be useful to determine if/how the controller and routers may have gotten out of sync. + +Router can also be interrogated directly for their links via IPC, using `ziti ps`. + +``` +$ ziti ps router dump-links 275061 +id: 4sYO18tZ1Fz4HByXuIp1Dq dest: o.oVU2Qm. type: tls +id: 19V7yhjBpHAc2prTDiTihQ dest: hBjIP2wmxj type: tls +``` + +### Router version dissemination + +Routers now get the version of the router they are dialing a link to, and pass their own version to that router as part of the dial. This allows routers to only enable specific features if both sides of the link support it. + +### Distributed Control preparation + +Giving the routers have more control over the links prepares us for a time when routers may be connected to multiple controllers. Routers will be able to notify controllers of existing links and will be prepared to resolve duplicate link dial requests from multiple sources. # Release 0.24.13 diff --git a/go.mod b/go.mod index de9dbdd79..16517b77b 100644 --- a/go.mod +++ b/go.mod @@ -35,9 +35,9 @@ require ( github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d github.com/keybase/go-ps v0.0.0-20190827175125-91aafc93ba19 github.com/michaelquigley/pfxlog v0.6.3 - github.com/openziti/channel v0.18.11 - github.com/openziti/foundation v0.17.7 - github.com/openziti/sdk-golang v0.16.14 + github.com/openziti/channel v0.18.14 + github.com/openziti/foundation v0.17.8 + github.com/openziti/sdk-golang v0.16.20 github.com/pborman/uuid v1.2.0 github.com/pkg/errors v0.9.1 github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 @@ -73,6 +73,7 @@ require ( github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dgryski/dgoogauth v0.0.0-20190221195224-5a805980a5f3 // indirect + github.com/dineshappavoo/basex v0.0.0-20170425072625-481a6f6dc663 // indirect github.com/disintegration/imaging v1.6.2 // indirect github.com/dlclark/regexp2 v1.4.0 // indirect github.com/docker/go-units v0.4.0 // indirect @@ -172,7 +173,7 @@ require ( require ( github.com/jedib0t/go-pretty/v6 v6.2.4 - github.com/openziti/edge v0.21.115 - github.com/openziti/fabric v0.17.57 + github.com/openziti/edge v0.21.120 + github.com/openziti/fabric v0.17.60 github.com/rivo/uniseg v0.2.0 // indirect ) diff --git a/go.sum b/go.sum index e2c6bd612..83fc98942 100644 --- a/go.sum +++ b/go.sum @@ -214,6 +214,8 @@ github.com/dgryski/dgoogauth v0.0.0-20190221195224-5a805980a5f3 h1:AqeKSZIG/NIC7 github.com/dgryski/dgoogauth v0.0.0-20190221195224-5a805980a5f3/go.mod h1:hEfFauPHz7+NnjR/yHJGhrKo1Za+zStgwUETx3yzqgY= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= +github.com/dineshappavoo/basex v0.0.0-20170425072625-481a6f6dc663 h1:fctNkSsavbXpt8geFWZb8n+noCqS8MrOXRJ/YfdZ2dQ= +github.com/dineshappavoo/basex v0.0.0-20170425072625-481a6f6dc663/go.mod h1:Kad2hux31v/IyD4Rf4wAwIyK48995rs3qAl9IUAhc2k= github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c= github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4= github.com/dlclark/regexp2 v1.1.6/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= @@ -818,22 +820,21 @@ github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+ github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= -github.com/openziti/channel v0.18.9/go.mod h1:pjoD6e9QgDGnaiakRA+s8YT/FzUHWDhJJF573oOJkXg= -github.com/openziti/channel v0.18.11 h1:zT2zRI6m/POr81dXmM8azLpcCjysgeuVPppztl25Zc0= -github.com/openziti/channel v0.18.11/go.mod h1:9z2HLdoYZ3RYm9S8XqlQB5spf3j+BLPof0A1zvjLyzc= +github.com/openziti/channel v0.18.13/go.mod h1:9z2HLdoYZ3RYm9S8XqlQB5spf3j+BLPof0A1zvjLyzc= +github.com/openziti/channel v0.18.14 h1:MS6Wfo0BklKI/IQ/HQl+wKJBw9rPimTSSqFIeBcsHGQ= +github.com/openziti/channel v0.18.14/go.mod h1:F370DYbKEKpIEhRaZVbEK6xN+pirSZx4qnwbObQAV2I= github.com/openziti/dilithium v0.3.3 h1:PLgQ6PMNLSTzCFbX/h98cmudgz/cU6TmjdSv5NAPD8k= github.com/openziti/dilithium v0.3.3/go.mod h1:vsCjI2AU/hon9e+dLhUFbCNGesJDj2ASgkySOcpmvjo= -github.com/openziti/edge v0.21.115 h1:sIxlCCUEfXgOpUikmd6Ya3Hsn1fhMXaFznkNeUF+OPQ= -github.com/openziti/edge v0.21.115/go.mod h1:2gb49/wivJSAnQwRCC8rAkQ0+3lWjwUgo/Kb7+0WJpM= -github.com/openziti/fabric v0.17.54/go.mod h1:o206EkKYBOoQvDKsFj07rlHI+za6yMpxUXyWj2XVrw0= -github.com/openziti/fabric v0.17.57 h1:mCo89KoEH1oaHeo0lc3p0x3A8G6/LsSiPTpd7NKvND0= -github.com/openziti/fabric v0.17.57/go.mod h1:nPHmXHx1MxgS2LlsjIWrWqq4B92OyPGtsLCdV3GBlXw= -github.com/openziti/foundation v0.17.1/go.mod h1:qzctNeCrOrPDQDr9g70IPHw+awal87qPqRe3UJLwNXw= +github.com/openziti/edge v0.21.120 h1:hoSmHPec/PEgQE4JZ3L05IBnyR8UqRpVOJUK5egQlNc= +github.com/openziti/edge v0.21.120/go.mod h1:Sdf0gOmnF3tgts9cV4nQxMzyhY/R4KpXZxKqTMje+7M= +github.com/openziti/fabric v0.17.60 h1:xUc2e2ITkpgyeWvHHb8JGv7r5AtwSzFv2PenaIiXo2E= +github.com/openziti/fabric v0.17.60/go.mod h1:8oBcmWriG/nTGF/+8pkv1oAKQY97XXG4ptns8dUTMr4= github.com/openziti/foundation v0.17.6/go.mod h1:dTaJgdO5OqnKpV+0OPHpnTnqfPpqNGbhJv8O6pMot6o= -github.com/openziti/foundation v0.17.7 h1:lnP+4e78Vmfu/iL1seKqurCiroYWi0Ae0SQEcM7b36A= -github.com/openziti/foundation v0.17.7/go.mod h1:dTaJgdO5OqnKpV+0OPHpnTnqfPpqNGbhJv8O6pMot6o= -github.com/openziti/sdk-golang v0.16.14 h1:PIjHRwQqKcclfJYVjEUnnri2PM9wCAEBLwFhwQ6FSJ4= -github.com/openziti/sdk-golang v0.16.14/go.mod h1:s8T8XNGGCeEeMhtCf5iUi2t97FqoslE6t1TTEbCVfPU= +github.com/openziti/foundation v0.17.8 h1:vlX6yQHrs5Tcp39xHylJhzohTDOZ6d/VrVlQahw7GBo= +github.com/openziti/foundation v0.17.8/go.mod h1:dTaJgdO5OqnKpV+0OPHpnTnqfPpqNGbhJv8O6pMot6o= +github.com/openziti/sdk-golang v0.16.19/go.mod h1:ougRN3x0v2L7sHwjkoeoa05ZlWs9+0ObyUCc2Sm8Kfw= +github.com/openziti/sdk-golang v0.16.20 h1:AtSaD0Do5HgkDWFR0dLUVpiOpky2WAh6cf78hcG30jk= +github.com/openziti/sdk-golang v0.16.20/go.mod h1:uQ90QCehBrfO/xmpnWGVr3Jj65BMUrvGPH4I0gRl0og= github.com/oracle/oci-go-sdk v24.2.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888= github.com/orcaman/concurrent-map v0.0.0-20190826125027-8c72a8bb44f6/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI= github.com/orcaman/concurrent-map v0.0.0-20210106121528-16402b402231 h1:fa50YL1pzKW+1SsBnJDOHppJN9stOEwS+CRWyUtyYGU= diff --git a/version b/version index fd137eb17..7d385d419 100644 --- a/version +++ b/version @@ -1 +1 @@ -0.24 +0.25 diff --git a/ziti/cmd/ziti/cmd/fabric/inspect.go b/ziti/cmd/ziti/cmd/fabric/inspect.go index 677ebd840..c17406ddc 100644 --- a/ziti/cmd/ziti/cmd/fabric/inspect.go +++ b/ziti/cmd/ziti/cmd/fabric/inspect.go @@ -51,12 +51,16 @@ func (self *ListCmd) run(cmd *cobra.Command, args []string) error { return err } + if self.OutputResponseJson() { + return nil + } + result := inspectOk.Payload if *result.Success { fmt.Printf("\nResults: (%d)\n", len(result.Values)) for _, value := range result.Values { fmt.Printf("%v.%v\n", stringz.OrEmpty(value.AppID), stringz.OrEmpty(value.Name)) - fmt.Printf("%v\n\n", stringz.OrEmpty(value.Value)) + fmt.Printf("%+v\n\n", value.Value) } } else { fmt.Printf("\nEncountered errors: (%d)\n", len(result.Errors)) diff --git a/ziti/cmd/ziti/cmd/fabric/list.go b/ziti/cmd/ziti/cmd/fabric/list.go index 3a941bded..87eb7283c 100644 --- a/ziti/cmd/ziti/cmd/fabric/list.go +++ b/ziti/cmd/ziti/cmd/fabric/list.go @@ -184,10 +184,11 @@ func outputLinks(o *api.Options, children []*gabs.Container, pagingInfo *api.Pag columnConfigs[4] = table.ColumnConfig{Align: text.AlignRight} columnConfigs[5] = table.ColumnConfig{Align: text.AlignRight} t.SetColumnConfigs(columnConfigs) - t.AppendHeader(table.Row{"ID", "Dialer", "Acceptor", "Static Cost", "Src Latency", "Dst Latency", "State", "Status", "Full Cost"}) + t.AppendHeader(table.Row{"ID", "Type", "Dialer", "Acceptor", "Static Cost", "Src Latency", "Dst Latency", "State", "Status", "Full Cost"}) for _, entity := range children { id := entity.Path("id").Data().(string) + linkType := entity.Path("type").Data().(string) srcRouter := entity.Path("sourceRouter.name").Data().(string) dstRouter := entity.Path("destRouter.name").Data().(string) staticCost := entity.Path("staticCost").Data().(float64) @@ -202,7 +203,7 @@ func outputLinks(o *api.Options, children []*gabs.Container, pagingInfo *api.Pag status = "down" } - t.AppendRow(table.Row{id, srcRouter, dstRouter, staticCost, + t.AppendRow(table.Row{id, linkType, srcRouter, dstRouter, staticCost, fmt.Sprintf("%.2vms", srcLatency), fmt.Sprintf("%.2vms", dstLatency), state, status, cost}) @@ -291,7 +292,7 @@ func outputRouters(o *api.Options, children []*gabs.Container, pagingInfo *api.P t := table.NewWriter() t.SetStyle(table.StyleRounded) - t.AppendHeader(table.Row{"ID", "Name", "Online", "Cost", "No Traversal", "Version"}) + t.AppendHeader(table.Row{"ID", "Name", "Online", "Cost", "No Traversal", "Version", "Listeners"}) for _, entity := range children { id := entity.Path("id").Data().(string) @@ -307,7 +308,17 @@ func outputRouters(o *api.Options, children []*gabs.Container, pagingInfo *api.P arch := versionInfo.Path("arch").Data().(string) version = fmt.Sprintf("%v on %v/%v", v, os, arch) } - t.AppendRow(table.Row{id, name, connected, cost, noTraversal, version}) + var listeners []string + listenerAddresses := entity.Path("listenerAddresses") + if listenerAddresses != nil { + children, _ := listenerAddresses.Children() + for idx, child := range children { + addr := child.Path("address").Data().(string) + linkType := child.Path("type").Data().(string) + listeners = append(listeners, fmt.Sprintf("%v: %v (%v)", idx+1, addr, linkType)) + } + } + t.AppendRow(table.Row{id, name, connected, cost, noTraversal, version, strings.Join(listeners, "\n")}) } renderTable(o, t, pagingInfo) diff --git a/ziti/cmd/ziti/cmd/ps.go b/ziti/cmd/ziti/cmd/ps.go index f8bbfd78c..3aec9b609 100644 --- a/ziti/cmd/ziti/cmd/ps.go +++ b/ziti/cmd/ziti/cmd/ps.go @@ -78,10 +78,7 @@ func NewCmdPs(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.Command Short: "Show Ziti router process info", Long: psLong, Run: func(cmd *cobra.Command, args []string) { - options.Cmd = cmd - options.Args = args - err := options.Run() - cmdhelper.CheckErr(err) + cmdhelper.CheckErr(cmd.Help()) }, } @@ -105,11 +102,7 @@ func NewCmdPs(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.Command routerCmd.AddCommand(NewCmdPsBasicRouterCmd("disconnect", router.CloseControlChannel, f, out, errOut)) routerCmd.AddCommand(NewCmdPsBasicRouterCmd("reconnect", router.OpenControlChannel, f, out, errOut)) routerCmd.AddCommand(NewCmdPsBasicRouterCmd("dump-api-sessions", debugops.DumpApiSessions, f, out, errOut)) - - // cmd.AddCommand(NewCmdPsController(f, out, errOut)) - // cmd.AddCommand(NewCmdPsMgmt(f, out, errOut)) - // cmd.AddCommand(NewCmdPsMgmtGw(f, out, errOut)) - // cmd.AddCommand(NewCmdPsRouter(f, out, errOut)) + routerCmd.AddCommand(NewCmdPsBasicRouterCmd("dump-links", router.DumpLinks, f, out, errOut)) options.addPsFlags(cmd) return cmd From a0634de4340d2cd1fae2e4b3b9edfbca4bd808d6 Mon Sep 17 00:00:00 2001 From: dovholuknf <46322585+dovholuknf@users.noreply.github.com> Date: Mon, 7 Mar 2022 18:16:46 -0500 Subject: [PATCH 03/11] remove old screen caps and aws.md and point/redirect to the new stuff --- quickstart/aws.md | 174 +-------------------------------- quickstart/img_1.png | Bin 18799 -> 0 bytes quickstart/img_2.png | Bin 18412 -> 0 bytes quickstart/img_3.png | Bin 79565 -> 0 bytes quickstart/security-groups.png | Bin 56122 -> 0 bytes 5 files changed, 5 insertions(+), 169 deletions(-) delete mode 100644 quickstart/img_1.png delete mode 100644 quickstart/img_2.png delete mode 100644 quickstart/img_3.png delete mode 100644 quickstart/security-groups.png diff --git a/quickstart/aws.md b/quickstart/aws.md index ed0624e8b..5a1b02510 100644 --- a/quickstart/aws.md +++ b/quickstart/aws.md @@ -1,171 +1,7 @@ -# Quickstart - AWS + + + -This quickstart is a barebones guide to running a full developer environment in AWS. It is not fault-tolerant. If you require fault tolerance this guide is not for you. This guide also assumes you're at least familiar with AWS's console and cloud development. Feel free to replace any steps along the way you desire but these steps were tested at least a few times and hopefully whenever you're reading this, the steps are still valid. If not file an issue or shoot over to [Discourse](https://openziti.discourse.group/) and start a discussion. +# Content moved -## Setup a Server - -1. After logging into AWS, navigate to the EC2 Dashboard -1. From the EC2 Dashboard, click "Launch Instance" -1. Find "Ubuntu Server 20.04 LTS (HVM), SSD Volume Type" and click 'select' -1. At the bottom of the screen - click the "Review and Launch" button -1. In AWS "Step 7. Review Instance Launch", find the "Security Groups" row and expand it if necessary -1. Click "Edit security groups" -1. Click "Add Rule" and select "Custom TCP Rule" and enter Port Range:"8441" Source: "0.0.0.0/0", Description: "Ziti Controller Port" -1. Click "Add Rule" and select "Custom TCP Rule" and enter Port Range:"8442" Source: "0.0.0.0/0", Description: "Ziti Edge Router Port" -1. Click "Add Rule" and select "Custom TCP Rule" and enter Port Range:"8443" Source: "0.0.0.0/0", Description: "Ziti Console TLS Port" -1. Your console should look like this: - ![security-groups.png](security-groups.png) - -1. click "Review and Launch" and then "Launch" -1. Select your keypair and choose "Launch Instances" - -## SSH to the Server - -1. After ssh'ing to the new ubuntu machine - run: `sudo apt update && sudo apt install jq -y` -1. Set some environment variables which are used to establish a PKI as well as the necessary configuration files. You will need to know the public IPv4 DNS name assigned to this machine. Replace "____EXTERNAL___DNS____" with the proper values. (**IMPORTANT** make sure the name is correct): - - export EXTERNAL_DNS="____EXTERNAL___DNS______FILLIN example: ec2-3-129-206-186.us-east-2.compute.amazonaws.com" - - export EXTERNAL_IP="$(curl -s eth0.me)" - export ZITI_EDGE_CONTROLLER_IP_OVERRIDE="${EXTERNAL_IP}" - export ZITI_EDGE_ROUTER_IP_OVERRIDE="${EXTERNAL_IP}" - export ZITI_EDGE_CONTROLLER_HOSTNAME="${EXTERNAL_DNS}" - export ZITI_EDGE_ROUTER_HOSTNAME="${EXTERNAL_DNS}" - export ZITI_EDGE_CONTROLLER_PORT=8441 - export ZITI_EDGE_ROUTER_PORT=8442 - -1. Run this single command to download and bootstrap the ziti-controller and ziti-router (feel free to read the bash script before running if you like): - - source <(wget -qO- https://raw.githubusercontent.com/openziti/ziti/release-next/quickstart/docker/image/ziti-cli-functions.sh); expressInstall - -1. At this point the controller and router are running and can be accessed from anywhere. Confirm both are running by running: - - ps -ef | grep ziti - root 2520 1 0 May19 ? 00:00:15 /home/ubuntu/.ziti/quickstart/ip-172-31-22-212/ziti-bin/ziti-v0.20.2/ziti-controller run /home/ubuntu/.ziti/quickstart/ip-172-31-22-212/controller.yaml - root 2547 1 0 May19 ? 00:00:10 /home/ubuntu/.ziti/quickstart/ip-172-31-22-212/ziti-bin/ziti-v0.20.2/ziti-router run /home/ubuntu/.ziti/quickstart/ip-172-31-22-212/ip-172-31-22-212-edge-router.yaml - -1. You can also issue this statement from the server and you will see a similar response to this: - - curl -sk "https://${ZITI_EDGE_CONTROLLER_HOSTNAME}:${ZITI_EDGE_CONTROLLER_PORT}/version" - {"data":{"apiVersions":{"edge":{"v1":{"path":"/edge/v1"}}},"buildDate":"2021-05-05 20:59:40","revision":"73dd1db42bf4","runtimeVersion":"go1.16.3","version":"v0.19.13"},"meta":{}} - -## Setting up Ziti Controller and Router as a service - -1. Stop ziti-controller and ziti-router if they are running: - - killall ziti-controller ziti-router -1. copy the two systemd files and refresh systemd - - sudo cp "${ZITI_HOME}/ziti-controller.service" /etc/systemd/system - sudo cp "${ZITI_HOME}/ziti-router-${ZITI_EDGE_ROUTER_RAWNAME}.service" /etc/systemd/system/ziti-router.service - sudo systemctl daemon-reload - sudo systemctl start ziti-controller - sudo systemctl start ziti-router -1. Allow 5 to 10 seconds for the ziti-controller and router to start completely. Run these two commands to verify ziti-controller and ziti-router are running: - - sudo systemctl -q status ziti-controller --lines=0 --no-pager - sudo systemctl -q status ziti-router --lines=0 --no-pager - - #sample output: - ubuntu@ip-172-31-22-212:~$ sudo systemctl -q status ziti-controller --lines=0 --no-pager - ● ziti-controller.service - Ziti-Controller - Loaded: loaded (/etc/systemd/system/ziti-controller.service; disabled; vendor preset: enabled) - Active: active (running) since Thu 2021-05-20 11:24:40 UTC; 5min ago - Main PID: 26848 (ziti-controller) - Tasks: 7 (limit: 1160) - Memory: 50.5M - CGroup: /system.slice/ziti-controller.service - └─26848 /home/ubuntu/.ziti/quickstart/ip-172-31-22-212/ziti-bin/ziti-v0.20.2/ziti-controller run /home/ubu… - - ubuntu@ip-172-31-22-212:~$ sudo systemctl -q status ziti-router --lines=0 --no-pager - ● ziti-router.service - Ziti-Router for ip-172-31-22-212-edge-router - Loaded: loaded (/etc/systemd/system/ziti-router.service; disabled; vendor preset: enabled) - Active: active (running) since Thu 2021-05-20 11:24:42 UTC; 5min ago - Main PID: 26864 (ziti-router) - Tasks: 7 (limit: 1160) - Memory: 28.4M - CGroup: /system.slice/ziti-router.service - └─26864 /home/ubuntu/.ziti/quickstart/ip-172-31-22-212/ziti-bin/ziti-v0.20.2/ziti-router run /home/ubuntu/… - ubuntu@ip-172-31-22-212:~$ - -## Setting up the Ziti Admin Console - -### By Cloning from Github - -1. clone the ziti-console repo from github: - - git clone https://github.com/openziti/ziti-console.git "${ZITI_HOME}/ziti-console" -1. Install node and npm and get the server ready - - cd "${ZITI_HOME}/ziti-console" - sudo apt install npm nodejs -y - npm install -1. Use the ziti-controller certificates for the Ziti Console: - - ln -s "${ZITI_PKI}/${ZITI_EDGE_CONTROLLER_HOSTNAME}-intermediate/certs/${ZITI_EDGE_CONTROLLER_HOSTNAME}-server.chain.pem" "${ZITI_HOME}/ziti-console/server.chain.pem" - ln -s "${ZITI_PKI}/${ZITI_EDGE_CONTROLLER_HOSTNAME}-intermediate/keys/${ZITI_EDGE_CONTROLLER_HOSTNAME}-server.key" "${ZITI_HOME}/ziti-console/server.key" -1. Emit the Ziti Console systemd file and update systemd to start the Ziti Console - - createZacSystemdFile - sudo cp "${ZITI_HOME}/ziti-console.service" /etc/systemd/system - sudo systemctl daemon-reload - sudo systemctl start ziti-console -1. Verify the Ziti Console is running - sudo systemctl status ziti-console --lines=0 --no-pager - - #sample output of the command: - ubuntu@ip-172-31-22-212:~$ sudo systemctl status ziti-console --lines=0 --no-pager - ● ziti-console.service - Ziti-Console - Loaded: loaded (/etc/systemd/system/ziti-console.service; disabled; vendor preset: enabled) - Active: active (running) since Wed 2021-05-19 22:04:44 UTC; 13h ago - Main PID: 13458 (node) - Tasks: 11 (limit: 1160) - Memory: 33.4M - CGroup: /system.slice/ziti-console.service - └─13458 /usr/bin/node /home/ubuntu/.ziti/quickstart/ip-172-31-22-212/ziti-console/server.js - -### By Using Docker - -1. If needed, install docker on the machine. I followed these instructions https://linuxize.com/post/how-to-install-and-use-docker-on-ubuntu-20-04/ but we'll condense them here for you to copy/paste: - - sudo apt update - sudo apt install apt-transport-https ca-certificates curl gnupg-agent software-properties-common -y - curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - - sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" - sudo apt install docker-ce docker-ce-cli containerd.io -y - sudo usermod -aG docker $USER -1. Log out of the server, and log back in to activate docker for your user -1. Copy/paste this command to start the Ziti Admin Console in TLS mode: - - docker run -d \ - --name zac \ - -p 8443:8443 \ - -v "${ZITI_PKI}/${ZITI_EDGE_CONTROLLER_HOSTNAME}-intermediate/keys/${ZITI_EDGE_CONTROLLER_HOSTNAME}-server.key":/usr/src/app/server.key \ - -v "${ZITI_PKI}/${ZITI_EDGE_CONTROLLER_HOSTNAME}-intermediate/certs/${ZITI_EDGE_CONTROLLER_HOSTNAME}-server.chain.pem":/usr/src/app/server.chain.pem \ - openziti/zac - -## Login and use the Ziti Console -1. At this point you should be able to navigate to both: https://${ZITI_EDGE_CONTROLLER_HOSTNAME}:8443 as well as https://$(curl -s eth0.me):8443 and see the ZAC login screen. (The TLS warnings your browser will show you are normal - it's because these steps use a self-signed certificate generated in the install process) -1. Set the controller as shown: - - ![img_1.png](img_1.png) -1. Login with admin/admin - - ![img_2.png](img_2.png) - -1. **IMPORTANT!!!** Edit your profile and change the password - - ![img_3.png](img_3.png) - -## Adding Environment Variables Back to the Shell - -If you log out and log back in again you can source the '.env' file located at: `. ~/.ziti/quickstart/$(hostname)/$(hostname).env`. Below is an example of logging in, not having ZITI_HOME set, sourcing the .env file and then showing ZITI_HOME set: - - Last login: Thu May 20 11:36:25 2021 from 67.246.244.61 - ubuntu@ip-172-31-22-212:~$ echo $ZITI_HOME - - ubuntu@ip-172-31-22-212:~$ . ~/.ziti/quickstart/$(hostname)/$(hostname).env - - adding /home/ubuntu/.ziti/quickstart/ip-172-31-22-212/ziti-bin/ziti-v0.20.2 to the path - ubuntu@ip-172-31-22-212:~$ echo $ZITI_HOME - /home/ubuntu/.ziti/quickstart/ip-172-31-22-212 +This content has been moved to the official doc repository. Find it at https://openziti.github.io/ziti/quickstarts/quickstart-overview.html \ No newline at end of file diff --git a/quickstart/img_1.png b/quickstart/img_1.png deleted file mode 100644 index a350b34a7d4c6192de96dd1e4d07041595567858..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18799 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 M)78&qol`;+01lAKBLDyZ diff --git a/quickstart/img_2.png b/quickstart/img_2.png deleted file mode 100644 index 14f6a36b6f4ec4cad32c92ffb35672a2b59880eb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18412 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 &l0bhsfns`H@~dMt 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 M>FVdQ&MBb@0Is}8Pyhe` diff --git a/quickstart/img_3.png b/quickstart/img_3.png deleted file mode 100644 index bc8d9694cb59cb98d094972cbcc31549d175cc40..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 79565 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 From e9797e67cd0da7e4a915bfdb93d156c0090ce07a Mon Sep 17 00:00:00 2001 From: Paul Lorenz Date: Wed, 9 Mar 2022 09:44:38 -0500 Subject: [PATCH 04/11] Update deps and changelog. Update build for gpg signing --- .github/workflows/main.yml | 2 ++ CHANGELOG.md | 5 +++++ go.mod | 10 +++++----- go.sum | 21 ++++++++++----------- 4 files changed, 22 insertions(+), 16 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3cfcef039..390c0bfb9 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -103,6 +103,8 @@ jobs: - name: Build and Test env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + ziti_ci_gpg_key: ${{ secrets.ZITI_CI_GPG_KEY }} + ziti_ci_gpg_key_id: ${{ secrets.ZITI_CI_GPG_KEY_ID }} run: | sudo apt-get update sudo apt-get -yq install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf gcc-aarch64-linux-gnu diff --git a/CHANGELOG.md b/CHANGELOG.md index a72901345..1f88fe601 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +# Release 0.25.1 + +* Bug fix: Fix panic caused by race condition at router start up + * Regression since 0.25.0 + # Release 0.25.0 ## Breaking Changes diff --git a/go.mod b/go.mod index 16517b77b..ecc92df7e 100644 --- a/go.mod +++ b/go.mod @@ -27,7 +27,7 @@ require ( github.com/fatih/color v1.13.0 github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa github.com/go-acme/lego/v4 v4.2.0 - github.com/go-openapi/runtime v0.23.1 + github.com/go-openapi/runtime v0.23.2 github.com/go-openapi/strfmt v0.21.2 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b github.com/golang/protobuf v1.5.2 @@ -154,11 +154,11 @@ require ( github.com/yusufpapurcu/wmi v1.2.2 // indirect go.mongodb.org/mongo-driver v1.8.3 // indirect go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1 // indirect - golang.org/x/crypto v0.0.0-20220214200702-86341886e292 // indirect + golang.org/x/crypto v0.0.0-20220307211146-efcb8507fb70 // indirect golang.org/x/image v0.0.0-20191206065243-da761ea9ff43 // indirect golang.org/x/mod v0.5.1 // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect - golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 // indirect + golang.org/x/sys v0.0.0-20220307203707-22a9840ba4d7 // indirect golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect golang.org/x/text v0.3.7 // indirect golang.org/x/tools v0.1.7 // indirect @@ -173,7 +173,7 @@ require ( require ( github.com/jedib0t/go-pretty/v6 v6.2.4 - github.com/openziti/edge v0.21.120 - github.com/openziti/fabric v0.17.60 + github.com/openziti/edge v0.21.124 + github.com/openziti/fabric v0.17.61 github.com/rivo/uniseg v0.2.0 // indirect ) diff --git a/go.sum b/go.sum index 83fc98942..edd2503d5 100644 --- a/go.sum +++ b/go.sum @@ -343,8 +343,8 @@ github.com/go-openapi/runtime v0.19.15/go.mod h1:dhGWCTKRXlAfGnQG0ONViOZpjfg0m2g github.com/go-openapi/runtime v0.19.16/go.mod h1:5P9104EJgYcizotuXhEuUrzVc+j1RiSjahULvYmlv98= github.com/go-openapi/runtime v0.19.24/go.mod h1:Lm9YGCeecBnUUkFTxPC4s1+lwrkJ0pthx8YvyjCfkgk= github.com/go-openapi/runtime v0.21.0/go.mod h1:aQg+kaIQEn+A2CRSY1TxbM8+sT9g2V3aLc1FbIAnbbs= -github.com/go-openapi/runtime v0.23.1 h1:/Drg9R96eMmgKJHVWZADz78XbE39/6QiIiB45mc+epo= -github.com/go-openapi/runtime v0.23.1/go.mod h1:AKurw9fNre+h3ELZfk6ILsfvPN+bvvlaU/M9q/r9hpk= +github.com/go-openapi/runtime v0.23.2 h1:SLoYVM3ovq+lynpgrHQZ5WQcx+CBE0HHHCZU3c2jQoo= +github.com/go-openapi/runtime v0.23.2/go.mod h1:AKurw9fNre+h3ELZfk6ILsfvPN+bvvlaU/M9q/r9hpk= github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= @@ -820,19 +820,16 @@ github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+ github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= -github.com/openziti/channel v0.18.13/go.mod h1:9z2HLdoYZ3RYm9S8XqlQB5spf3j+BLPof0A1zvjLyzc= github.com/openziti/channel v0.18.14 h1:MS6Wfo0BklKI/IQ/HQl+wKJBw9rPimTSSqFIeBcsHGQ= github.com/openziti/channel v0.18.14/go.mod h1:F370DYbKEKpIEhRaZVbEK6xN+pirSZx4qnwbObQAV2I= github.com/openziti/dilithium v0.3.3 h1:PLgQ6PMNLSTzCFbX/h98cmudgz/cU6TmjdSv5NAPD8k= github.com/openziti/dilithium v0.3.3/go.mod h1:vsCjI2AU/hon9e+dLhUFbCNGesJDj2ASgkySOcpmvjo= -github.com/openziti/edge v0.21.120 h1:hoSmHPec/PEgQE4JZ3L05IBnyR8UqRpVOJUK5egQlNc= -github.com/openziti/edge v0.21.120/go.mod h1:Sdf0gOmnF3tgts9cV4nQxMzyhY/R4KpXZxKqTMje+7M= -github.com/openziti/fabric v0.17.60 h1:xUc2e2ITkpgyeWvHHb8JGv7r5AtwSzFv2PenaIiXo2E= -github.com/openziti/fabric v0.17.60/go.mod h1:8oBcmWriG/nTGF/+8pkv1oAKQY97XXG4ptns8dUTMr4= -github.com/openziti/foundation v0.17.6/go.mod h1:dTaJgdO5OqnKpV+0OPHpnTnqfPpqNGbhJv8O6pMot6o= +github.com/openziti/edge v0.21.124 h1:+GmkJ/4HFXNeOVv4zn2gmZEURGDBuDi3N8lMzwsw6sI= +github.com/openziti/edge v0.21.124/go.mod h1:qZ4Jn3Y0POBdIQR03maD+GAzNI7C5W57aiBiFE5c7is= +github.com/openziti/fabric v0.17.61 h1:3sWTJdZPfHOsyxGWA0e5D7fQhjaZWRoB8dj9JHfqaPQ= +github.com/openziti/fabric v0.17.61/go.mod h1:8oBcmWriG/nTGF/+8pkv1oAKQY97XXG4ptns8dUTMr4= github.com/openziti/foundation v0.17.8 h1:vlX6yQHrs5Tcp39xHylJhzohTDOZ6d/VrVlQahw7GBo= github.com/openziti/foundation v0.17.8/go.mod h1:dTaJgdO5OqnKpV+0OPHpnTnqfPpqNGbhJv8O6pMot6o= -github.com/openziti/sdk-golang v0.16.19/go.mod h1:ougRN3x0v2L7sHwjkoeoa05ZlWs9+0ObyUCc2Sm8Kfw= github.com/openziti/sdk-golang v0.16.20 h1:AtSaD0Do5HgkDWFR0dLUVpiOpky2WAh6cf78hcG30jk= github.com/openziti/sdk-golang v0.16.20/go.mod h1:uQ90QCehBrfO/xmpnWGVr3Jj65BMUrvGPH4I0gRl0og= github.com/oracle/oci-go-sdk v24.2.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888= @@ -1134,8 +1131,9 @@ golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWP golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220214200702-86341886e292 h1:f+lwQ+GtmgoY+A2YaQxlSOnDjXcQ7ZRLWOHbC6HtRqE= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220307211146-efcb8507fb70 h1:syTAU9FwmvzEoIYMqcPHOcVm4H3U5u90WsvuYgwpETU= +golang.org/x/crypto v0.0.0-20220307211146-efcb8507fb70/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1383,8 +1381,9 @@ golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 h1:nhht2DYV/Sn3qOayu8lM+cU1ii9sTLUeBQwQQfUHtrs= golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220307203707-22a9840ba4d7 h1:8IVLkfbr2cLhv0a/vKq4UFUcJym8RmDoDboxCFWEjYE= +golang.org/x/sys v0.0.0-20220307203707-22a9840ba4d7/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= From c792e73b06bc75303d6763c435f1bbd1a5a51583 Mon Sep 17 00:00:00 2001 From: dovholuknf <46322585+dovholuknf@users.noreply.github.com> Date: Wed, 9 Mar 2022 18:37:41 -0500 Subject: [PATCH 05/11] minor changes to get docker compose working again --- quickstart/docker/image/run-edge-router.sh | 6 +-- quickstart/docker/image/ziti-cli-functions.sh | 52 +++++++++---------- 2 files changed, 27 insertions(+), 31 deletions(-) diff --git a/quickstart/docker/image/run-edge-router.sh b/quickstart/docker/image/run-edge-router.sh index f3bccfe51..082ca1bc0 100755 --- a/quickstart/docker/image/run-edge-router.sh +++ b/quickstart/docker/image/run-edge-router.sh @@ -1,5 +1,8 @@ #!/bin/bash +# give the controller time to ramp up before running if running in docker-compose +sleep 5 + . "${ZITI_SCRIPTS}/ziti-cli-functions.sh" if [[ "${ZITI_CONTROLLER_RAWNAME-}" == "" ]]; then export export ZITI_CONTROLLER_RAWNAME="ziti-controller"; fi @@ -8,7 +11,6 @@ if [[ "${ZITI_EDGE_ROUTER_RAWNAME-}" == "" ]]; then export export ZITI_EDGE_ROUT if [[ "${ZITI_EDGE_ROUTER_PORT-}" == "" ]]; then export ZITI_EDGE_ROUTER_PORT="3022"; fi if [[ "${ZITI_EDGE_ROUTER_HOSTNAME}" == "" ]]; then export ZITI_EDGE_ROUTER_HOSTNAME="${ZITI_EDGE_ROUTER_RAWNAME}${ZITI_DOMAIN_SUFFIX}"; fi -generateEnvFile . ${ZITI_HOME}/ziti.env # shellcheck disable=SC2091 @@ -17,8 +19,6 @@ until $(curl -s -o /dev/null --fail -k "https://${ZITI_EDGE_CTRL_ADVERTISED_HOST sleep 2 done -sleep 2 - createRouterPki "${ZITI_EDGE_ROUTER_RAWNAME}" zitiLogin diff --git a/quickstart/docker/image/ziti-cli-functions.sh b/quickstart/docker/image/ziti-cli-functions.sh index d0b5882ee..cd6f027a4 100644 --- a/quickstart/docker/image/ziti-cli-functions.sh +++ b/quickstart/docker/image/ziti-cli-functions.sh @@ -423,15 +423,15 @@ function ziti_expressConfiguration { "${ZITI_BIN_DIR-}/ziti" edge delete service-edge-router-policy allSvcPublicRouters > /dev/null "${ZITI_BIN_DIR-}/ziti" edge create service-edge-router-policy allSvcPublicRouters --edge-router-roles '#public' --service-roles '#all' > /dev/null - createRouterPki - - if [[ "${ZITI_EDGE_ROUTER_RAWNAME-}" != "" ]]; then echo "ZITI_EDGE_ROUTER_RAWNAME OVERRIDDEN: $ZITI_EDGE_ROUTER_RAWNAME" else ZITI_EDGE_ROUTER_RAWNAME="${ZITI_NETWORK}-edge-router" fi echo "USING ZITI_EDGE_ROUTER_RAWNAME: $ZITI_EDGE_ROUTER_RAWNAME" + + createRouterPki "${ZITI_EDGE_ROUTER_RAWNAME}" + createEdgeRouterConfig "${ZITI_EDGE_ROUTER_RAWNAME}" #createRouterSystemdFile "${ZITI_EDGE_ROUTER_RAWNAME}" @@ -635,8 +635,18 @@ function showIssuerAndSubjectForPEM() { } function createRouterPki { - #TODO: this needs to be parameterized to take other router_name - pki_client_server "${ZITI_EDGE_ROUTER_RAWNAME}" "${ZITI_CONTROLLER_INTERMEDIATE_NAME}" "${ZITI_EDGE_ROUTER_IP_OVERRIDE-}" + # Allow router name to be passed in as arg + router_name="${1-}" + if [[ "${router_name}" == "" ]]; then + echo -e " * ERROR: $(RED "routerName needs to be supplied") " + return 1 + fi + mkdir -p "${ZITI_PKI_OS_SPECIFIC}/routers/${router_name}" + export ZITI_ROUTER_IDENTITY_CERT="${ZITI_PKI_OS_SPECIFIC}/routers/${router_name}/client.cert" + export ZITI_ROUTER_IDENTITY_SERVER_CERT="${ZITI_PKI_OS_SPECIFIC}/routers/${router_name}/server.cert" + export ZITI_ROUTER_IDENTITY_KEY="${ZITI_PKI_OS_SPECIFIC}/routers/${router_name}/server.key" + export ZITI_ROUTER_IDENTITY_CA="${ZITI_PKI_OS_SPECIFIC}/routers/${router_name}/cas.cert" + pki_client_server "${router_name}" "${ZITI_CONTROLLER_INTERMEDIATE_NAME}" "${ZITI_EDGE_ROUTER_IP_OVERRIDE-}" } function createPrivateRouterConfig { @@ -673,10 +683,9 @@ function createPrivateRouterConfig { return 1 fi -output_file="${ZITI_HOME-}/${router_name}.yaml" -"${ZITI_BIN_DIR}/ziti" create config router edge --routerName "${router_name}" --private > "${output_file}" - -echo -e "Controller configuration file written to: $(BLUE "${output_file}")" + output_file="${ZITI_HOME-}/${router_name}.yaml" + "${ZITI_BIN_DIR}/ziti" create config router edge --routerName "${router_name}" --private > "${output_file}" + echo -e "Router configuration file written to: $(BLUE "${output_file}")" } function createPki { @@ -739,10 +748,9 @@ function createFabricRouterConfig { return 1 fi -output_file="${ZITI_HOME}/${router_name}.yaml" -"${ZITI_BIN_DIR}/ziti" create config router fabric --routerName "${router_name}" > "${output_file}" - -echo -e "Fabric router configuration file written to: $(BLUE "${output_file}")" + output_file="${ZITI_HOME}/${router_name}.yaml" + "${ZITI_BIN_DIR}/ziti" create config router fabric --routerName "${router_name}" > "${output_file}" + echo -e "Fabric router configuration file written to: $(BLUE "${output_file}")" } function createEdgeRouterWssConfig { @@ -775,10 +783,9 @@ function createEdgeRouterWssConfig { return 1 fi -output_file="${ZITI_HOME-}/${router_name}.yaml" -"${ZITI_BIN_DIR}/ziti" create config router edge --wss --routerName "${router_name}" > "${output_file}" - -echo -e "Edge router wss configuration file written to: $(BLUE "${output_file}")" + output_file="${ZITI_HOME-}/${router_name}.yaml" + "${ZITI_BIN_DIR}/ziti" create config router edge --wss --routerName "${router_name}" > "${output_file}" + echo -e "WSS Edge router wss configuration file written to: $(BLUE "${output_file}")" } # shellcheck disable=SC2120 @@ -814,17 +821,8 @@ function createEdgeRouterConfig { output_file="${ZITI_HOME}/${router_name}.yaml" - mkdir -p "${ZITI_PKI_OS_SPECIFIC}/routers/${router_name}" - export ZITI_ROUTER_IDENTITY_CERT="${ZITI_PKI_OS_SPECIFIC}/routers/${router_name}/client.cert" - export ZITI_ROUTER_IDENTITY_SERVER_CERT="${ZITI_PKI_OS_SPECIFIC}/routers/${router_name}/server.cert" - export ZITI_ROUTER_IDENTITY_KEY="${ZITI_PKI_OS_SPECIFIC}/routers/${router_name}/server.key" - export ZITI_ROUTER_IDENTITY_CA="${ZITI_PKI_OS_SPECIFIC}/routers/${router_name}/cas.cert" "${ZITI_BIN_DIR}/ziti" create config router edge --routerName "${router_name}" > "${output_file}" echo -e "edge router configuration file written to: $(BLUE "${output_file}")" - unset ZITI_ROUTER_IDENTITY_CERT - unset ZITI_ROUTER_IDENTITY_SERVER_CERT - unset ZITI_ROUTER_IDENTITY_KEY - unset ZITI_ROUTER_IDENTITY_CA } function createFabricIdentity { @@ -956,8 +954,6 @@ function ziti_createEnvFile { export ZITI_BIN_ROOT="${ZITI_HOME}/ziti-bin" - if [[ "${ZITI_EDGE_ROUTER_RAWNAME-}" == "" ]]; then export ZITI_EDGE_ROUTER_RAWNAME="${ZITI_NETWORK}-edge-router"; fi - if [[ "${ZITI_CTRL_MGMT_HOST_PORT-}" == "" ]]; then export ZITI_CTRL_MGMT_HOST_PORT="${ZITI_CONTROLLER_HOSTNAME}:10000"; fi if [[ "${ZITI_CTRL_IDENTITY_CERT-}" == "" ]]; then export ZITI_CTRL_IDENTITY_CERT="${ZITI_PKI_OS_SPECIFIC}/${ZITI_CONTROLLER_INTERMEDIATE_NAME}/certs/${ZITI_CONTROLLER_HOSTNAME}-client.cert"; fi if [[ "${ZITI_CTRL_IDENTITY_SERVER_CERT-}" == "" ]]; then export ZITI_CTRL_IDENTITY_SERVER_CERT="${ZITI_PKI_OS_SPECIFIC}/${ZITI_CONTROLLER_INTERMEDIATE_NAME}/certs/${ZITI_CONTROLLER_HOSTNAME}-server.chain.pem"; fi From 8a43e4858db55c3ef9225828fa1b430c190310fc Mon Sep 17 00:00:00 2001 From: dovholuknf <46322585+dovholuknf@users.noreply.github.com> Date: Wed, 9 Mar 2022 18:41:45 -0500 Subject: [PATCH 06/11] update readme too --- quickstart/docker/README.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/quickstart/docker/README.md b/quickstart/docker/README.md index 38141c6c0..fa05e51aa 100644 --- a/quickstart/docker/README.md +++ b/quickstart/docker/README.md @@ -1,7 +1,14 @@ Building and Deploying the Latest Quickstart Docker Image ========================== -Build Docker Image +Build Docker Image for Local Dev (probably for docker-compose testing) +------------------ +1. change to this directory: `cd quickstart/docker` +2. run the questionably named `./pushDevBuild.sh` which will create a `openziti/quickstart:dev` tag +3. update `docker-compose.yml` and replace `:latest` with `:dev` +4. run `docker-compose` as normal + +Build Docker Image For Publication ------------------ 1. change to this directory: `cd quickstart/docker` 1. set an environment variable: `export ZITI_HOME=$(pwd)` From 97f596e580d01f6453562d1cf2d4149b9f3e7b77 Mon Sep 17 00:00:00 2001 From: dovholuknf <46322585+dovholuknf@users.noreply.github.com> Date: Wed, 9 Mar 2022 18:54:04 -0500 Subject: [PATCH 07/11] rename from run-edge-router to run-router. callout scripts to copy explicitly --- quickstart/docker/docker-compose.yml | 10 +++++----- quickstart/docker/image/Dockerfile | 4 +++- .../docker/image/{run-edge-router.sh => run-router.sh} | 0 3 files changed, 8 insertions(+), 6 deletions(-) rename quickstart/docker/image/{run-edge-router.sh => run-router.sh} (100%) mode change 100755 => 100644 diff --git a/quickstart/docker/docker-compose.yml b/quickstart/docker/docker-compose.yml index 871d14bfc..0ae6989c7 100644 --- a/quickstart/docker/docker-compose.yml +++ b/quickstart/docker/docker-compose.yml @@ -32,7 +32,7 @@ services: volumes: - ziti-fs:/openziti entrypoint: /bin/bash - command: "/openziti/scripts/run-edge-router.sh edge" + command: "/openziti/scripts/run-router.sh edge" ziti-edge-router-wss: image: openziti/quickstart:latest @@ -49,7 +49,7 @@ services: volumes: - ziti-fs:/openziti entrypoint: /bin/bash - command: "/openziti/scripts/run-edge-router.sh wss" + command: "/openziti/scripts/run-router.sh wss" ziti-fabric-router-br: image: openziti/quickstart:latest @@ -65,7 +65,7 @@ services: volumes: - ziti-fs:/openziti entrypoint: /bin/bash - command: "/openziti/scripts/run-edge-router.sh fabric" + command: "/openziti/scripts/run-router.sh fabric" ziti-private-red: image: openziti/quickstart:latest @@ -80,7 +80,7 @@ services: volumes: - ziti-fs:/openziti entrypoint: /bin/bash - command: "/openziti/scripts/run-edge-router.sh private" + command: "/openziti/scripts/run-router.sh private" ziti-private-blue: image: openziti/quickstart:latest @@ -95,7 +95,7 @@ services: volumes: - ziti-fs:/openziti entrypoint: /bin/bash - command: "/openziti/scripts/run-edge-router.sh private" + command: "/openziti/scripts/run-router.sh private" web-test-blue: image: crccheck/hello-world diff --git a/quickstart/docker/image/Dockerfile b/quickstart/docker/image/Dockerfile index eacecb1e5..2b2f55436 100644 --- a/quickstart/docker/image/Dockerfile +++ b/quickstart/docker/image/Dockerfile @@ -22,7 +22,9 @@ RUN mkdir -p "${ZITI_SCRIPTS}" # copy the ziti binaries to a directory already on the path COPY --chown=ziti ziti.ignore "${ZITI_BIN_DIR}" -COPY --chown=ziti *.sh "${ZITI_SCRIPTS}/" +COPY --chown=ziti ziti-cli-function.sh "${ZITI_SCRIPTS}/" +COPY --chown=ziti run-controller.sh "${ZITI_SCRIPTS}/" +COPY --chown=ziti run-router.sh "${ZITI_SCRIPTS}/" RUN /bin/bash -c "source ${ZITI_SCRIPTS}/ziti-cli-functions.sh; ziti_createEnvFile; echo source ${ZITI_HOME}/ziti.env >> ~/.bashrc" diff --git a/quickstart/docker/image/run-edge-router.sh b/quickstart/docker/image/run-router.sh old mode 100755 new mode 100644 similarity index 100% rename from quickstart/docker/image/run-edge-router.sh rename to quickstart/docker/image/run-router.sh From 1428210b8d3d4ffac085897c7640ef7b5c7c3b4e Mon Sep 17 00:00:00 2001 From: dovholuknf <46322585+dovholuknf@users.noreply.github.com> Date: Wed, 9 Mar 2022 18:55:31 -0500 Subject: [PATCH 08/11] find other run-edge-router and replace --- quickstart/README.md | 2 +- quickstart/docker/simplified-docker-compose.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/quickstart/README.md b/quickstart/README.md index ada0fb5cb..e3659bb1d 100644 --- a/quickstart/README.md +++ b/quickstart/README.md @@ -103,7 +103,7 @@ Ziti Controller: Ziti Edge Router: - routerName=edge-router; docker run -d --name "${zitinw}-${routerName}" --rm -e ZITI_EDGE_ROUTER_RAWNAME="${routerName}" --volume "${zitinw_shared}":/openziti/shared -it --network="${zitinw}" --hostname "${routerName}" --network-alias="${routerName}" --rm openziti/quickstart /openziti/scripts/run-edge-router.sh edge + routerName=edge-router; docker run -d --name "${zitinw}-${routerName}" --rm -e ZITI_EDGE_ROUTER_RAWNAME="${routerName}" --volume "${zitinw_shared}":/openziti/shared -it --network="${zitinw}" --hostname "${routerName}" --network-alias="${routerName}" --rm openziti/quickstart /openziti/scripts/run-router.sh edge diff --git a/quickstart/docker/simplified-docker-compose.yml b/quickstart/docker/simplified-docker-compose.yml index 32a9318c1..c31c57704 100644 --- a/quickstart/docker/simplified-docker-compose.yml +++ b/quickstart/docker/simplified-docker-compose.yml @@ -30,7 +30,7 @@ services: volumes: - ziti-fs:/openziti entrypoint: /bin/bash - command: "/openziti/scripts/run-edge-router.sh edge" + command: "/openziti/scripts/run-router.sh edge" ziti-console: image: openziti/zac From 7d59942d97a08994434e600281bb0f39dacd7213 Mon Sep 17 00:00:00 2001 From: gberl002 Date: Thu, 10 Mar 2022 11:18:59 -0500 Subject: [PATCH 09/11] Adding a default for when ZITI_EDGE_ROUTER_RAWNAME is blank, also fixing some typos --- quickstart/README.md | 4 ++-- ziti/cmd/ziti/cmd/create_config_router_common.go | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/quickstart/README.md b/quickstart/README.md index e3659bb1d..f3b528aa0 100644 --- a/quickstart/README.md +++ b/quickstart/README.md @@ -19,7 +19,7 @@ Lastly, you can choose to run [docker](https://docs.docker.com/get-started/) dir more verbose but should you prefer to not use docker-compose it can also illustrate how to establish a Ziti Network piece by piece. -## Prerequisties +## Prerequisites ### Bash @@ -91,7 +91,7 @@ Before running the commands below please do the following: #make a docker network for isolation while allowing the parts to be able to interact docker network create "$zitinw" -### Staring the Containers +### Starting the Containers To start the containers you can simply run these two commands in two different shells. (or choose to daemonize them once you're ready to do so). Take special note of the initial variables used in these commands. The ${zitinw} variable diff --git a/ziti/cmd/ziti/cmd/create_config_router_common.go b/ziti/cmd/ziti/cmd/create_config_router_common.go index 110c7ad82..4a2d35b3b 100644 --- a/ziti/cmd/ziti/cmd/create_config_router_common.go +++ b/ziti/cmd/ziti/cmd/create_config_router_common.go @@ -32,6 +32,8 @@ func SetZitiRouterIdentity(r *RouterTemplateValues, routerName string) { val := os.Getenv(constants.ZitiEdgeRouterRawNameVarName) if val != "" { r.Edge.Hostname = val + } else { + r.Edge.Hostname, _ = os.Hostname() } } func SetZitiRouterIdentityCert(r *RouterTemplateValues, routerName string) { From 34429aad7d8ddb5462478a976ca61b8bc62660c1 Mon Sep 17 00:00:00 2001 From: dovholuknf <46322585+dovholuknf@users.noreply.github.com> Date: Thu, 10 Mar 2022 12:55:02 -0500 Subject: [PATCH 10/11] fix typo --- quickstart/docker/attach.container.to.compose.sh | 1 + quickstart/docker/image/Dockerfile | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 quickstart/docker/attach.container.to.compose.sh diff --git a/quickstart/docker/attach.container.to.compose.sh b/quickstart/docker/attach.container.to.compose.sh new file mode 100644 index 000000000..ae92d8bde --- /dev/null +++ b/quickstart/docker/attach.container.to.compose.sh @@ -0,0 +1 @@ +docker run --network docker_zitiblue --rm -it openziti/quickstart /bin/bash diff --git a/quickstart/docker/image/Dockerfile b/quickstart/docker/image/Dockerfile index 2b2f55436..23e1918d6 100644 --- a/quickstart/docker/image/Dockerfile +++ b/quickstart/docker/image/Dockerfile @@ -22,7 +22,7 @@ RUN mkdir -p "${ZITI_SCRIPTS}" # copy the ziti binaries to a directory already on the path COPY --chown=ziti ziti.ignore "${ZITI_BIN_DIR}" -COPY --chown=ziti ziti-cli-function.sh "${ZITI_SCRIPTS}/" +COPY --chown=ziti ziti-cli-functions.sh "${ZITI_SCRIPTS}/" COPY --chown=ziti run-controller.sh "${ZITI_SCRIPTS}/" COPY --chown=ziti run-router.sh "${ZITI_SCRIPTS}/" From 038ba8edaaf12a41361352f2bfc8f2cc385c0911 Mon Sep 17 00:00:00 2001 From: Paul Lorenz Date: Thu, 10 Mar 2022 12:57:05 -0500 Subject: [PATCH 11/11] Update deps and change. Add fabric db ops. Add ziti agent command --- CHANGELOG.md | 16 ++ go.mod | 6 +- go.sum | 12 +- ziti-controller/subcmd/run.go | 19 ++- ziti/cmd/ziti/cmd/agent.go | 88 +++++++++++ ...add_route.go => agent_router_add_route.go} | 17 +-- .../{ps_router_basic.go => agent_simple.go} | 18 +-- ziti/cmd/ziti/cmd/cmd.go | 4 +- ziti/cmd/ziti/cmd/common.go | 5 - ziti/cmd/ziti/cmd/edge/traceroute.go | 12 +- ziti/cmd/ziti/cmd/fabric/db.go | 24 +++ .../cmd/ziti/cmd/fabric/db_check_integrity.go | 140 ++++++++++++++++++ ziti/cmd/ziti/cmd/fabric/db_snapshot.go | 60 ++++++++ ziti/cmd/ziti/cmd/fabric/root.go | 1 + ziti/cmd/ziti/cmd/ps.go | 58 ++------ .../ziti/cmd/ps_clear_channel_log_level.go | 13 +- ziti/cmd/ziti/cmd/ps_gc.go | 13 +- ziti/cmd/ziti/cmd/ps_get.go | 11 +- ziti/cmd/ziti/cmd/ps_goversion.go | 13 +- ziti/cmd/ziti/cmd/ps_memstats.go | 13 +- ziti/cmd/ziti/cmd/ps_pprof_cpu.go | 13 +- ziti/cmd/ziti/cmd/ps_pprof_heap.go | 11 +- ziti/cmd/ziti/cmd/ps_router_reconnect.go | 73 --------- ziti/cmd/ziti/cmd/ps_set_channel_log_level.go | 13 +- ziti/cmd/ziti/cmd/ps_set_log_level.go | 13 +- ziti/cmd/ziti/cmd/ps_setgc.go | 13 +- ziti/cmd/ziti/cmd/ps_stack.go | 13 +- ziti/cmd/ziti/cmd/ps_stats.go | 13 +- ziti/cmd/ziti/cmd/ps_trace.go | 11 +- ziti/cmd/ziti/cmd/pscmd.go | 113 -------------- ziti/cmd/ziti/cmd/version.go | 22 +-- 31 files changed, 481 insertions(+), 370 deletions(-) create mode 100644 ziti/cmd/ziti/cmd/agent.go rename ziti/cmd/ziti/cmd/{ps_router_add_route.go => agent_router_add_route.go} (86%) rename ziti/cmd/ziti/cmd/{ps_router_basic.go => agent_simple.go} (75%) create mode 100644 ziti/cmd/ziti/cmd/fabric/db.go create mode 100644 ziti/cmd/ziti/cmd/fabric/db_check_integrity.go create mode 100644 ziti/cmd/ziti/cmd/fabric/db_snapshot.go delete mode 100644 ziti/cmd/ziti/cmd/ps_router_reconnect.go delete mode 100644 ziti/cmd/ziti/cmd/pscmd.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f88fe601..ac382a583 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,19 @@ +# Release 0.25.2 + +## Deprecations +The Ziti Edge management REST `/database` and `/terminators` endpoints are being deprecated. They belong in the +fabric management API, but there was no fabric REST api at the time when they were added. Now that they are +available under fabric, they will be removed from the edge APIs in a future release, v0.26 or later. + +## What's New + +* Enhancement: Only translate router ids -> names in `ziti edge traceroute` when requested to with flag +* Enhancement: Add the /database rest API from edge to fabric, where they below + * `ziti fabric db` now as the same commands as `ziti edge db` +* Enhancement: Add `ziti agent` command for sending IPC commands. Contains copy of what was under `ziti ps`. +* Enhancement: Add `ziti agent controller snapshot-db ` IPC command + + # Release 0.25.1 * Bug fix: Fix panic caused by race condition at router start up diff --git a/go.mod b/go.mod index ecc92df7e..d5f31b50d 100644 --- a/go.mod +++ b/go.mod @@ -158,7 +158,7 @@ require ( golang.org/x/image v0.0.0-20191206065243-da761ea9ff43 // indirect golang.org/x/mod v0.5.1 // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect - golang.org/x/sys v0.0.0-20220307203707-22a9840ba4d7 // indirect + golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5 // indirect golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect golang.org/x/text v0.3.7 // indirect golang.org/x/tools v0.1.7 // indirect @@ -173,7 +173,7 @@ require ( require ( github.com/jedib0t/go-pretty/v6 v6.2.4 - github.com/openziti/edge v0.21.124 - github.com/openziti/fabric v0.17.61 + github.com/openziti/edge v0.21.127 + github.com/openziti/fabric v0.17.62 github.com/rivo/uniseg v0.2.0 // indirect ) diff --git a/go.sum b/go.sum index edd2503d5..308145830 100644 --- a/go.sum +++ b/go.sum @@ -824,10 +824,10 @@ github.com/openziti/channel v0.18.14 h1:MS6Wfo0BklKI/IQ/HQl+wKJBw9rPimTSSqFIeBcs github.com/openziti/channel v0.18.14/go.mod h1:F370DYbKEKpIEhRaZVbEK6xN+pirSZx4qnwbObQAV2I= github.com/openziti/dilithium v0.3.3 h1:PLgQ6PMNLSTzCFbX/h98cmudgz/cU6TmjdSv5NAPD8k= github.com/openziti/dilithium v0.3.3/go.mod h1:vsCjI2AU/hon9e+dLhUFbCNGesJDj2ASgkySOcpmvjo= -github.com/openziti/edge v0.21.124 h1:+GmkJ/4HFXNeOVv4zn2gmZEURGDBuDi3N8lMzwsw6sI= -github.com/openziti/edge v0.21.124/go.mod h1:qZ4Jn3Y0POBdIQR03maD+GAzNI7C5W57aiBiFE5c7is= -github.com/openziti/fabric v0.17.61 h1:3sWTJdZPfHOsyxGWA0e5D7fQhjaZWRoB8dj9JHfqaPQ= -github.com/openziti/fabric v0.17.61/go.mod h1:8oBcmWriG/nTGF/+8pkv1oAKQY97XXG4ptns8dUTMr4= +github.com/openziti/edge v0.21.127 h1:4Z2AlmSjMgw/a0PQt4aYuVIURW0nQVwnR0mSvgyVZcc= +github.com/openziti/edge v0.21.127/go.mod h1:NIOrmA5yEhdTbmARyPm4DOrdpWiaB33OVpJVj4PkEDY= +github.com/openziti/fabric v0.17.62 h1:V1Eavso4AvMSCMXJYtEATu5NQ9Dtpu3s3OYZt+SuVwY= +github.com/openziti/fabric v0.17.62/go.mod h1:8oBcmWriG/nTGF/+8pkv1oAKQY97XXG4ptns8dUTMr4= github.com/openziti/foundation v0.17.8 h1:vlX6yQHrs5Tcp39xHylJhzohTDOZ6d/VrVlQahw7GBo= github.com/openziti/foundation v0.17.8/go.mod h1:dTaJgdO5OqnKpV+0OPHpnTnqfPpqNGbhJv8O6pMot6o= github.com/openziti/sdk-golang v0.16.20 h1:AtSaD0Do5HgkDWFR0dLUVpiOpky2WAh6cf78hcG30jk= @@ -1382,8 +1382,8 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220307203707-22a9840ba4d7 h1:8IVLkfbr2cLhv0a/vKq4UFUcJym8RmDoDboxCFWEjYE= -golang.org/x/sys v0.0.0-20220307203707-22a9840ba4d7/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5 h1:y/woIyUBFbpQGKS0u1aHF/40WUDnek3fPOyD08H5Vng= +golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= diff --git a/ziti-controller/subcmd/run.go b/ziti-controller/subcmd/run.go index c7878abed..aa25cb8cb 100644 --- a/ziti-controller/subcmd/run.go +++ b/ziti-controller/subcmd/run.go @@ -25,6 +25,7 @@ import ( "github.com/openziti/ziti/common/version" "github.com/sirupsen/logrus" "github.com/spf13/cobra" + "io" "os" "os/signal" "syscall" @@ -59,12 +60,6 @@ func run(cmd *cobra.Command, args []string) { startLogger = startLogger.WithField("nodeId", config.Id.Token) startLogger.Info("starting ziti-controller") - if cliAgentEnabled { - if err := agent.Listen(agent.Options{Addr: cliAgentAddr}); err != nil { - pfxlog.Logger().WithError(err).Error("unable to start CLI agent") - } - } - var fabricController *controller.Controller if fabricController, err = controller.NewController(config, version.GetCmdBuildInfo()); err != nil { fmt.Printf("unable to create fabric controller %+v\n", err) @@ -78,6 +73,18 @@ func run(cmd *cobra.Command, args []string) { } edgeController.Initialize() + + if cliAgentEnabled { + options := agent.Options{Addr: cliAgentAddr} + fabricController.RegisterDefaultAgentOps() + options.CustomOps = map[byte]func(conn io.ReadWriter) error{ + agent.CustomOp: fabricController.HandleCustomAgentOp, + } + if err := agent.Listen(options); err != nil { + pfxlog.Logger().WithError(err).Error("unable to start CLI agent") + } + } + go waitForShutdown(fabricController, edgeController) edgeController.Run() if err := fabricController.Run(); err != nil { diff --git a/ziti/cmd/ziti/cmd/agent.go b/ziti/cmd/ziti/cmd/agent.go new file mode 100644 index 000000000..38dd96693 --- /dev/null +++ b/ziti/cmd/ziti/cmd/agent.go @@ -0,0 +1,88 @@ +/* + Copyright NetFoundry, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package cmd + +import ( + "github.com/openziti/edge/router/debugops" + "github.com/openziti/fabric/controller" + "github.com/openziti/fabric/router" + cmdhelper "github.com/openziti/ziti/ziti/cmd/ziti/cmd/helpers" + "github.com/spf13/cobra" + "io" +) + +type AgentAppId byte + +const ( + AgentAppController = AgentAppId(controller.AgentAppId) + AgentAppRouter = AgentAppId(router.AgentAppId) +) + +func NewAgentCmd(out io.Writer, errOut io.Writer) *cobra.Command { + agentCmd := &cobra.Command{ + Use: "agent", + Short: "Interact with ziti processes using the the IPC agent", + Run: func(cmd *cobra.Command, args []string) { + cmdhelper.CheckErr(cmd.Help()) + }, + } + + ctrlCmd := &cobra.Command{ + Use: "controller", + Aliases: []string{"c"}, + Short: "Interact with a ziti-controller process using the IPC agent", + Run: func(cmd *cobra.Command, args []string) { + cmdhelper.CheckErr(cmd.Help()) + }, + } + + agentCmd.AddCommand(ctrlCmd) + ctrlCmd.AddCommand(NewSimpleAgentCustomCmd("snapshot-db", AgentAppController, controller.AgentOpSnapshotDbSnaps, out, errOut)) + + routerCmd := &cobra.Command{ + Use: "router", + Aliases: []string{"r"}, + Short: "Interact with a ziti-router process using the IPC agent", + Run: func(cmd *cobra.Command, args []string) { + cmdhelper.CheckErr(cmd.Help()) + }, + } + + agentCmd.AddCommand(routerCmd) + + routerCmd.AddCommand(NewCmdPsRoute(out, errOut)) + routerCmd.AddCommand(NewSimpleAgentCustomCmd("dump-routes", AgentAppRouter, router.DumpForwarderTables, out, errOut)) + routerCmd.AddCommand(NewSimpleAgentCustomCmd("disconnect", AgentAppRouter, router.CloseControlChannel, out, errOut)) + routerCmd.AddCommand(NewSimpleAgentCustomCmd("reconnect", AgentAppRouter, router.OpenControlChannel, out, errOut)) + routerCmd.AddCommand(NewSimpleAgentCustomCmd("dump-api-sessions", AgentAppRouter, debugops.DumpApiSessions, out, errOut)) + routerCmd.AddCommand(NewSimpleAgentCustomCmd("dump-links", AgentAppRouter, router.DumpLinks, out, errOut)) + + agentCmd.AddCommand(NewCmdPsGoversion(out, errOut)) + agentCmd.AddCommand(NewCmdPsGc(out, errOut)) + agentCmd.AddCommand(NewCmdPsSetgc(out, errOut)) + agentCmd.AddCommand(NewCmdPsStack(out, errOut)) + agentCmd.AddCommand(NewCmdPsMemstats(out, errOut)) + agentCmd.AddCommand(NewCmdPsStats(out, errOut)) + agentCmd.AddCommand(NewCmdPsPprofHeap(out, errOut)) + agentCmd.AddCommand(NewCmdPsPprofCpu(out, errOut)) + agentCmd.AddCommand(NewCmdPsTrace(out, errOut)) + agentCmd.AddCommand(NewCmdPsSetLogLevel(out, errOut)) + agentCmd.AddCommand(NewCmdPsSetChannelLogLevel(out, errOut)) + agentCmd.AddCommand(NewCmdPsClearChannelLogLevel(out, errOut)) + + return agentCmd +} diff --git a/ziti/cmd/ziti/cmd/ps_router_add_route.go b/ziti/cmd/ziti/cmd/agent_router_add_route.go similarity index 86% rename from ziti/cmd/ziti/cmd/ps_router_add_route.go rename to ziti/cmd/ziti/cmd/agent_router_add_route.go index ef4b677da..19cb804a8 100644 --- a/ziti/cmd/ziti/cmd/ps_router_add_route.go +++ b/ziti/cmd/ziti/cmd/agent_router_add_route.go @@ -22,7 +22,6 @@ import ( "github.com/openziti/fabric/pb/ctrl_pb" "github.com/openziti/fabric/router" "github.com/openziti/foundation/agent" - cmdutil "github.com/openziti/ziti/ziti/cmd/ziti/cmd/factory" cmdhelper "github.com/openziti/ziti/ziti/cmd/ziti/cmd/helpers" "github.com/spf13/cobra" "io" @@ -36,13 +35,12 @@ type PsRouteOptions struct { } // NewCmdPsRoute creates a command object for the "create" command -func NewCmdPsRoute(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.Command { +func NewCmdPsRoute(out io.Writer, errOut io.Writer) *cobra.Command { options := &PsRouteOptions{ PsOptions: PsOptions{ CommonOptions: CommonOptions{ - Factory: f, - Out: out, - Err: errOut, + Out: out, + Err: errOut, }, }, } @@ -92,14 +90,15 @@ func (o *PsRouteOptions) Run() error { return err } - fullBuf := make([]byte, len(buf)+5) - fullBuf[0] = router.UpdateRoute + fullBuf := make([]byte, len(buf)+6) + fullBuf[0] = byte(AgentAppRouter) + fullBuf[1] = router.UpdateRoute sizeBuf := make([]byte, 4) binary.LittleEndian.PutUint32(sizeBuf, uint32(len(buf))) - copy(fullBuf[1:], sizeBuf) - copy(fullBuf[5:], buf) + copy(fullBuf[2:], sizeBuf) + copy(fullBuf[6:], buf) return agent.MakeRequest(addr, agent.CustomOp, fullBuf, os.Stdout) } diff --git a/ziti/cmd/ziti/cmd/ps_router_basic.go b/ziti/cmd/ziti/cmd/agent_simple.go similarity index 75% rename from ziti/cmd/ziti/cmd/ps_router_basic.go rename to ziti/cmd/ziti/cmd/agent_simple.go index 93cb5f7de..f5257f473 100644 --- a/ziti/cmd/ziti/cmd/ps_router_basic.go +++ b/ziti/cmd/ziti/cmd/agent_simple.go @@ -18,24 +18,22 @@ package cmd import ( "github.com/openziti/foundation/agent" - cmdutil "github.com/openziti/ziti/ziti/cmd/ziti/cmd/factory" cmdhelper "github.com/openziti/ziti/ziti/cmd/ziti/cmd/helpers" "github.com/spf13/cobra" "io" "os" ) -type PsBasicRouterCmdOptions struct { +type SimpleAgentCmdOptions struct { PsOptions } -func NewCmdPsBasicRouterCmd(name string, op byte, f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.Command { - options := &PsBasicRouterCmdOptions{ +func NewSimpleAgentCustomCmd(name string, appId AgentAppId, op byte, out io.Writer, errOut io.Writer) *cobra.Command { + options := &SimpleAgentCmdOptions{ PsOptions: PsOptions{ CommonOptions: CommonOptions{ - Factory: f, - Out: out, - Err: errOut, + Out: out, + Err: errOut, }, }, } @@ -46,7 +44,7 @@ func NewCmdPsBasicRouterCmd(name string, op byte, f cmdutil.Factory, out io.Writ Run: func(cmd *cobra.Command, args []string) { options.Cmd = cmd options.Args = args - err := options.Run(op) + err := options.Run(appId, op) cmdhelper.CheckErr(err) }, } @@ -57,13 +55,13 @@ func NewCmdPsBasicRouterCmd(name string, op byte, f cmdutil.Factory, out io.Writ } // Run implements the command -func (o *PsBasicRouterCmdOptions) Run(op byte) error { +func (o *SimpleAgentCmdOptions) Run(appId AgentAppId, op byte) error { addr, err := agent.ParseGopsAddress(o.Args) if err != nil { return err } - buf := []byte{op} + buf := []byte{byte(appId), op} return agent.MakeRequest(addr, agent.CustomOp, buf, os.Stdout) } diff --git a/ziti/cmd/ziti/cmd/cmd.go b/ziti/cmd/ziti/cmd/cmd.go index c8fb557f6..d8e361f5d 100644 --- a/ziti/cmd/ziti/cmd/cmd.go +++ b/ziti/cmd/ziti/cmd/cmd.go @@ -131,7 +131,8 @@ func NewCmdRoot(f cmdutil.Factory, in io.Reader, out, err io.Writer) *cobra.Comm createCommands := NewCmdCreate(f, out, err) updateCommands := NewCmdUpdate(f, out, err) executeCommands := NewCmdExecute(f, out, err) - psCommands := NewCmdPs(f, out, err) + agentCommands := NewAgentCmd(out, err) + psCommands := NewCmdPs(out, err) pkiCommands := NewCmdPKI(f, out, err) fabricCommand := fabric.NewFabricCmd(p) edgeCommand := edge.NewCmdEdge(f, out, err) @@ -161,6 +162,7 @@ func NewCmdRoot(f cmdutil.Factory, in io.Reader, out, err io.Writer) *cobra.Comm Message: "Executing Ziti components:", Commands: []*cobra.Command{ executeCommands, + agentCommands, psCommands, pkiCommands, unwrapIdentityFileCommand, diff --git a/ziti/cmd/ziti/cmd/common.go b/ziti/cmd/ziti/cmd/common.go index 3f675dd09..a83fdbd3a 100644 --- a/ziti/cmd/ziti/cmd/common.go +++ b/ziti/cmd/ziti/cmd/common.go @@ -19,7 +19,6 @@ package cmd import ( "fmt" cmdutil "github.com/openziti/ziti/ziti/cmd/ziti/cmd/factory" - "github.com/openziti/ziti/ziti/cmd/ziti/cmd/table" "github.com/openziti/ziti/ziti/cmd/ziti/internal/log" "github.com/spf13/cobra" "io" @@ -57,10 +56,6 @@ func (c *CommonOptions) Stdout() io.Writer { return os.Stdout } -func (c *CommonOptions) CreateTable() table.Table { - return c.Factory.CreateTable(c.Stdout()) -} - // Debugf outputs the given text to the console if verbose mode is enabled func (c *CommonOptions) Debugf(format string, a ...interface{}) { if c.Verbose { diff --git a/ziti/cmd/ziti/cmd/edge/traceroute.go b/ziti/cmd/ziti/cmd/edge/traceroute.go index 3d370d3bf..8c49a10e4 100644 --- a/ziti/cmd/ziti/cmd/edge/traceroute.go +++ b/ziti/cmd/ziti/cmd/edge/traceroute.go @@ -20,6 +20,7 @@ type traceRouteOptions struct { hops uint8 configFile string timeout time.Duration + lookupRouterName bool } func newTraceRouteCmd(out io.Writer, errOut io.Writer) *cobra.Command { @@ -50,6 +51,7 @@ func newTraceRouteCmd(out io.Writer, errOut io.Writer) *cobra.Command { cmd.Flags().Uint8Var(&options.hops, "hops", 0, "Maximum number of hops") cmd.Flags().DurationVarP(&options.timeout, "timeout", "t", 5*time.Second, "Trace route response timeout") cmd.Flags().BoolVarP(&options.Verbose, "verbose", "", false, "Enable verbose logging") + cmd.Flags().BoolVarP(&options.lookupRouterName, "get-router-names", "n", false, "Lookup and output router names instead of ids. Requires admin privileges") return cmd } @@ -99,10 +101,12 @@ func (o *traceRouteOptions) Run() error { hopLabel := result.HopId if result.HopType == "forwarder" { - hopLabel, err = mapIdToName("transit-routers", result.HopId, o.Options) - if err != nil { - hopLabel = result.HopId - routerNameLookupsFailed = true + if o.lookupRouterName { + hopLabel, err = mapIdToName("transit-routers", result.HopId, o.Options) + if err != nil { + hopLabel = result.HopId + routerNameLookupsFailed = true + } } } diff --git a/ziti/cmd/ziti/cmd/fabric/db.go b/ziti/cmd/ziti/cmd/fabric/db.go new file mode 100644 index 000000000..849db59ff --- /dev/null +++ b/ziti/cmd/ziti/cmd/fabric/db.go @@ -0,0 +1,24 @@ +package fabric + +import ( + "github.com/openziti/ziti/ziti/cmd/ziti/cmd/common" + cmdhelper "github.com/openziti/ziti/ziti/cmd/ziti/cmd/helpers" + "github.com/spf13/cobra" +) + +func newDbCmd(p common.OptionsProvider) *cobra.Command { + cmd := &cobra.Command{ + Use: "db", + Short: "Database management operations for the Ziti Edge Controller", + Run: func(cmd *cobra.Command, args []string) { + err := cmd.Help() + cmdhelper.CheckErr(err) + }, + } + + cmd.AddCommand(newDbSnapshotCmd(p)) + cmd.AddCommand(newDbCheckIntegrityCmd(p)) + cmd.AddCommand(newDbCheckIntegrityStatusCmd(p)) + + return cmd +} diff --git a/ziti/cmd/ziti/cmd/fabric/db_check_integrity.go b/ziti/cmd/ziti/cmd/fabric/db_check_integrity.go new file mode 100644 index 000000000..46d9fb153 --- /dev/null +++ b/ziti/cmd/ziti/cmd/fabric/db_check_integrity.go @@ -0,0 +1,140 @@ +package fabric + +import ( + "fmt" + "github.com/Jeffail/gabs" + "github.com/openziti/ziti/ziti/cmd/ziti/cmd/api" + "github.com/openziti/ziti/ziti/cmd/ziti/cmd/common" + cmdhelper "github.com/openziti/ziti/ziti/cmd/ziti/cmd/helpers" + "github.com/openziti/ziti/ziti/cmd/ziti/util" + "github.com/pkg/errors" + "github.com/spf13/cobra" + "net/http" +) + +type dbCheckIntegrityOptions struct { + api.Options + fix bool +} + +func newDbCheckIntegrityCmd(p common.OptionsProvider) *cobra.Command { + options := &dbCheckIntegrityOptions{ + Options: api.Options{CommonOptions: p()}, + } + + cmd := &cobra.Command{ + Use: "start-check-integrity", + Short: "starts background operation checking integrity of database references and constraints", + Args: cobra.ExactArgs(0), + Run: func(cmd *cobra.Command, args []string) { + options.Cmd = cmd + options.Args = args + err := runCheckIntegrityDb(options) + cmdhelper.CheckErr(err) + }, + SuggestFor: []string{}, + } + + // allow interspersing positional args and flags + cmd.Flags().SetInterspersed(true) + cmd.Flags().BoolVarP(&options.fix, "fix-errors", "f", false, "Attempt to fix any detected errors") + options.AddCommonFlags(cmd) + + return cmd +} + +func runCheckIntegrityDb(o *dbCheckIntegrityOptions) error { + var target string + if o.fix { + target = "database/fix-data-integrity" + } else { + target = "database/check-data-integrity" + } + + if _, err := util.ControllerUpdate("edge", target, "", o.Out, http.MethodPost, o.OutputJSONRequest, o.OutputJSONResponse, o.Options.Timeout, o.Options.Verbose); err != nil { + return err + } + + _, err := fmt.Fprint(o.Out, "check integrity operation started\n") + return err +} + +func newDbCheckIntegrityStatusCmd(p common.OptionsProvider) *cobra.Command { + options := &dbCheckIntegrityOptions{ + Options: api.Options{CommonOptions: p()}, + } + + cmd := &cobra.Command{ + Use: "check-integrity-status", + Short: "shows current results from background operation checking integrity of database references and constraints", + Args: cobra.ExactArgs(0), + Run: func(cmd *cobra.Command, args []string) { + options.Cmd = cmd + options.Args = args + err := runCheckIntegrityStatus(options) + cmdhelper.CheckErr(err) + }, + SuggestFor: []string{}, + } + + // allow interspersing positional args and flags + cmd.Flags().SetInterspersed(true) + options.AddCommonFlags(cmd) + + return cmd +} + +func runCheckIntegrityStatus(o *dbCheckIntegrityOptions) error { + body, err := util.EdgeControllerList("database/data-integrity-results", nil, o.OutputJSONResponse, o.Out, o.Options.Timeout, o.Options.Verbose) + if err != nil { + return err + } + + data := body.S("data") + inProgress := data.S("inProgress").Data() + if _, err = fmt.Fprintf(o.Out, "In Progress: %v\n", inProgress); err != nil { + return err + } + + fixingErrors := data.S("fixingErrors").Data() + if _, err = fmt.Fprintf(o.Out, "Fixing Errors: %v\n", fixingErrors); err != nil { + return err + } + + tooManyErrors := data.S("tooManyErrors").Data() + if _, err = fmt.Fprintf(o.Out, "Too Many Errors: %v (if true, additional errors can be found in controller log)\n", tooManyErrors); err != nil { + return err + } + + startTime := data.S("startTime").Data() + if _, err = fmt.Fprintf(o.Out, "Started At: %v\n", startTime); err != nil { + return err + } + + endTime := data.S("endTime").Data() + if _, err = fmt.Fprintf(o.Out, "Finished At: %v\n", endTime); err != nil { + return err + } + + opError := data.S("error").Data() + if _, err = fmt.Fprintf(o.Out, "Operation Error: %v\n", opError); err != nil { + return err + } + + results := data.S("results") + children, err := results.Children() + if len(children) == 0 || errors.Is(gabs.ErrNotObjOrArray, err) { + _, err = fmt.Fprintln(o.Out, "no data integrity errors found") + } + + for _, child := range children { + desc := child.S("description").Data().(string) + fixed := child.S("fixed").Data().(bool) + + if _, err = fmt.Fprintf(o.Out, "Issue: %v. Fixed: %v\n", desc, fixed); err != nil { + return err + } + } + + return err +} diff --git a/ziti/cmd/ziti/cmd/fabric/db_snapshot.go b/ziti/cmd/ziti/cmd/fabric/db_snapshot.go new file mode 100644 index 000000000..3c82124ee --- /dev/null +++ b/ziti/cmd/ziti/cmd/fabric/db_snapshot.go @@ -0,0 +1,60 @@ +/* + Copyright NetFoundry, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package fabric + +import ( + "github.com/openziti/ziti/ziti/cmd/ziti/cmd/api" + "github.com/openziti/ziti/ziti/cmd/ziti/cmd/common" + cmdhelper "github.com/openziti/ziti/ziti/cmd/ziti/cmd/helpers" + "github.com/openziti/ziti/ziti/cmd/ziti/util" + "github.com/spf13/cobra" + "net/http" +) + +type dbSnapshotOptions struct { + api.Options +} + +func newDbSnapshotCmd(p common.OptionsProvider) *cobra.Command { + options := &dbSnapshotOptions{ + Options: api.Options{CommonOptions: p()}, + } + + cmd := &cobra.Command{ + Use: "snapshot", + Short: "creates a database snapshot", + Args: cobra.ExactArgs(0), + Run: func(cmd *cobra.Command, args []string) { + options.Cmd = cmd + options.Args = args + err := runSnapshotDb(options) + cmdhelper.CheckErr(err) + }, + SuggestFor: []string{}, + } + + // allow interspersing positional args and flags + cmd.Flags().SetInterspersed(true) + options.AddCommonFlags(cmd) + + return cmd +} + +func runSnapshotDb(o *dbSnapshotOptions) error { + _, err := util.ControllerUpdate("edge", "database/snapshot", "", o.Out, http.MethodPost, false, false, o.Options.Timeout, o.Options.Verbose) + return err +} diff --git a/ziti/cmd/ziti/cmd/fabric/root.go b/ziti/cmd/ziti/cmd/fabric/root.go index 1061f9ebd..2266757c4 100644 --- a/ziti/cmd/ziti/cmd/fabric/root.go +++ b/ziti/cmd/ziti/cmd/fabric/root.go @@ -33,6 +33,7 @@ func NewFabricCmd(p common.OptionsProvider) *cobra.Command { fabricCmd.AddCommand(newAddIdentityCmd(p), newRemoveIdentityCmd(p)) fabricCmd.AddCommand(newCreateCommand(p), newListCmd(p), newUpdateCommand(p), newDeleteCmd(p)) fabricCmd.AddCommand(newInspectCmd(p)) + fabricCmd.AddCommand(newDbCmd(p)) return fabricCmd } diff --git a/ziti/cmd/ziti/cmd/ps.go b/ziti/cmd/ziti/cmd/ps.go index 3aec9b609..f4a75a29a 100644 --- a/ziti/cmd/ziti/cmd/ps.go +++ b/ziti/cmd/ziti/cmd/ps.go @@ -19,8 +19,6 @@ package cmd import ( "bytes" "fmt" - "github.com/openziti/edge/router/debugops" - "github.com/openziti/fabric/router" "github.com/spf13/cobra" "io" "os" @@ -28,7 +26,6 @@ import ( "strconv" "strings" - cmdutil "github.com/openziti/ziti/ziti/cmd/ziti/cmd/factory" cmdhelper "github.com/openziti/ziti/ziti/cmd/ziti/cmd/helpers" "github.com/openziti/ziti/ziti/cmd/ziti/cmd/templates" "github.com/openziti/ziti/ziti/goprocess" @@ -52,12 +49,11 @@ var ( ) // NewCmdPs Pss a command object for the "Ps" command -func NewCmdPs(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.Command { +func NewCmdPs(out io.Writer, errOut io.Writer) *cobra.Command { options := &PsOptions{ CommonOptions: CommonOptions{ - Factory: f, - Out: out, - Err: errOut, + Out: out, + Err: errOut, }, } @@ -73,45 +69,23 @@ func NewCmdPs(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.Command }, } - routerCmd := &cobra.Command{ - Use: "router", - Short: "Show Ziti router process info", - Long: psLong, - Run: func(cmd *cobra.Command, args []string) { - cmdhelper.CheckErr(cmd.Help()) - }, - } + cmd.AddCommand(NewCmdPsGet(out, errOut)) + cmd.AddCommand(NewCmdPsGoversion(out, errOut)) + cmd.AddCommand(NewCmdPsGc(out, errOut)) + cmd.AddCommand(NewCmdPsSetgc(out, errOut)) + cmd.AddCommand(NewCmdPsStack(out, errOut)) + cmd.AddCommand(NewCmdPsMemstats(out, errOut)) + cmd.AddCommand(NewCmdPsStats(out, errOut)) + cmd.AddCommand(NewCmdPsPprofHeap(out, errOut)) + cmd.AddCommand(NewCmdPsPprofCpu(out, errOut)) + cmd.AddCommand(NewCmdPsTrace(out, errOut)) + cmd.AddCommand(NewCmdPsSetLogLevel(out, errOut)) + cmd.AddCommand(NewCmdPsSetChannelLogLevel(out, errOut)) + cmd.AddCommand(NewCmdPsClearChannelLogLevel(out, errOut)) - cmd.AddCommand(routerCmd) - cmd.AddCommand(NewCmdPsGet(f, out, errOut)) - cmd.AddCommand(NewCmdPsGoversion(f, out, errOut)) - cmd.AddCommand(NewCmdPsGc(f, out, errOut)) - cmd.AddCommand(NewCmdPsSetgc(f, out, errOut)) - cmd.AddCommand(NewCmdPsStack(f, out, errOut)) - cmd.AddCommand(NewCmdPsMemstats(f, out, errOut)) - cmd.AddCommand(NewCmdPsStats(f, out, errOut)) - cmd.AddCommand(NewCmdPsPprofHeap(f, out, errOut)) - cmd.AddCommand(NewCmdPsPprofCpu(f, out, errOut)) - cmd.AddCommand(NewCmdPsTrace(f, out, errOut)) - cmd.AddCommand(NewCmdPsSetLogLevel(f, out, errOut)) - cmd.AddCommand(NewCmdPsSetChannelLogLevel(f, out, errOut)) - cmd.AddCommand(NewCmdPsClearChannelLogLevel(f, out, errOut)) - - routerCmd.AddCommand(NewCmdPsRoute(f, out, errOut)) - routerCmd.AddCommand(NewCmdPsBasicRouterCmd("dump-routes", router.DumpForwarderTables, f, out, errOut)) - routerCmd.AddCommand(NewCmdPsBasicRouterCmd("disconnect", router.CloseControlChannel, f, out, errOut)) - routerCmd.AddCommand(NewCmdPsBasicRouterCmd("reconnect", router.OpenControlChannel, f, out, errOut)) - routerCmd.AddCommand(NewCmdPsBasicRouterCmd("dump-api-sessions", debugops.DumpApiSessions, f, out, errOut)) - routerCmd.AddCommand(NewCmdPsBasicRouterCmd("dump-links", router.DumpLinks, f, out, errOut)) - - options.addPsFlags(cmd) return cmd } -func (options *PsOptions) addPsFlags(cmd *cobra.Command) { - // cmd.Flags().StringVarP(&options.Flags.Identity, "identity", "", "", "Which identity to use.") -} - // Run implements this command func (o *PsOptions) Run() error { fmt.Println("ps is running") diff --git a/ziti/cmd/ziti/cmd/ps_clear_channel_log_level.go b/ziti/cmd/ziti/cmd/ps_clear_channel_log_level.go index ade37810c..391dc1076 100644 --- a/ziti/cmd/ziti/cmd/ps_clear_channel_log_level.go +++ b/ziti/cmd/ziti/cmd/ps_clear_channel_log_level.go @@ -20,7 +20,6 @@ import ( "bytes" "encoding/binary" "github.com/openziti/foundation/agent" - cmdutil "github.com/openziti/ziti/ziti/cmd/ziti/cmd/factory" cmdhelper "github.com/openziti/ziti/ziti/cmd/ziti/cmd/helpers" "github.com/spf13/cobra" "io" @@ -33,20 +32,20 @@ type PsClearChannelLogLevelOptions struct { } // NewCmdPsClearChannelLogLevel creates a command object for the "create" command -func NewCmdPsClearChannelLogLevel(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.Command { +func NewCmdPsClearChannelLogLevel(out io.Writer, errOut io.Writer) *cobra.Command { options := &PsClearChannelLogLevelOptions{ PsOptions: PsOptions{ CommonOptions: CommonOptions{ - Factory: f, - Out: out, - Err: errOut, + Out: out, + Err: errOut, }, }, } cmd := &cobra.Command{ - Use: "clear-channel-log-level target channel", - Args: cobra.MinimumNArgs(1), + Use: "clear-channel-log-level target channel", + Short: "Clears a channel-specific log level in the target application", + Args: cobra.MinimumNArgs(1), Run: func(cmd *cobra.Command, args []string) { options.Cmd = cmd options.Args = args diff --git a/ziti/cmd/ziti/cmd/ps_gc.go b/ziti/cmd/ziti/cmd/ps_gc.go index 6eb66f8ba..3b00584b4 100644 --- a/ziti/cmd/ziti/cmd/ps_gc.go +++ b/ziti/cmd/ziti/cmd/ps_gc.go @@ -18,7 +18,6 @@ package cmd import ( "github.com/openziti/foundation/agent" - cmdutil "github.com/openziti/ziti/ziti/cmd/ziti/cmd/factory" cmdhelper "github.com/openziti/ziti/ziti/cmd/ziti/cmd/helpers" "github.com/spf13/cobra" "io" @@ -32,20 +31,20 @@ type PsGcOptions struct { } // NewCmdPsGc creates a command object for the "create" command -func NewCmdPsGc(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.Command { +func NewCmdPsGc(out io.Writer, errOut io.Writer) *cobra.Command { options := &PsGcOptions{ PsOptions: PsOptions{ CommonOptions: CommonOptions{ - Factory: f, - Out: out, - Err: errOut, + Out: out, + Err: errOut, }, }, } cmd := &cobra.Command{ - Use: "gc ", - Args: cobra.MaximumNArgs(1), + Use: "gc ", + Short: "Run garbage collection in the target application", + Args: cobra.MaximumNArgs(1), Run: func(cmd *cobra.Command, args []string) { options.Cmd = cmd options.Args = args diff --git a/ziti/cmd/ziti/cmd/ps_get.go b/ziti/cmd/ziti/cmd/ps_get.go index d5178b540..546415686 100644 --- a/ziti/cmd/ziti/cmd/ps_get.go +++ b/ziti/cmd/ziti/cmd/ps_get.go @@ -23,7 +23,6 @@ import ( "io" "strconv" - cmdutil "github.com/openziti/ziti/ziti/cmd/ziti/cmd/factory" cmdhelper "github.com/openziti/ziti/ziti/cmd/ziti/cmd/helpers" "github.com/openziti/ziti/ziti/cmd/ziti/internal/log" ) @@ -36,19 +35,19 @@ type PsGetOptions struct { } // NewCmdPsGet creates a command object for the "create" command -func NewCmdPsGet(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.Command { +func NewCmdPsGet(out io.Writer, errOut io.Writer) *cobra.Command { options := &PsGetOptions{ PsOptions: PsOptions{ CommonOptions: CommonOptions{ - Factory: f, - Out: out, - Err: errOut, + Out: out, + Err: errOut, }, }, } cmd := &cobra.Command{ - Use: "get", + Use: "get", + Short: "Returns information about the target process", Run: func(cmd *cobra.Command, args []string) { options.Cmd = cmd options.Args = args diff --git a/ziti/cmd/ziti/cmd/ps_goversion.go b/ziti/cmd/ziti/cmd/ps_goversion.go index e7ab5dcf5..c846d8e64 100644 --- a/ziti/cmd/ziti/cmd/ps_goversion.go +++ b/ziti/cmd/ziti/cmd/ps_goversion.go @@ -18,7 +18,6 @@ package cmd import ( "github.com/openziti/foundation/agent" - cmdutil "github.com/openziti/ziti/ziti/cmd/ziti/cmd/factory" cmdhelper "github.com/openziti/ziti/ziti/cmd/ziti/cmd/helpers" "github.com/spf13/cobra" "io" @@ -32,20 +31,20 @@ type PsGoversionOptions struct { } // NewCmdPsGoversion creates a command object for the "create" command -func NewCmdPsGoversion(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.Command { +func NewCmdPsGoversion(out io.Writer, errOut io.Writer) *cobra.Command { options := &PsGoversionOptions{ PsOptions: PsOptions{ CommonOptions: CommonOptions{ - Factory: f, - Out: out, - Err: errOut, + Out: out, + Err: errOut, }, }, } cmd := &cobra.Command{ - Use: "goversion ", - Args: cobra.MaximumNArgs(1), + Use: "goversion ", + Short: "Returns the golang version of the target application", + Args: cobra.MaximumNArgs(1), Run: func(cmd *cobra.Command, args []string) { options.Cmd = cmd options.Args = args diff --git a/ziti/cmd/ziti/cmd/ps_memstats.go b/ziti/cmd/ziti/cmd/ps_memstats.go index a9fbb9df3..733e5e3fc 100644 --- a/ziti/cmd/ziti/cmd/ps_memstats.go +++ b/ziti/cmd/ziti/cmd/ps_memstats.go @@ -18,7 +18,6 @@ package cmd import ( "github.com/openziti/foundation/agent" - cmdutil "github.com/openziti/ziti/ziti/cmd/ziti/cmd/factory" cmdhelper "github.com/openziti/ziti/ziti/cmd/ziti/cmd/helpers" "github.com/spf13/cobra" "io" @@ -32,20 +31,20 @@ type PsMemstatsOptions struct { } // NewCmdPsMemstats creates a command object for the "create" command -func NewCmdPsMemstats(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.Command { +func NewCmdPsMemstats(out io.Writer, errOut io.Writer) *cobra.Command { options := &PsMemstatsOptions{ PsOptions: PsOptions{ CommonOptions: CommonOptions{ - Factory: f, - Out: out, - Err: errOut, + Out: out, + Err: errOut, }, }, } cmd := &cobra.Command{ - Use: "memstats ", - Args: cobra.MaximumNArgs(1), + Use: "memstats ", + Short: "Returns memory use summary of the target application", + Args: cobra.MaximumNArgs(1), Run: func(cmd *cobra.Command, args []string) { options.Cmd = cmd options.Args = args diff --git a/ziti/cmd/ziti/cmd/ps_pprof_cpu.go b/ziti/cmd/ziti/cmd/ps_pprof_cpu.go index 0f6273bfb..6abb8e9c7 100644 --- a/ziti/cmd/ziti/cmd/ps_pprof_cpu.go +++ b/ziti/cmd/ziti/cmd/ps_pprof_cpu.go @@ -18,7 +18,6 @@ package cmd import ( "github.com/openziti/foundation/agent" - cmdutil "github.com/openziti/ziti/ziti/cmd/ziti/cmd/factory" cmdhelper "github.com/openziti/ziti/ziti/cmd/ziti/cmd/helpers" "github.com/spf13/cobra" "io" @@ -32,20 +31,20 @@ type PsPprofCpuOptions struct { } // NewCmdPsPprofCpu creates a command object for the "create" command -func NewCmdPsPprofCpu(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.Command { +func NewCmdPsPprofCpu(out io.Writer, errOut io.Writer) *cobra.Command { options := &PsPprofCpuOptions{ PsOptions: PsOptions{ CommonOptions: CommonOptions{ - Factory: f, - Out: out, - Err: errOut, + Out: out, + Err: errOut, }, }, } cmd := &cobra.Command{ - Use: "pprof-cpu", - Args: cobra.MaximumNArgs(2), + Use: "pprof-cpu", + Short: "Runs and emits a 30 second pprof from the target application", + Args: cobra.MaximumNArgs(2), Run: func(cmd *cobra.Command, args []string) { options.Cmd = cmd options.Args = args diff --git a/ziti/cmd/ziti/cmd/ps_pprof_heap.go b/ziti/cmd/ziti/cmd/ps_pprof_heap.go index 371c38ae3..ca7a383f3 100644 --- a/ziti/cmd/ziti/cmd/ps_pprof_heap.go +++ b/ziti/cmd/ziti/cmd/ps_pprof_heap.go @@ -18,7 +18,6 @@ package cmd import ( "github.com/openziti/foundation/agent" - cmdutil "github.com/openziti/ziti/ziti/cmd/ziti/cmd/factory" cmdhelper "github.com/openziti/ziti/ziti/cmd/ziti/cmd/helpers" "github.com/spf13/cobra" "io" @@ -31,19 +30,19 @@ type PsPprofHeapOptions struct { } // NewCmdPsPprofHeap creates a command object for the "create" command -func NewCmdPsPprofHeap(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.Command { +func NewCmdPsPprofHeap(out io.Writer, errOut io.Writer) *cobra.Command { options := &PsPprofHeapOptions{ PsOptions: PsOptions{ CommonOptions: CommonOptions{ - Factory: f, - Out: out, - Err: errOut, + Out: out, + Err: errOut, }, }, } cmd := &cobra.Command{ - Use: "pprof-heap", + Use: "pprof-heap", + Short: "Returns a memory heap pprof of the target application", Run: func(cmd *cobra.Command, args []string) { options.Cmd = cmd options.Args = args diff --git a/ziti/cmd/ziti/cmd/ps_router_reconnect.go b/ziti/cmd/ziti/cmd/ps_router_reconnect.go deleted file mode 100644 index c4f1a9ce1..000000000 --- a/ziti/cmd/ziti/cmd/ps_router_reconnect.go +++ /dev/null @@ -1,73 +0,0 @@ -/* - Copyright NetFoundry, Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package cmd - -import ( - "github.com/openziti/fabric/router" - "github.com/openziti/foundation/agent" - cmdutil "github.com/openziti/ziti/ziti/cmd/ziti/cmd/factory" - cmdhelper "github.com/openziti/ziti/ziti/cmd/ziti/cmd/helpers" - "github.com/spf13/cobra" - "io" - "os" -) - -// PsRouteOptions the options for the create spring command -type PsRouterReconnectOptions struct { - PsOptions - CtrlListener string -} - -// NewCmdPsRouterReconnect creates a command object for the "router-disconnect" command -func NewCmdPsRouterReconnect(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.Command { - options := &PsRouterReconnectOptions{ - PsOptions: PsOptions{ - CommonOptions: CommonOptions{ - Factory: f, - Out: out, - Err: errOut, - }, - }, - } - - cmd := &cobra.Command{ - Args: cobra.MaximumNArgs(1), - Use: "router-reconnect ", - Run: func(cmd *cobra.Command, args []string) { - options.Cmd = cmd - options.Args = args - err := options.Run() - cmdhelper.CheckErr(err) - }, - } - - options.addCommonFlags(cmd) - - return cmd -} - -// Run implements the command -func (o *PsRouterReconnectOptions) Run() error { - addr, err := agent.ParseGopsAddress(o.Args) - if err != nil { - return err - } - - buf := []byte{router.OpenControlChannel} - - return agent.MakeRequest(addr, agent.CustomOp, buf, os.Stdout) -} diff --git a/ziti/cmd/ziti/cmd/ps_set_channel_log_level.go b/ziti/cmd/ziti/cmd/ps_set_channel_log_level.go index 4a1bd5a54..d3ec563fa 100644 --- a/ziti/cmd/ziti/cmd/ps_set_channel_log_level.go +++ b/ziti/cmd/ziti/cmd/ps_set_channel_log_level.go @@ -20,7 +20,6 @@ import ( "bytes" "encoding/binary" "github.com/openziti/foundation/agent" - cmdutil "github.com/openziti/ziti/ziti/cmd/ziti/cmd/factory" cmdhelper "github.com/openziti/ziti/ziti/cmd/ziti/cmd/helpers" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -36,20 +35,20 @@ type PsSetChannelLogLevelOptions struct { } // NewCmdPsSetChannelLogLevel creates a command object for the "create" command -func NewCmdPsSetChannelLogLevel(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.Command { +func NewCmdPsSetChannelLogLevel(out io.Writer, errOut io.Writer) *cobra.Command { options := &PsSetChannelLogLevelOptions{ PsOptions: PsOptions{ CommonOptions: CommonOptions{ - Factory: f, - Out: out, - Err: errOut, + Out: out, + Err: errOut, }, }, } cmd := &cobra.Command{ - Use: "set-channel-log-level target channel log-level (panic, fatal, error, warn, info, debug, trace)", - Args: cobra.MinimumNArgs(2), + Use: "set-channel-log-level target channel log-level (panic, fatal, error, warn, info, debug, trace)", + Short: "Sets a channel-specific log level in the target application", + Args: cobra.MinimumNArgs(2), Run: func(cmd *cobra.Command, args []string) { options.Cmd = cmd options.Args = args diff --git a/ziti/cmd/ziti/cmd/ps_set_log_level.go b/ziti/cmd/ziti/cmd/ps_set_log_level.go index bf0d473df..347fdd4bc 100644 --- a/ziti/cmd/ziti/cmd/ps_set_log_level.go +++ b/ziti/cmd/ziti/cmd/ps_set_log_level.go @@ -18,7 +18,6 @@ package cmd import ( "github.com/openziti/foundation/agent" - cmdutil "github.com/openziti/ziti/ziti/cmd/ziti/cmd/factory" cmdhelper "github.com/openziti/ziti/ziti/cmd/ziti/cmd/helpers" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -35,20 +34,20 @@ type PsSetLogLevelOptions struct { } // NewCmdPsLogLevel creates a command object for the "create" command -func NewCmdPsSetLogLevel(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.Command { +func NewCmdPsSetLogLevel(out io.Writer, errOut io.Writer) *cobra.Command { options := &PsSetLogLevelOptions{ PsOptions: PsOptions{ CommonOptions: CommonOptions{ - Factory: f, - Out: out, - Err: errOut, + Out: out, + Err: errOut, }, }, } cmd := &cobra.Command{ - Use: "set-log-level target log-level (panic, fatal, error, warn, info, debug, trace)", - Args: cobra.MinimumNArgs(1), + Use: "set-log-level target log-level (panic, fatal, error, warn, info, debug, trace)", + Short: "Sets the global logrus logging level in the target application", + Args: cobra.MinimumNArgs(1), Run: func(cmd *cobra.Command, args []string) { options.Cmd = cmd options.Args = args diff --git a/ziti/cmd/ziti/cmd/ps_setgc.go b/ziti/cmd/ziti/cmd/ps_setgc.go index 4acb78e97..3101cb9f8 100644 --- a/ziti/cmd/ziti/cmd/ps_setgc.go +++ b/ziti/cmd/ziti/cmd/ps_setgc.go @@ -19,7 +19,6 @@ package cmd import ( "encoding/binary" "github.com/openziti/foundation/agent" - cmdutil "github.com/openziti/ziti/ziti/cmd/ziti/cmd/factory" cmdhelper "github.com/openziti/ziti/ziti/cmd/ziti/cmd/helpers" "github.com/spf13/cobra" "io" @@ -34,20 +33,20 @@ type PsSetgcOptions struct { } // NewCmdPsSetgc creates a command object for the "create" command -func NewCmdPsSetgc(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.Command { +func NewCmdPsSetgc(out io.Writer, errOut io.Writer) *cobra.Command { options := &PsSetgcOptions{ PsOptions: PsOptions{ CommonOptions: CommonOptions{ - Factory: f, - Out: out, - Err: errOut, + Out: out, + Err: errOut, }, }, } cmd := &cobra.Command{ - Use: "setgc target gc-percentage", - Args: cobra.MinimumNArgs(1), + Use: "setgc target gc-percentage", + Short: "Sets the GC percentage in the target application", + Args: cobra.MinimumNArgs(1), Run: func(cmd *cobra.Command, args []string) { options.Cmd = cmd options.Args = args diff --git a/ziti/cmd/ziti/cmd/ps_stack.go b/ziti/cmd/ziti/cmd/ps_stack.go index 856a9edf2..d55ca6a36 100644 --- a/ziti/cmd/ziti/cmd/ps_stack.go +++ b/ziti/cmd/ziti/cmd/ps_stack.go @@ -18,7 +18,6 @@ package cmd import ( "github.com/openziti/foundation/agent" - cmdutil "github.com/openziti/ziti/ziti/cmd/ziti/cmd/factory" cmdhelper "github.com/openziti/ziti/ziti/cmd/ziti/cmd/helpers" "github.com/spf13/cobra" "io" @@ -34,20 +33,20 @@ type PsStackOptions struct { } // NewCmdPsStack creates a command object for the "create" command -func NewCmdPsStack(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.Command { +func NewCmdPsStack(out io.Writer, errOut io.Writer) *cobra.Command { options := &PsStackOptions{ PsOptions: PsOptions{ CommonOptions: CommonOptions{ - Factory: f, - Out: out, - Err: errOut, + Out: out, + Err: errOut, }, }, } cmd := &cobra.Command{ - Args: cobra.MaximumNArgs(1), - Use: "stack []", + Args: cobra.MaximumNArgs(1), + Use: "stack []", + Short: "Emits a go-routine stack dump from the target application", Run: func(cmd *cobra.Command, args []string) { options.Cmd = cmd options.Args = args diff --git a/ziti/cmd/ziti/cmd/ps_stats.go b/ziti/cmd/ziti/cmd/ps_stats.go index 3b617baf1..485dc1a78 100644 --- a/ziti/cmd/ziti/cmd/ps_stats.go +++ b/ziti/cmd/ziti/cmd/ps_stats.go @@ -18,7 +18,6 @@ package cmd import ( "github.com/openziti/foundation/agent" - cmdutil "github.com/openziti/ziti/ziti/cmd/ziti/cmd/factory" cmdhelper "github.com/openziti/ziti/ziti/cmd/ziti/cmd/helpers" "github.com/spf13/cobra" "io" @@ -32,20 +31,20 @@ type PsStatsOptions struct { } // NewCmdPsStats creates a command object for the "create" command -func NewCmdPsStats(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.Command { +func NewCmdPsStats(out io.Writer, errOut io.Writer) *cobra.Command { options := &PsStatsOptions{ PsOptions: PsOptions{ CommonOptions: CommonOptions{ - Factory: f, - Out: out, - Err: errOut, + Out: out, + Err: errOut, }, }, } cmd := &cobra.Command{ - Use: "stats ", - Args: cobra.MaximumNArgs(1), + Use: "stats ", + Short: "Emits some runtime information (# go-routines, threads, cpus, etc) from the target application", + Args: cobra.MaximumNArgs(1), Run: func(cmd *cobra.Command, args []string) { options.Cmd = cmd options.Args = args diff --git a/ziti/cmd/ziti/cmd/ps_trace.go b/ziti/cmd/ziti/cmd/ps_trace.go index 6d27d863b..885f8d5b8 100644 --- a/ziti/cmd/ziti/cmd/ps_trace.go +++ b/ziti/cmd/ziti/cmd/ps_trace.go @@ -18,7 +18,6 @@ package cmd import ( "github.com/openziti/foundation/agent" - cmdutil "github.com/openziti/ziti/ziti/cmd/ziti/cmd/factory" cmdhelper "github.com/openziti/ziti/ziti/cmd/ziti/cmd/helpers" "github.com/spf13/cobra" "io" @@ -31,19 +30,19 @@ type PsTraceOptions struct { } // NewCmdPsTrace creates a command object for the "create" command -func NewCmdPsTrace(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.Command { +func NewCmdPsTrace(out io.Writer, errOut io.Writer) *cobra.Command { options := &PsTraceOptions{ PsOptions: PsOptions{ CommonOptions: CommonOptions{ - Factory: f, - Out: out, - Err: errOut, + Out: out, + Err: errOut, }, }, } cmd := &cobra.Command{ - Use: "trace", + Use: "trace", + Short: "Turn tracing on for 5 seconds in the target application", Run: func(cmd *cobra.Command, args []string) { options.Cmd = cmd options.Args = args diff --git a/ziti/cmd/ziti/cmd/pscmd.go b/ziti/cmd/ziti/cmd/pscmd.go deleted file mode 100644 index 497a7b025..000000000 --- a/ziti/cmd/ziti/cmd/pscmd.go +++ /dev/null @@ -1,113 +0,0 @@ -/* - Copyright NetFoundry, Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package cmd - -import ( - "errors" - "fmt" - "github.com/openziti/foundation/agent" - "io" - "io/ioutil" - "net" - "os" - "os/exec" - "strconv" - "strings" - - "github.com/openziti/ziti/ziti/util" -) - -func trace(addr net.TCPAddr, _ []string) error { - fmt.Println("Tracing now, will take 5 secs...") - out, err := cmd(addr, agent.Trace) - if err != nil { - return err - } - if len(out) == 0 { - return errors.New("nothing has traced") - } - tmpfile, err := ioutil.TempFile("", "trace") - if err != nil { - return err - } - if err := ioutil.WriteFile(tmpfile.Name(), out, 0); err != nil { - return err - } - fmt.Printf("Trace dump saved to: %s\n", tmpfile.Name()) - // If go tool chain not found, stopping here and keep trace file. - if _, err := exec.LookPath("go"); err != nil { - return nil - } - defer os.Remove(tmpfile.Name()) - cmd := exec.Command("go", "tool", "trace", tmpfile.Name()) - cmd.Env = os.Environ() - cmd.Stdin = os.Stdin - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - return cmd.Run() -} - -// targetToAddr tries to parse the target string, be it remote host:port -// or local process's PID. -func targetToAddr(target string) (*net.TCPAddr, error) { - if strings.Contains(target, ":") { - // addr host:port passed - var err error - addr, err := net.ResolveTCPAddr("tcp", target) - if err != nil { - return nil, fmt.Errorf("couldn't parse dst address: %v", err) - } - return addr, nil - } - // try to find port by pid then, connect to local - pid, err := strconv.Atoi(target) - if err != nil { - return nil, fmt.Errorf("couldn't parse PID: %v", err) - } - port, err := util.GetPort(pid) - if err != nil { - return nil, fmt.Errorf("couldn't get port for PID %v: %v", pid, err) - } - addr, _ := net.ResolveTCPAddr("tcp", "127.0.0.1:"+port) - return addr, nil -} - -func cmd(addr net.TCPAddr, c byte, params ...byte) ([]byte, error) { - conn, err := cmdLazy(addr, c, params...) - if err != nil { - return nil, fmt.Errorf("couldn't get port by PID: %v", err) - } - - all, err := ioutil.ReadAll(conn) - if err != nil { - return nil, err - } - return all, nil -} - -func cmdLazy(addr net.TCPAddr, c byte, params ...byte) (io.Reader, error) { - conn, err := net.DialTCP("tcp", nil, &addr) - if err != nil { - return nil, err - } - buf := []byte{c} - buf = append(buf, params...) - if _, err := conn.Write(buf); err != nil { - return nil, err - } - return conn, nil -} diff --git a/ziti/cmd/ziti/cmd/version.go b/ziti/cmd/ziti/cmd/version.go index 5792c176b..c8ce5a858 100644 --- a/ziti/cmd/ziti/cmd/version.go +++ b/ziti/cmd/ziti/cmd/version.go @@ -22,6 +22,8 @@ import ( "github.com/openziti/ziti/ziti/cmd/ziti/cmd/table" "github.com/openziti/ziti/ziti/cmd/ziti/internal/log" "io" + "os" + // "strings" "github.com/openziti/ziti/common/version" @@ -77,19 +79,19 @@ func (o *VersionOptions) Run() error { info := util.ColorInfo - table := o.CreateTable() - table.AddRow("NAME", "VERSION") + t := table.CreateTable(os.Stdout) + t.AddRow("NAME", "VERSION") - table.AddRow(c.ZITI, info(version.GetBuildMetadata(o.Verbose))) + t.AddRow(c.ZITI, info(version.GetBuildMetadata(o.Verbose))) - o.versionPrintZitiApp(c.ZITI_CONTROLLER, &table) - o.versionPrintZitiApp(c.ZITI_FABRIC, &table) - o.versionPrintZitiApp(c.ZITI_PROX_C, &table) - o.versionPrintZitiApp(c.ZITI_ROUTER, &table) - o.versionPrintZitiApp(c.ZITI_TUNNEL, &table) - o.versionPrintZitiApp(c.ZITI_EDGE_TUNNEL, &table) + o.versionPrintZitiApp(c.ZITI_CONTROLLER, &t) + o.versionPrintZitiApp(c.ZITI_FABRIC, &t) + o.versionPrintZitiApp(c.ZITI_PROX_C, &t) + o.versionPrintZitiApp(c.ZITI_ROUTER, &t) + o.versionPrintZitiApp(c.ZITI_TUNNEL, &t) + o.versionPrintZitiApp(c.ZITI_EDGE_TUNNEL, &t) - table.Render() + t.Render() if !o.NoVersionCheck && !o.Verbose { return o.versionCheck()