diff --git a/CHANGELOG.md b/CHANGELOG.md index a684f154e..b4fa0eaa0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ FEATURE: New `zrok overview` command, which returns all of the account details as a single JSON structure. See the OpenAPI spec at `specs/zrok.yml` for more details of the `/api/v1/overview` endpoint (https://github.com/openziti/zrok/issues/374) +FEATURE: New `zrok` SDK (https://github.com/openziti/zrok/issues/34). `pastebin` example illustrates basic SDK usage (see `sdk/examples/pastebin/README.md` for details) ((https://github.com/openziti/zrok/issues/379) + # v0.4.2 Some days are just like this. `v0.4.2` is a re-do of `v0.4.1`. Trying to get Homebrew working and had a bad release. Hopefully this is the one. diff --git a/cmd/zrok/accessPrivate.go b/cmd/zrok/accessPrivate.go index 9c23990e8..3730493e5 100644 --- a/cmd/zrok/accessPrivate.go +++ b/cmd/zrok/accessPrivate.go @@ -8,11 +8,11 @@ import ( "github.com/openziti/zrok/endpoints/proxy" "github.com/openziti/zrok/endpoints/tcpTunnel" "github.com/openziti/zrok/endpoints/udpTunnel" + "github.com/openziti/zrok/environment" "github.com/openziti/zrok/rest_client_zrok" "github.com/openziti/zrok/rest_client_zrok/share" "github.com/openziti/zrok/rest_model_zrok" "github.com/openziti/zrok/tui" - "github.com/openziti/zrok/zrokdir" "github.com/sirupsen/logrus" "github.com/spf13/cobra" "net/url" @@ -48,16 +48,16 @@ func newAccessPrivateCommand() *accessPrivateCommand { func (cmd *accessPrivateCommand) run(_ *cobra.Command, args []string) { shrToken := args[0] - zrd, err := zrokdir.Load() + env, err := environment.LoadRoot() if err != nil { - tui.Error("unable to load zrokdir", err) + tui.Error("error loading environment", err) } - if zrd.Env == nil { + if !env.IsEnabled() { tui.Error("unable to load environment; did you 'zrok enable'?", nil) } - zrok, err := zrd.Client() + zrok, err := env.Client() if err != nil { if !panicInstead { tui.Error("unable to create zrok client", err) @@ -65,11 +65,11 @@ func (cmd *accessPrivateCommand) run(_ *cobra.Command, args []string) { panic(err) } - auth := httptransport.APIKeyAuth("X-TOKEN", "header", zrd.Env.Token) + auth := httptransport.APIKeyAuth("X-TOKEN", "header", env.Environment().Token) req := share.NewAccessParams() req.Body = &rest_model_zrok.AccessRequest{ ShrToken: shrToken, - EnvZID: zrd.Env.ZId, + EnvZID: env.Environment().ZitiIdentity, } accessResp, err := zrok.Share.Access(req, auth) if err != nil { @@ -101,20 +101,20 @@ func (cmd *accessPrivateCommand) run(_ *cobra.Command, args []string) { case "tcpTunnel": fe, err := tcpTunnel.NewFrontend(&tcpTunnel.FrontendConfig{ BindAddress: cmd.bindAddress, - IdentityName: "backend", + IdentityName: env.EnvironmentIdentityName(), ShrToken: args[0], RequestsChan: requests, }) if err != nil { if !panicInstead { - tui.Error("unable to create private frontend", err) + tui.Error("unable to create private access", err) } panic(err) } go func() { if err := fe.Run(); err != nil { if !panicInstead { - tui.Error("error starting frontend", err) + tui.Error("error starting access", err) } panic(err) } @@ -123,7 +123,7 @@ func (cmd *accessPrivateCommand) run(_ *cobra.Command, args []string) { case "udpTunnel": fe, err := udpTunnel.NewFrontend(&udpTunnel.FrontendConfig{ BindAddress: cmd.bindAddress, - IdentityName: "backend", + IdentityName: env.EnvironmentIdentityName(), ShrToken: args[0], RequestsChan: requests, IdleTime: time.Minute, @@ -144,7 +144,7 @@ func (cmd *accessPrivateCommand) run(_ *cobra.Command, args []string) { }() default: - cfg := proxy.DefaultFrontendConfig("backend") + cfg := proxy.DefaultFrontendConfig(env.EnvironmentIdentityName()) cfg.ShrToken = shrToken cfg.Address = cmd.bindAddress cfg.RequestsChan = requests @@ -168,7 +168,7 @@ func (cmd *accessPrivateCommand) run(_ *cobra.Command, args []string) { signal.Notify(c, os.Interrupt, syscall.SIGTERM) go func() { <-c - cmd.destroy(accessResp.Payload.FrontendToken, zrd.Env.ZId, shrToken, zrok, auth) + cmd.destroy(accessResp.Payload.FrontendToken, env.Environment().ZitiIdentity, shrToken, zrok, auth) os.Exit(0) }() @@ -203,7 +203,7 @@ func (cmd *accessPrivateCommand) run(_ *cobra.Command, args []string) { } close(requests) - cmd.destroy(accessResp.Payload.FrontendToken, zrd.Env.ZId, shrToken, zrok, auth) + cmd.destroy(accessResp.Payload.FrontendToken, env.Environment().ZitiIdentity, shrToken, zrok, auth) } } diff --git a/cmd/zrok/adminBootstrap.go b/cmd/zrok/adminBootstrap.go index 2501d17c1..e2e8416df 100644 --- a/cmd/zrok/adminBootstrap.go +++ b/cmd/zrok/adminBootstrap.go @@ -14,7 +14,6 @@ func init() { type adminBootstrap struct { cmd *cobra.Command - skipCtrl bool skipFrontend bool } @@ -26,7 +25,6 @@ func newAdminBootstrap() *adminBootstrap { } command := &adminBootstrap{cmd: cmd} cmd.Run = command.run - cmd.Flags().BoolVar(&command.skipCtrl, "skip-ctrl", false, "Skip controller (ctrl) identity bootstrapping") cmd.Flags().BoolVar(&command.skipFrontend, "skip-frontend", false, "Skip frontend identity bootstrapping") return command } @@ -38,7 +36,7 @@ func (cmd *adminBootstrap) run(_ *cobra.Command, args []string) { panic(err) } logrus.Infof(cf.Dump(inCfg, cf.DefaultOptions())) - if err := controller.Bootstrap(cmd.skipCtrl, cmd.skipFrontend, inCfg); err != nil { + if err := controller.Bootstrap(cmd.skipFrontend, inCfg); err != nil { panic(err) } logrus.Info("bootstrap complete!") diff --git a/cmd/zrok/adminCreateFrontend.go b/cmd/zrok/adminCreateFrontend.go index 34588f6c3..430ecc59c 100644 --- a/cmd/zrok/adminCreateFrontend.go +++ b/cmd/zrok/adminCreateFrontend.go @@ -1,14 +1,13 @@ package main import ( - "os" - + "github.com/openziti/zrok/environment" "github.com/openziti/zrok/rest_client_zrok/admin" "github.com/openziti/zrok/rest_model_zrok" "github.com/openziti/zrok/tui" - "github.com/openziti/zrok/zrokdir" "github.com/sirupsen/logrus" "github.com/spf13/cobra" + "os" ) func init() { @@ -35,12 +34,12 @@ func (cmd *adminCreateFrontendCommand) run(_ *cobra.Command, args []string) { publicName := args[1] urlTemplate := args[2] - zrd, err := zrokdir.Load() + env, err := environment.LoadRoot() if err != nil { panic(err) } - zrok, err := zrd.Client() + zrok, err := env.Client() if err != nil { panic(err) } diff --git a/cmd/zrok/adminCreateIdentity.go b/cmd/zrok/adminCreateIdentity.go index ee04feefd..f32ad348b 100644 --- a/cmd/zrok/adminCreateIdentity.go +++ b/cmd/zrok/adminCreateIdentity.go @@ -2,8 +2,8 @@ package main import ( "fmt" + "github.com/openziti/zrok/environment" "github.com/openziti/zrok/rest_client_zrok/admin" - "github.com/openziti/zrok/zrokdir" "github.com/sirupsen/logrus" "github.com/spf13/cobra" "os" @@ -32,7 +32,11 @@ func newAdminCreateIdentity() *adminCreateIdentity { func (cmd *adminCreateIdentity) run(_ *cobra.Command, args []string) { name := args[0] - zif, err := zrokdir.ZitiIdentityFile(name) + env, err := environment.LoadRoot() + if err != nil { + panic(err) + } + zif, err := env.ZitiIdentityNamed(name) if err != nil { panic(err) } @@ -41,12 +45,7 @@ func (cmd *adminCreateIdentity) run(_ *cobra.Command, args []string) { os.Exit(1) } - zrd, err := zrokdir.Load() - if err != nil { - panic(err) - } - - zrok, err := zrd.Client() + zrok, err := env.Client() if err != nil { panic(err) } @@ -59,7 +58,7 @@ func (cmd *adminCreateIdentity) run(_ *cobra.Command, args []string) { panic(err) } - if err := zrokdir.SaveZitiIdentity(name, resp.Payload.Cfg); err != nil { + if err := env.SaveZitiIdentityNamed(name, resp.Payload.Cfg); err != nil { panic(err) } diff --git a/cmd/zrok/adminDeleteFrontend.go b/cmd/zrok/adminDeleteFrontend.go index 071889bed..9d806df72 100644 --- a/cmd/zrok/adminDeleteFrontend.go +++ b/cmd/zrok/adminDeleteFrontend.go @@ -1,9 +1,9 @@ package main import ( + "github.com/openziti/zrok/environment" "github.com/openziti/zrok/rest_client_zrok/admin" "github.com/openziti/zrok/rest_model_zrok" - "github.com/openziti/zrok/zrokdir" "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -30,12 +30,12 @@ func newAdminDeleteFrontendCommand() *adminDeleteFrontendCommand { func (cmd *adminDeleteFrontendCommand) run(_ *cobra.Command, args []string) { feToken := args[0] - zrd, err := zrokdir.Load() + env, err := environment.LoadRoot() if err != nil { panic(err) } - zrok, err := zrd.Client() + zrok, err := env.Client() if err != nil { panic(err) } diff --git a/cmd/zrok/adminGenerate.go b/cmd/zrok/adminGenerate.go index afe5aa15c..9576646d7 100644 --- a/cmd/zrok/adminGenerate.go +++ b/cmd/zrok/adminGenerate.go @@ -3,9 +3,9 @@ package main import ( "fmt" "github.com/jaevor/go-nanoid" + "github.com/openziti/zrok/environment" "github.com/openziti/zrok/rest_client_zrok/admin" "github.com/openziti/zrok/rest_model_zrok" - "github.com/openziti/zrok/zrokdir" "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -43,12 +43,12 @@ func (cmd *adminGenerateCommand) run(_ *cobra.Command, args []string) { } } - zrd, err := zrokdir.Load() + env, err := environment.LoadRoot() if err != nil { - logrus.Error("error loading zrokdir", err) + logrus.Error("error loading environment", err) } - zrok, err := zrd.Client() + zrok, err := env.Client() if err != nil { if !panicInstead { logrus.Error("error creating zrok api client", err) diff --git a/cmd/zrok/adminListFrontends.go b/cmd/zrok/adminListFrontends.go index 8a8fbf640..ee7ece190 100644 --- a/cmd/zrok/adminListFrontends.go +++ b/cmd/zrok/adminListFrontends.go @@ -3,8 +3,8 @@ package main import ( "fmt" "github.com/jedib0t/go-pretty/v6/table" + "github.com/openziti/zrok/environment" "github.com/openziti/zrok/rest_client_zrok/admin" - "github.com/openziti/zrok/zrokdir" "github.com/spf13/cobra" "os" "time" @@ -31,12 +31,12 @@ func newAdminListFrontendsCommand() *adminListFrontendsCommand { } func (cmd *adminListFrontendsCommand) run(_ *cobra.Command, _ []string) { - zrd, err := zrokdir.Load() + env, err := environment.LoadRoot() if err != nil { panic(err) } - zrok, err := zrd.Client() + zrok, err := env.Client() if err != nil { panic(err) } diff --git a/cmd/zrok/adminUpdateFrontend.go b/cmd/zrok/adminUpdateFrontend.go index 2605458a0..2c77513d3 100644 --- a/cmd/zrok/adminUpdateFrontend.go +++ b/cmd/zrok/adminUpdateFrontend.go @@ -1,9 +1,9 @@ package main import ( + "github.com/openziti/zrok/environment" "github.com/openziti/zrok/rest_client_zrok/admin" "github.com/openziti/zrok/rest_model_zrok" - "github.com/openziti/zrok/zrokdir" "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -38,12 +38,12 @@ func (cmd *adminUpdateFrontendCommand) run(_ *cobra.Command, args []string) { panic("must specify at least one of public name or url template") } - zrd, err := zrokdir.Load() + env, err := environment.LoadRoot() if err != nil { panic(err) } - zrok, err := zrd.Client() + zrok, err := env.Client() if err != nil { panic(err) } diff --git a/cmd/zrok/configGet.go b/cmd/zrok/configGet.go index 5340cf4b8..45480da41 100644 --- a/cmd/zrok/configGet.go +++ b/cmd/zrok/configGet.go @@ -2,7 +2,7 @@ package main import ( "fmt" - "github.com/openziti/zrok/zrokdir" + "github.com/openziti/zrok/environment" "github.com/spf13/cobra" ) @@ -28,15 +28,15 @@ func newConfigGetCommand() *configGetCommand { func (cmd *configGetCommand) run(_ *cobra.Command, args []string) { configName := args[0] - zrd, err := zrokdir.Load() + env, err := environment.LoadRoot() if err != nil { panic(err) } switch configName { case "apiEndpoint": - if zrd.Cfg != nil && zrd.Cfg.ApiEndpoint != "" { - fmt.Printf("apiEndpoint = %v\n", zrd.Cfg.ApiEndpoint) + if env.Config() != nil && env.Config().ApiEndpoint != "" { + fmt.Printf("apiEndpoint = %v\n", env.Config().ApiEndpoint) } else { fmt.Println("apiEndpoint = ") } diff --git a/cmd/zrok/configSet.go b/cmd/zrok/configSet.go index a72d29f40..553102e9f 100644 --- a/cmd/zrok/configSet.go +++ b/cmd/zrok/configSet.go @@ -2,12 +2,12 @@ package main import ( "fmt" - "net/url" - "os" - + "github.com/openziti/zrok/environment" + "github.com/openziti/zrok/environment/env_core" "github.com/openziti/zrok/tui" - "github.com/openziti/zrok/zrokdir" "github.com/spf13/cobra" + "net/url" + "os" ) func init() { @@ -33,17 +33,13 @@ func (cmd *configSetCommand) run(_ *cobra.Command, args []string) { configName := args[0] value := args[1] - zrd, err := zrokdir.Load() + env, err := environment.LoadRoot() if err != nil { panic(err) } - modified := false switch configName { case "apiEndpoint": - if zrd.Cfg == nil { - zrd.Cfg = &zrokdir.Config{} - } ok, err := isFullyValidUrl(value) if err != nil { tui.Error("unable to validate api endpoint", err) @@ -51,24 +47,25 @@ func (cmd *configSetCommand) run(_ *cobra.Command, args []string) { if !ok { tui.Error("invalid apiEndpoint; please make sure URL starts with http:// or https://", nil) } - zrd.Cfg.ApiEndpoint = value - modified = true - - default: - fmt.Printf("unknown config name '%v'\n", configName) - os.Exit(1) - } - - if modified { - if err := zrd.Save(); err != nil { - panic(err) + if env.Config() == nil { + if err := env.SetConfig(&env_core.Config{ApiEndpoint: value}); err != nil { + tui.Error("unable to save config", err) + } + } else { + cfg := env.Config() + cfg.ApiEndpoint = value + if err := env.SetConfig(cfg); err != nil { + tui.Error("unable to save config", err) + } } fmt.Println("zrok configuration updated") - if zrd.Env != nil && configName == "apiEndpoint" { + if env.IsEnabled() { fmt.Printf("\n[%v]: because you have a %v-d environment, you won't see your config change until you run %v first!\n\n", tui.WarningLabel, tui.Code.Render("zrok enable"), tui.Code.Render("zrok disable")) } - } else { - fmt.Println("zrok configuration not changed") + + default: + fmt.Printf("unknown config name '%v'\n", configName) + os.Exit(1) } } diff --git a/cmd/zrok/configUnset.go b/cmd/zrok/configUnset.go index 6d27a930d..a7cb6930a 100644 --- a/cmd/zrok/configUnset.go +++ b/cmd/zrok/configUnset.go @@ -2,8 +2,9 @@ package main import ( "fmt" + "github.com/openziti/zrok/environment" + "github.com/openziti/zrok/environment/env_core" "github.com/openziti/zrok/tui" - "github.com/openziti/zrok/zrokdir" "github.com/spf13/cobra" "os" ) @@ -30,33 +31,23 @@ func newConfigUnsetCommand() *configUnsetCommand { func (cmd *configUnsetCommand) run(_ *cobra.Command, args []string) { configName := args[0] - zrd, err := zrokdir.Load() + env, err := environment.LoadRoot() if err != nil { panic(err) } - modified := false switch configName { case "apiEndpoint": - if zrd.Cfg != nil && zrd.Cfg.ApiEndpoint != "" { - zrd.Cfg.ApiEndpoint = "" - modified = true + if err := env.SetConfig(&env_core.Config{}); err != nil { + tui.Error("unable to save config", err) + } + fmt.Println("zrok configuration updated") + if env.IsEnabled() { + fmt.Printf("\n[%v]: because you have a %v-d environment, you won't see your config change until you run %v first!\n\n", tui.WarningLabel, tui.Code.Render("zrok enable"), tui.Code.Render("zrok disable")) } default: fmt.Printf("unknown config name '%v'\n", configName) os.Exit(1) } - - if modified { - if err := zrd.Save(); err != nil { - panic(err) - } - fmt.Println("zrok configuration updated") - if zrd.Env != nil && configName == "apiEndpoint" { - fmt.Printf("\n[%v]: because you have a %v-d environment, you won't see your config change until you run %v first!\n\n", tui.WarningLabel, tui.Code.Render("zrok enable"), tui.Code.Render("zrok disable")) - } - } else { - fmt.Println("zrok configuration not changed") - } } diff --git a/cmd/zrok/console.go b/cmd/zrok/console.go index 1650e6c1b..72de2ce4b 100644 --- a/cmd/zrok/console.go +++ b/cmd/zrok/console.go @@ -2,8 +2,8 @@ package main import ( "fmt" + "github.com/openziti/zrok/environment" "github.com/openziti/zrok/tui" - "github.com/openziti/zrok/zrokdir" "github.com/spf13/cobra" ) @@ -27,12 +27,12 @@ func newConsoleCommand() *consoleCommand { } func (cmd *consoleCommand) run(_ *cobra.Command, _ []string) { - zrd, err := zrokdir.Load() + env, err := environment.LoadRoot() if err != nil { - tui.Error("unable to load zrokdir", err) + tui.Error("unable to load environment", err) } - apiEndpoint, _ := zrd.ApiEndpoint() + apiEndpoint, _ := env.ApiEndpoint() if err := openBrowser(apiEndpoint); err != nil { tui.Error(fmt.Sprintf("unable to open '%v'", apiEndpoint), err) } diff --git a/cmd/zrok/disable.go b/cmd/zrok/disable.go index d1382e8cf..a7e497501 100644 --- a/cmd/zrok/disable.go +++ b/cmd/zrok/disable.go @@ -2,11 +2,11 @@ package main import ( "fmt" - httptransport "github.com/go-openapi/runtime/client" - "github.com/openziti/zrok/rest_client_zrok/environment" + httpTransport "github.com/go-openapi/runtime/client" + "github.com/openziti/zrok/environment" + restEnvironment "github.com/openziti/zrok/rest_client_zrok/environment" "github.com/openziti/zrok/rest_model_zrok" "github.com/openziti/zrok/tui" - "github.com/openziti/zrok/zrokdir" "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -31,41 +31,41 @@ func newDisableCommand() *disableCommand { } func (cmd *disableCommand) run(_ *cobra.Command, _ []string) { - zrd, err := zrokdir.Load() + env, err := environment.LoadRoot() if err != nil { if !panicInstead { - tui.Error("unable to load zrokdir", err) + tui.Error("unable to load environment", err) } panic(err) } - if zrd.Env == nil { + if !env.IsEnabled() { tui.Error("no environment found; nothing to disable!", nil) } - zrok, err := zrd.Client() + zrok, err := env.Client() if err != nil { if !panicInstead { tui.Error("could not create zrok client", err) } panic(err) } - auth := httptransport.APIKeyAuth("X-TOKEN", "header", zrd.Env.Token) - req := environment.NewDisableParams() + auth := httpTransport.APIKeyAuth("X-TOKEN", "header", env.Environment().Token) + req := restEnvironment.NewDisableParams() req.Body = &rest_model_zrok.DisableRequest{ - Identity: zrd.Env.ZId, + Identity: env.Environment().ZitiIdentity, } _, err = zrok.Environment.Disable(req, auth) if err != nil { logrus.Warnf("share cleanup failed (%v); will clean up local environment", err) } - if err := zrokdir.DeleteEnvironment(); err != nil { + if err := env.DeleteEnvironment(); err != nil { if !panicInstead { tui.Error("error removing zrok environment", err) } panic(err) } - if err := zrokdir.DeleteZitiIdentity("backend"); err != nil { + if err := env.DeleteZitiIdentityNamed(env.EnvironmentIdentityName()); err != nil { if !panicInstead { tui.Error("error removing zrok backend identity", err) } diff --git a/cmd/zrok/enable.go b/cmd/zrok/enable.go index d9790f951..fb49533f1 100644 --- a/cmd/zrok/enable.go +++ b/cmd/zrok/enable.go @@ -2,20 +2,20 @@ package main import ( "fmt" - "github.com/sirupsen/logrus" - "os" - user2 "os/user" - "time" - "github.com/charmbracelet/bubbles/spinner" tea "github.com/charmbracelet/bubbletea" httptransport "github.com/go-openapi/runtime/client" - "github.com/openziti/zrok/rest_client_zrok/environment" + "github.com/openziti/zrok/environment" + "github.com/openziti/zrok/environment/env_core" + restEnvironment "github.com/openziti/zrok/rest_client_zrok/environment" "github.com/openziti/zrok/rest_model_zrok" "github.com/openziti/zrok/tui" - "github.com/openziti/zrok/zrokdir" "github.com/shirou/gopsutil/v3/host" + "github.com/sirupsen/logrus" "github.com/spf13/cobra" + "os" + user2 "os/user" + "time" ) func init() { @@ -42,13 +42,13 @@ func newEnableCommand() *enableCommand { } func (cmd *enableCommand) run(_ *cobra.Command, args []string) { - zrd, err := zrokdir.Load() + env, err := environment.LoadRoot() if err != nil { panic(err) } token := args[0] - if zrd.Env != nil { + if env.IsEnabled() { tui.Error(fmt.Sprintf("you already have an enabled environment, %v first before you %v", tui.Code.Render("zrok disable"), tui.Code.Render("zrok enable")), nil) } @@ -64,13 +64,13 @@ func (cmd *enableCommand) run(_ *cobra.Command, args []string) { if cmd.description == "@" { cmd.description = fmt.Sprintf("%v@%v", user.Username, hostName) } - zrok, err := zrd.Client() + zrok, err := env.Client() if err != nil { - cmd.endpointError(zrd.ApiEndpoint()) + cmd.endpointError(env.ApiEndpoint()) tui.Error("error creating service client", err) } auth := httptransport.APIKeyAuth("X-TOKEN", "header", token) - req := environment.NewEnableParams() + req := restEnvironment.NewEnableParams() req.Body = &rest_model_zrok.EnableRequest{ Description: cmd.description, Host: hostDetail, @@ -110,15 +110,14 @@ func (cmd *enableCommand) run(_ *cobra.Command, args []string) { case <-done: case <-time.After(1 * time.Second): } - cmd.endpointError(zrd.ApiEndpoint()) + cmd.endpointError(env.ApiEndpoint()) os.Exit(1) } if err != nil { prg.Send("writing the environment details...") } - apiEndpoint, _ := zrd.ApiEndpoint() - zrd.Env = &zrokdir.Environment{Token: token, ZId: resp.Payload.Identity, ApiEndpoint: apiEndpoint} - if err := zrd.Save(); err != nil { + apiEndpoint, _ := env.ApiEndpoint() + if err := env.SetEnvironment(&env_core.Environment{Token: token, ZitiIdentity: resp.Payload.Identity, ApiEndpoint: apiEndpoint}); err != nil { if !cmd.headless && prg != nil { prg.Send(fmt.Sprintf("there was an error saving the new environment: %v", err)) prg.Quit() @@ -131,7 +130,7 @@ func (cmd *enableCommand) run(_ *cobra.Command, args []string) { } os.Exit(1) } - if err := zrokdir.SaveZitiIdentity("backend", resp.Payload.Cfg); err != nil { + if err := env.SaveZitiIdentityNamed(env.EnvironmentIdentityName(), resp.Payload.Cfg); err != nil { if !cmd.headless && prg != nil { prg.Send(fmt.Sprintf("there was an error writing the environment: %v", err)) prg.Quit() diff --git a/cmd/zrok/invite.go b/cmd/zrok/invite.go index ed33cf26d..0695474f7 100644 --- a/cmd/zrok/invite.go +++ b/cmd/zrok/invite.go @@ -2,19 +2,18 @@ package main import ( "fmt" - "os" - "strings" - "github.com/charmbracelet/bubbles/textinput" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" + "github.com/openziti/zrok/environment" "github.com/openziti/zrok/rest_client_zrok/account" "github.com/openziti/zrok/rest_client_zrok/metadata" "github.com/openziti/zrok/rest_model_zrok" "github.com/openziti/zrok/tui" "github.com/openziti/zrok/util" - "github.com/openziti/zrok/zrokdir" "github.com/spf13/cobra" + "os" + "strings" ) func init() { @@ -42,15 +41,15 @@ func newInviteCommand() *inviteCommand { } func (cmd *inviteCommand) run(_ *cobra.Command, _ []string) { - zrd, err := zrokdir.Load() + env, err := environment.LoadRoot() if err != nil { - tui.Error("error loading zrokdir", err) + tui.Error("error loading environment", err) } - zrok, err := zrd.Client() + zrok, err := env.Client() if err != nil { if !panicInstead { - cmd.endpointError(zrd.ApiEndpoint()) + cmd.endpointError(env.ApiEndpoint()) tui.Error("error creating zrok api client", err) } panic(err) @@ -63,7 +62,7 @@ func (cmd *inviteCommand) run(_ *cobra.Command, _ []string) { if md != nil { if !md.GetPayload().InvitesOpen { - apiEndpoint, _ := zrd.ApiEndpoint() + apiEndpoint, _ := env.ApiEndpoint() tui.Error(fmt.Sprintf("'%v' is not currently accepting new users", apiEndpoint), nil) } cmd.tui.invitesOpen = md.GetPayload().InvitesOpen @@ -86,7 +85,7 @@ func (cmd *inviteCommand) run(_ *cobra.Command, _ []string) { } _, err = zrok.Account.Invite(req) if err != nil { - cmd.endpointError(zrd.ApiEndpoint()) + cmd.endpointError(env.ApiEndpoint()) tui.Error("error creating invitation", err) } diff --git a/cmd/zrok/overview.go b/cmd/zrok/overview.go index 3303e8c3c..329e4a29a 100644 --- a/cmd/zrok/overview.go +++ b/cmd/zrok/overview.go @@ -2,8 +2,8 @@ package main import ( "fmt" + "github.com/openziti/zrok/environment" "github.com/openziti/zrok/tui" - "github.com/openziti/zrok/zrokdir" "github.com/spf13/cobra" "io" "net/http" @@ -29,7 +29,7 @@ func newOverviewCommand() *overviewCommand { } func (cmd *overviewCommand) run(_ *cobra.Command, _ []string) { - zrd, err := zrokdir.Load() + root, err := environment.LoadRoot() if err != nil { if !panicInstead { tui.Error("error loading zrokdir", err) @@ -37,12 +37,12 @@ func (cmd *overviewCommand) run(_ *cobra.Command, _ []string) { panic(err) } - if zrd.Env == nil { + if !root.IsEnabled() { tui.Error("unable to load environment; did you 'zrok enable'?", nil) } client := &http.Client{} - apiEndpoint, _ := zrd.ApiEndpoint() + apiEndpoint, _ := root.ApiEndpoint() req, err := http.NewRequest("GET", fmt.Sprintf("%v/api/v1/overview", apiEndpoint), nil) if err != nil { if !panicInstead { @@ -50,7 +50,7 @@ func (cmd *overviewCommand) run(_ *cobra.Command, _ []string) { } panic(err) } - req.Header.Add("X-TOKEN", zrd.Env.Token) + req.Header.Add("X-TOKEN", root.Environment().Token) resp, err := client.Do(req) if err != nil { if !panicInstead { diff --git a/cmd/zrok/release.go b/cmd/zrok/release.go index 32b5525b0..f68ec68a7 100644 --- a/cmd/zrok/release.go +++ b/cmd/zrok/release.go @@ -2,10 +2,10 @@ package main import ( httptransport "github.com/go-openapi/runtime/client" + "github.com/openziti/zrok/environment" "github.com/openziti/zrok/rest_client_zrok/share" "github.com/openziti/zrok/rest_model_zrok" "github.com/openziti/zrok/tui" - "github.com/openziti/zrok/zrokdir" "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -31,19 +31,19 @@ func newReleaseCommand() *releaseCommand { func (cmd *releaseCommand) run(_ *cobra.Command, args []string) { shrToken := args[0] - zrd, err := zrokdir.Load() + env, err := environment.LoadRoot() if err != nil { if !panicInstead { - tui.Error("unable to load zrokdir", err) + tui.Error("unable to load environment", err) } panic(err) } - if zrd.Env == nil { + if !env.IsEnabled() { tui.Error("unable to load environment; did you 'zrok enable'?", nil) } - zrok, err := zrd.Client() + zrok, err := env.Client() if err != nil { if !panicInstead { tui.Error("unable to create zrok client", err) @@ -51,10 +51,10 @@ func (cmd *releaseCommand) run(_ *cobra.Command, args []string) { panic(err) } - auth := httptransport.APIKeyAuth("X-TOKEN", "header", zrd.Env.Token) + auth := httptransport.APIKeyAuth("X-TOKEN", "header", env.Environment().Token) req := share.NewUnshareParams() req.Body = &rest_model_zrok.UnshareRequest{ - EnvZID: zrd.Env.ZId, + EnvZID: env.Environment().ZitiIdentity, ShrToken: shrToken, Reserved: true, } diff --git a/cmd/zrok/reserve.go b/cmd/zrok/reserve.go index bf21b6648..997bac2fb 100644 --- a/cmd/zrok/reserve.go +++ b/cmd/zrok/reserve.go @@ -2,11 +2,11 @@ package main import ( httptransport "github.com/go-openapi/runtime/client" - "github.com/openziti/zrok/model" + "github.com/openziti/zrok/environment" "github.com/openziti/zrok/rest_client_zrok/share" "github.com/openziti/zrok/rest_model_zrok" + "github.com/openziti/zrok/sdk" "github.com/openziti/zrok/tui" - "github.com/openziti/zrok/zrokdir" "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -39,8 +39,8 @@ func newReserveCommand() *reserveCommand { } func (cmd *reserveCommand) run(_ *cobra.Command, args []string) { - shareMode := args[0] - if shareMode != "public" && shareMode != "private" { + shareMode := sdk.ShareMode(args[0]) + if shareMode != sdk.PublicShareMode && shareMode != sdk.PrivateShareMode { tui.Error("invalid sharing mode; expecting 'public' or 'private'", nil) } @@ -60,41 +60,41 @@ func (cmd *reserveCommand) run(_ *cobra.Command, args []string) { target = args[1] } - zrd, err := zrokdir.Load() + env, err := environment.LoadRoot() if err != nil { if !panicInstead { - tui.Error("error loading zrokdir", err) + tui.Error("error loading environment", err) } panic(err) } - if zrd.Env == nil { + if !env.IsEnabled() { tui.Error("unable to load environment; did you 'zrok enable'?", nil) } - zrok, err := zrd.Client() + zrok, err := env.Client() if err != nil { if !panicInstead { tui.Error("unable to create zrok client", err) } panic(err) } - auth := httptransport.APIKeyAuth("X-TOKEN", "header", zrd.Env.Token) + auth := httptransport.APIKeyAuth("X-TOKEN", "header", env.Environment().Token) req := share.NewShareParams() req.Body = &rest_model_zrok.ShareRequest{ - EnvZID: zrd.Env.ZId, - ShareMode: shareMode, + EnvZID: env.Environment().ZitiIdentity, + ShareMode: string(shareMode), BackendMode: cmd.backendMode, BackendProxyEndpoint: target, - AuthScheme: string(model.None), + AuthScheme: string(sdk.None), Reserved: true, } - if shareMode == "public" { + if shareMode == sdk.PublicShareMode { req.Body.FrontendSelection = cmd.frontendSelection } if len(cmd.basicAuth) > 0 { logrus.Infof("configuring basic auth") - req.Body.AuthScheme = string(model.Basic) + req.Body.AuthScheme = string(sdk.Basic) for _, pair := range cmd.basicAuth { tokens := strings.Split(pair, ":") if len(tokens) == 2 { diff --git a/cmd/zrok/sharePrivate.go b/cmd/zrok/sharePrivate.go index 049d91a52..4b7f908e9 100644 --- a/cmd/zrok/sharePrivate.go +++ b/cmd/zrok/sharePrivate.go @@ -9,12 +9,12 @@ import ( "github.com/openziti/zrok/endpoints/proxy" "github.com/openziti/zrok/endpoints/tcpTunnel" "github.com/openziti/zrok/endpoints/udpTunnel" - "github.com/openziti/zrok/model" + "github.com/openziti/zrok/environment" "github.com/openziti/zrok/rest_client_zrok" "github.com/openziti/zrok/rest_client_zrok/share" "github.com/openziti/zrok/rest_model_zrok" + "github.com/openziti/zrok/sdk" "github.com/openziti/zrok/tui" - "github.com/openziti/zrok/zrokdir" "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -78,19 +78,19 @@ func (cmd *sharePrivateCommand) run(_ *cobra.Command, args []string) { tui.Error(fmt.Sprintf("invalid backend mode '%v'; expected {proxy, web, tcpTunnel}", cmd.backendMode), nil) } - zrd, err := zrokdir.Load() + env, err := environment.LoadRoot() if err != nil { if !panicInstead { - tui.Error("unable to load zrokdir", err) + tui.Error("unable to load environment", err) } panic(err) } - if zrd.Env == nil { + if !env.IsEnabled() { tui.Error("unable to load environment; did you 'zrok enable'?", nil) } - zif, err := zrokdir.ZitiIdentityFile("backend") + zif, err := env.ZitiIdentityNamed(env.EnvironmentIdentityName()) if err != nil { if !panicInstead { tui.Error("unable to load ziti identity configuration", err) @@ -98,7 +98,7 @@ func (cmd *sharePrivateCommand) run(_ *cobra.Command, args []string) { panic(err) } - zrok, err := zrd.Client() + zrok, err := env.Client() if err != nil { if !panicInstead { tui.Error("unable to create zrok client", err) @@ -106,18 +106,18 @@ func (cmd *sharePrivateCommand) run(_ *cobra.Command, args []string) { panic(err) } - auth := httptransport.APIKeyAuth("X-TOKEN", "header", zrd.Env.Token) + auth := httptransport.APIKeyAuth("X-TOKEN", "header", env.Environment().Token) req := share.NewShareParams() req.Body = &rest_model_zrok.ShareRequest{ - EnvZID: zrd.Env.ZId, - ShareMode: "private", + EnvZID: env.Environment().ZitiIdentity, + ShareMode: string(sdk.PrivateShareMode), BackendMode: cmd.backendMode, BackendProxyEndpoint: target, - AuthScheme: string(model.None), + AuthScheme: string(sdk.None), } if len(cmd.basicAuth) > 0 { logrus.Infof("configuring basic auth") - req.Body.AuthScheme = string(model.Basic) + req.Body.AuthScheme = string(sdk.Basic) for _, pair := range cmd.basicAuth { tokens := strings.Split(pair, ":") if len(tokens) == 2 { @@ -139,7 +139,7 @@ func (cmd *sharePrivateCommand) run(_ *cobra.Command, args []string) { signal.Notify(c, os.Interrupt, syscall.SIGTERM) go func() { <-c - cmd.destroy(zrd.Env.ZId, resp.Payload.ShrToken, zrok, auth) + cmd.destroy(env.Environment().ZitiIdentity, resp.Payload.ShrToken, zrok, auth) os.Exit(0) }() @@ -231,7 +231,7 @@ func (cmd *sharePrivateCommand) run(_ *cobra.Command, args []string) { } else { shareDescription := fmt.Sprintf("access your share with: %v", tui.Code.Render(fmt.Sprintf("zrok access private %v", resp.Payload.ShrToken))) - mdl := newShareModel(resp.Payload.ShrToken, []string{shareDescription}, "private", cmd.backendMode) + mdl := newShareModel(resp.Payload.ShrToken, []string{shareDescription}, sdk.PrivateShareMode, sdk.BackendMode(cmd.backendMode)) logrus.SetOutput(mdl) prg := tea.NewProgram(mdl, tea.WithAltScreen()) mdl.prg = prg @@ -250,7 +250,7 @@ func (cmd *sharePrivateCommand) run(_ *cobra.Command, args []string) { } close(requestsChan) - cmd.destroy(zrd.Env.ZId, resp.Payload.ShrToken, zrok, auth) + cmd.destroy(env.Environment().ZitiIdentity, resp.Payload.ShrToken, zrok, auth) } } diff --git a/cmd/zrok/sharePublic.go b/cmd/zrok/sharePublic.go index 336c51b7b..8f572143f 100644 --- a/cmd/zrok/sharePublic.go +++ b/cmd/zrok/sharePublic.go @@ -7,12 +7,12 @@ import ( httptransport "github.com/go-openapi/runtime/client" "github.com/openziti/zrok/endpoints" "github.com/openziti/zrok/endpoints/proxy" - "github.com/openziti/zrok/model" + "github.com/openziti/zrok/environment" "github.com/openziti/zrok/rest_client_zrok" "github.com/openziti/zrok/rest_client_zrok/share" "github.com/openziti/zrok/rest_model_zrok" + "github.com/openziti/zrok/sdk" "github.com/openziti/zrok/tui" - "github.com/openziti/zrok/zrokdir" "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -72,19 +72,19 @@ func (cmd *sharePublicCommand) run(_ *cobra.Command, args []string) { tui.Error(fmt.Sprintf("invalid backend mode '%v'; expected {proxy, web}", cmd.backendMode), nil) } - zrd, err := zrokdir.Load() + env, err := environment.LoadRoot() if err != nil { if !panicInstead { - tui.Error("unable to load zrokdir", err) + tui.Error("unable to load environment", err) } panic(err) } - if zrd.Env == nil { + if !env.IsEnabled() { tui.Error("unable to load environment; did you 'zrok enable'?", nil) } - zif, err := zrokdir.ZitiIdentityFile("backend") + zif, err := env.ZitiIdentityNamed(env.EnvironmentIdentityName()) if err != nil { if !panicInstead { tui.Error("unable to load ziti identity configuration", err) @@ -92,7 +92,7 @@ func (cmd *sharePublicCommand) run(_ *cobra.Command, args []string) { panic(err) } - zrok, err := zrd.Client() + zrok, err := env.Client() if err != nil { if !panicInstead { tui.Error("unable to create zrok client", err) @@ -100,19 +100,19 @@ func (cmd *sharePublicCommand) run(_ *cobra.Command, args []string) { panic(err) } - auth := httptransport.APIKeyAuth("X-TOKEN", "header", zrd.Env.Token) + auth := httptransport.APIKeyAuth("X-TOKEN", "header", env.Environment().Token) req := share.NewShareParams() req.Body = &rest_model_zrok.ShareRequest{ - EnvZID: zrd.Env.ZId, - ShareMode: "public", + EnvZID: env.Environment().ZitiIdentity, + ShareMode: string(sdk.PublicShareMode), FrontendSelection: cmd.frontendSelection, BackendMode: cmd.backendMode, BackendProxyEndpoint: target, - AuthScheme: string(model.None), + AuthScheme: string(sdk.None), } if len(cmd.basicAuth) > 0 { logrus.Infof("configuring basic auth") - req.Body.AuthScheme = string(model.Basic) + req.Body.AuthScheme = string(sdk.Basic) for _, pair := range cmd.basicAuth { tokens := strings.Split(pair, ":") if len(tokens) == 2 { @@ -134,7 +134,7 @@ func (cmd *sharePublicCommand) run(_ *cobra.Command, args []string) { signal.Notify(c, os.Interrupt, syscall.SIGTERM) go func() { <-c - cmd.destroy(zrd.Env.ZId, resp.Payload.ShrToken, zrok, auth) + cmd.destroy(env.Environment().ZitiIdentity, resp.Payload.ShrToken, zrok, auth) os.Exit(0) }() @@ -185,7 +185,7 @@ func (cmd *sharePublicCommand) run(_ *cobra.Command, args []string) { } } else { - mdl := newShareModel(resp.Payload.ShrToken, resp.Payload.FrontendProxyEndpoints, "public", cmd.backendMode) + mdl := newShareModel(resp.Payload.ShrToken, resp.Payload.FrontendProxyEndpoints, sdk.PublicShareMode, sdk.BackendMode(cmd.backendMode)) logrus.SetOutput(mdl) prg := tea.NewProgram(mdl, tea.WithAltScreen()) mdl.prg = prg @@ -204,7 +204,7 @@ func (cmd *sharePublicCommand) run(_ *cobra.Command, args []string) { } close(requestsChan) - cmd.destroy(zrd.Env.ZId, resp.Payload.ShrToken, zrok, auth) + cmd.destroy(env.Environment().ZitiIdentity, resp.Payload.ShrToken, zrok, auth) } } diff --git a/cmd/zrok/shareReserved.go b/cmd/zrok/shareReserved.go index dafeeb569..f5dd54ce7 100644 --- a/cmd/zrok/shareReserved.go +++ b/cmd/zrok/shareReserved.go @@ -6,11 +6,12 @@ import ( httptransport "github.com/go-openapi/runtime/client" "github.com/openziti/zrok/endpoints" "github.com/openziti/zrok/endpoints/proxy" + "github.com/openziti/zrok/environment" "github.com/openziti/zrok/rest_client_zrok/metadata" "github.com/openziti/zrok/rest_client_zrok/share" "github.com/openziti/zrok/rest_model_zrok" + "github.com/openziti/zrok/sdk" "github.com/openziti/zrok/tui" - "github.com/openziti/zrok/zrokdir" "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -44,26 +45,26 @@ func (cmd *shareReservedCommand) run(_ *cobra.Command, args []string) { shrToken := args[0] var target string - zrd, err := zrokdir.Load() + env, err := environment.LoadRoot() if err != nil { if !panicInstead { - tui.Error("error loading zrokdir", err) + tui.Error("error loading environment", err) } panic(err) } - if zrd.Env == nil { + if !env.IsEnabled() { tui.Error("unable to load environment; did you 'zrok enable'?", nil) } - zrok, err := zrd.Client() + zrok, err := env.Client() if err != nil { if !panicInstead { tui.Error("unable to create zrok client", err) } panic(err) } - auth := httptransport.APIKeyAuth("X-TOKEN", "header", zrd.Env.Token) + auth := httptransport.APIKeyAuth("X-TOKEN", "header", env.Environment().Token) req := metadata.NewGetShareDetailParams() req.ShrToken = shrToken resp, err := zrok.Metadata.GetShareDetail(req, auth) @@ -78,7 +79,7 @@ func (cmd *shareReservedCommand) run(_ *cobra.Command, args []string) { target = resp.Payload.BackendProxyEndpoint } - zif, err := zrokdir.ZitiIdentityFile("backend") + zif, err := env.ZitiIdentityNamed(env.EnvironmentIdentityName()) if err != nil { if !panicInstead { tui.Error("unable to load ziti identity configuration", err) @@ -144,10 +145,10 @@ func (cmd *shareReservedCommand) run(_ *cobra.Command, args []string) { if cmd.headless { switch resp.Payload.ShareMode { - case "public": + case string(sdk.PublicShareMode): logrus.Infof("access your zrok share: %v", resp.Payload.FrontendEndpoint) - case "private": + case string(sdk.PrivateShareMode): logrus.Infof("use this command to access your zrok share: 'zrok access private %v'", shrToken) } for { @@ -159,13 +160,13 @@ func (cmd *shareReservedCommand) run(_ *cobra.Command, args []string) { } else { var shareDescription string switch resp.Payload.ShareMode { - case "public": + case string(sdk.PublicShareMode): shareDescription = resp.Payload.FrontendEndpoint - case "private": + case string(sdk.PrivateShareMode): shareDescription = fmt.Sprintf("access your share with: %v", tui.Code.Render(fmt.Sprintf("zrok access private %v", shrToken))) } - mdl := newShareModel(shrToken, []string{shareDescription}, resp.Payload.ShareMode, resp.Payload.BackendMode) + mdl := newShareModel(shrToken, []string{shareDescription}, sdk.ShareMode(resp.Payload.ShareMode), sdk.BackendMode(resp.Payload.BackendMode)) logrus.SetOutput(mdl) prg := tea.NewProgram(mdl, tea.WithAltScreen()) mdl.prg = prg diff --git a/cmd/zrok/shareTui.go b/cmd/zrok/shareTui.go index 123b88688..1b709fd5a 100644 --- a/cmd/zrok/shareTui.go +++ b/cmd/zrok/shareTui.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "github.com/openziti/zrok/sdk" "strings" "time" @@ -19,8 +20,8 @@ var wordwrapBreakpoints = map[rune]bool{' ': true, '-': true} type shareModel struct { shrToken string frontendDescriptions []string - shareMode string - backendMode string + shareMode sdk.ShareMode + backendMode sdk.BackendMode requests []*endpoints.Request log []string showLog bool @@ -32,7 +33,7 @@ type shareModel struct { type shareLogLine string -func newShareModel(shrToken string, frontendEndpoints []string, shareMode, backendMode string) *shareModel { +func newShareModel(shrToken string, frontendEndpoints []string, shareMode sdk.ShareMode, backendMode sdk.BackendMode) *shareModel { return &shareModel{ shrToken: shrToken, frontendDescriptions: frontendEndpoints, @@ -116,15 +117,15 @@ func (m *shareModel) adjustPaneHeights() { func (m *shareModel) renderConfig() string { out := "[" if m.shareMode == "public" { - out += shareModePublicStyle.Render(strings.ToUpper(m.shareMode)) + out += shareModePublicStyle.Render(strings.ToUpper(string(m.shareMode))) } else { - out += shareModePrivateStyle.Render(strings.ToUpper(m.shareMode)) + out += shareModePrivateStyle.Render(strings.ToUpper(string(m.shareMode))) } out += "] [" if m.backendMode == "proxy" { - out += backendModeProxyStyle.Render(strings.ToUpper(m.backendMode)) + out += backendModeProxyStyle.Render(strings.ToUpper(string(m.backendMode))) } else { - out += backendModeWebStyle.Render(strings.ToUpper(m.backendMode)) + out += backendModeWebStyle.Render(strings.ToUpper(string(m.backendMode))) } out += "]" return out diff --git a/cmd/zrok/status.go b/cmd/zrok/status.go index 3cdccb8be..fe6d00beb 100644 --- a/cmd/zrok/status.go +++ b/cmd/zrok/status.go @@ -3,8 +3,8 @@ package main import ( "fmt" "github.com/jedib0t/go-pretty/v6/table" + "github.com/openziti/zrok/environment" "github.com/openziti/zrok/tui" - "github.com/openziti/zrok/zrokdir" "github.com/spf13/cobra" "os" ) @@ -34,9 +34,13 @@ func newStatusCommand() *statusCommand { func (cmd *statusCommand) run(_ *cobra.Command, _ []string) { _, _ = fmt.Fprintf(os.Stderr, "\n") - zrd, err := zrokdir.Load() + env, err := environment.LoadRoot() if err != nil { - tui.Error("unable to load zrokdir", err) + tui.Error("error loading environment", err) + } + + if !environment.IsLatest(env) { + tui.Warning(fmt.Sprintf("Your environment is out of date ('%v'), use '%v' to update (make a backup before updating!)\n", env.Metadata().V, tui.Code.Render("zrok update"))) } _, _ = fmt.Fprintf(os.Stdout, tui.Code.Render("Config")+":\n\n") @@ -44,13 +48,12 @@ func (cmd *statusCommand) run(_ *cobra.Command, _ []string) { t.SetOutputMirror(os.Stdout) t.SetStyle(table.StyleColoredDark) t.AppendHeader(table.Row{"Config", "Value", "Source"}) - apiEndpoint, from := zrd.ApiEndpoint() + apiEndpoint, from := env.ApiEndpoint() t.AppendRow(table.Row{"apiEndpoint", apiEndpoint, from}) t.Render() _, _ = fmt.Fprintf(os.Stderr, "\n") - if zrd.Env == nil { - tui.Warning("Unable to load your local environment!\n") + if !env.IsEnabled() { _, _ = fmt.Fprintf(os.Stderr, "To create a local environment use the %v command.\n", tui.Code.Render("zrok enable")) } else { _, _ = fmt.Fprintf(os.Stdout, tui.Code.Render("Environment")+":\n\n") @@ -60,17 +63,17 @@ func (cmd *statusCommand) run(_ *cobra.Command, _ []string) { t.SetStyle(table.StyleColoredDark) t.AppendHeader(table.Row{"Property", "Value"}) if cmd.secrets { - t.AppendRow(table.Row{"Secret Token", zrd.Env.Token}) - t.AppendRow(table.Row{"Ziti Identity", zrd.Env.ZId}) + t.AppendRow(table.Row{"Secret Token", env.Environment().Token}) + t.AppendRow(table.Row{"Ziti Identity", env.Environment().ZitiIdentity}) } else { secretToken := "<>" - if zrd.Env.Token == "" { + if env.Environment().Token == "" { secretToken = "<>" } t.AppendRow(table.Row{"Secret Token", secretToken}) zId := "<>" - if zrd.Env.ZId == "" { + if env.Environment().ZitiIdentity == "" { zId = "<>" } t.AppendRow(table.Row{"Ziti Identity", zId}) diff --git a/cmd/zrok/testLoopPublic.go b/cmd/zrok/testLoopPublic.go index 7f7da5503..f6a0d7267 100644 --- a/cmd/zrok/testLoopPublic.go +++ b/cmd/zrok/testLoopPublic.go @@ -8,13 +8,14 @@ import ( httptransport "github.com/go-openapi/runtime/client" "github.com/openziti/sdk-golang/ziti" "github.com/openziti/sdk-golang/ziti/edge" - "github.com/openziti/zrok/model" + "github.com/openziti/zrok/environment" + "github.com/openziti/zrok/environment/env_core" "github.com/openziti/zrok/rest_client_zrok" "github.com/openziti/zrok/rest_client_zrok/share" "github.com/openziti/zrok/rest_model_zrok" + "github.com/openziti/zrok/sdk" "github.com/openziti/zrok/tui" "github.com/openziti/zrok/util" - "github.com/openziti/zrok/zrokdir" "github.com/sirupsen/logrus" "github.com/spf13/cobra" "io" @@ -105,7 +106,7 @@ func (cmd *testLoopPublicCommand) run(_ *cobra.Command, _ []string) { type looper struct { id int cmd *testLoopPublicCommand - env *zrokdir.Environment + env *env_core.Environment done chan struct{} listener edge.Listener zif string @@ -175,33 +176,33 @@ func (l *looper) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (l *looper) startup() { logrus.Infof("starting #%d", l.id) - zrd, err := zrokdir.Load() + env, err := environment.LoadRoot() if err != nil { panic(err) } - if zrd.Env == nil { + if !env.IsEnabled() { tui.Error("unable to load environment; did you 'zrok enable'?", nil) } - l.env = zrd.Env + l.env = env.Environment() - l.zif, err = zrokdir.ZitiIdentityFile("backend") + l.zif, err = env.ZitiIdentityNamed(env.EnvironmentIdentityName()) if err != nil { panic(err) } - l.zrok, err = zrd.Client() + l.zrok, err = env.Client() if err != nil { panic(err) } l.auth = httptransport.APIKeyAuth("x-token", "header", l.env.Token) tunnelReq := share.NewShareParams() tunnelReq.Body = &rest_model_zrok.ShareRequest{ - EnvZID: l.env.ZId, - ShareMode: "public", + EnvZID: l.env.ZitiIdentity, + ShareMode: string(sdk.PublicShareMode), FrontendSelection: l.cmd.frontendSelection, - BackendMode: "proxy", + BackendMode: string(sdk.ProxyBackendMode), BackendProxyEndpoint: fmt.Sprintf("looper#%d", l.id), - AuthScheme: string(model.None), + AuthScheme: string(sdk.None), } tunnelReq.SetTimeout(60 * time.Second) tunnelResp, err := l.zrok.Share.Share(tunnelReq, l.auth) @@ -272,7 +273,7 @@ func (l *looper) shutdown() { untunnelReq := share.NewUnshareParams() untunnelReq.Body = &rest_model_zrok.UnshareRequest{ - EnvZID: l.env.ZId, + EnvZID: l.env.ZitiIdentity, ShrToken: l.shrToken, } if _, err := l.zrok.Share.Unshare(untunnelReq, l.auth); err != nil { diff --git a/cmd/zrok/update.go b/cmd/zrok/update.go new file mode 100644 index 000000000..da4df341d --- /dev/null +++ b/cmd/zrok/update.go @@ -0,0 +1,52 @@ +package main + +import ( + "fmt" + "github.com/openziti/zrok/environment" + "github.com/openziti/zrok/tui" + "github.com/spf13/cobra" +) + +func init() { + rootCmd.AddCommand(newUpdateCommand().cmd) +} + +type updateCommand struct { + cmd *cobra.Command +} + +func newUpdateCommand() *updateCommand { + cmd := &cobra.Command{ + Use: "update", + Short: "Update your environment to the latest version", + Args: cobra.NoArgs, + } + command := &updateCommand{cmd: cmd} + cmd.Run = command.run + return command +} + +func (cmd *updateCommand) run(_ *cobra.Command, _ []string) { + r, err := environment.LoadRoot() + if err != nil { + if !panicInstead { + tui.Error("unable to load environment", err) + } + panic(err) + } + + if environment.IsLatest(r) { + fmt.Printf("zrok environment is already the latest version at '%v'\n", r.Metadata().V) + return + } + + newR, err := environment.UpdateRoot(r) + if err != nil { + if !panicInstead { + tui.Error("unable to update environment", err) + } + panic(err) + } + + fmt.Printf("environment updated to '%v'\n", newR.Metadata().V) +} diff --git a/controller/bootstrap.go b/controller/bootstrap.go index 084c51b14..041b722b4 100644 --- a/controller/bootstrap.go +++ b/controller/bootstrap.go @@ -6,22 +6,22 @@ import ( "encoding/json" "fmt" "github.com/openziti/edge-api/rest_management_api_client" - "github.com/openziti/edge-api/rest_management_api_client/config" + restMgmtEdgeConfig "github.com/openziti/edge-api/rest_management_api_client/config" "github.com/openziti/edge-api/rest_management_api_client/edge_router_policy" "github.com/openziti/edge-api/rest_management_api_client/identity" - rest_model_edge "github.com/openziti/edge-api/rest_model" + restModelEdge "github.com/openziti/edge-api/rest_model" "github.com/openziti/sdk-golang/ziti" - zrok_config "github.com/openziti/zrok/controller/config" + "github.com/openziti/zrok/controller/config" "github.com/openziti/zrok/controller/store" "github.com/openziti/zrok/controller/zrokEdgeSdk" - "github.com/openziti/zrok/model" - "github.com/openziti/zrok/zrokdir" + "github.com/openziti/zrok/environment" + "github.com/openziti/zrok/sdk" "github.com/pkg/errors" "github.com/sirupsen/logrus" "time" ) -func Bootstrap(skipCtrl, skipFrontend bool, inCfg *zrok_config.Config) error { +func Bootstrap(skipFrontend bool, inCfg *config.Config) error { cfg = inCfg if v, err := store.Open(cfg.Store); err == nil { @@ -36,34 +36,19 @@ func Bootstrap(skipCtrl, skipFrontend bool, inCfg *zrok_config.Config) error { return errors.Wrap(err, "error connecting to the ziti edge management api") } - var ctrlZId string - if !skipCtrl { - logrus.Info("creating identity for controller ziti access") - - if ctrlZId, err = getIdentityId("ctrl"); err == nil { - logrus.Infof("controller identity: %v", ctrlZId) - } else { - ctrlZId, err = bootstrapIdentity("ctrl", edge) - if err != nil { - panic(err) - } - } - if err := assertIdentity(ctrlZId, edge); err != nil { - panic(err) - } - if err := assertErpForIdentity("ctrl", ctrlZId, edge); err != nil { - panic(err) - } + env, err := environment.LoadRoot() + if err != nil { + return err } var frontendZId string if !skipFrontend { - logrus.Info("creating identity for frontend ziti access") + logrus.Info("creating identity for public frontend access") - if frontendZId, err = getIdentityId("frontend"); err == nil { + if frontendZId, err = getIdentityId(env.PublicIdentityName()); err == nil { logrus.Infof("frontend identity: %v", frontendZId) } else { - frontendZId, err = bootstrapIdentity("frontend", edge) + frontendZId, err = bootstrapIdentity(env.PublicIdentityName(), edge) if err != nil { panic(err) } @@ -71,7 +56,7 @@ func Bootstrap(skipCtrl, skipFrontend bool, inCfg *zrok_config.Config) error { if err := assertIdentity(frontendZId, edge); err != nil { panic(err) } - if err := assertErpForIdentity("frontend", frontendZId, edge); err != nil { + if err := assertErpForIdentity(env.PublicIdentityName(), frontendZId, edge); err != nil { panic(err) } @@ -100,10 +85,10 @@ func Bootstrap(skipCtrl, skipFrontend bool, inCfg *zrok_config.Config) error { } func assertZrokProxyConfigType(edge *rest_management_api_client.ZitiEdgeManagement) error { - filter := fmt.Sprintf("name=\"%v\"", model.ZrokProxyConfig) + filter := fmt.Sprintf("name=\"%v\"", sdk.ZrokProxyConfig) limit := int64(100) offset := int64(0) - listReq := &config.ListConfigTypesParams{ + listReq := &restMgmtEdgeConfig.ListConfigTypesParams{ Filter: &filter, Limit: &limit, Offset: &offset, @@ -115,27 +100,31 @@ func assertZrokProxyConfigType(edge *rest_management_api_client.ZitiEdgeManageme return err } if len(listResp.Payload.Data) < 1 { - name := model.ZrokProxyConfig - ct := &rest_model_edge.ConfigTypeCreate{Name: &name} - createReq := &config.CreateConfigTypeParams{ConfigType: ct} + name := sdk.ZrokProxyConfig + ct := &restModelEdge.ConfigTypeCreate{Name: &name} + createReq := &restMgmtEdgeConfig.CreateConfigTypeParams{ConfigType: ct} createReq.SetTimeout(30 * time.Second) createResp, err := edge.Config.CreateConfigType(createReq, nil) if err != nil { return err } - logrus.Infof("created '%v' config type with id '%v'", model.ZrokProxyConfig, createResp.Payload.Data.ID) + logrus.Infof("created '%v' config type with id '%v'", sdk.ZrokProxyConfig, createResp.Payload.Data.ID) } else if len(listResp.Payload.Data) > 1 { - return errors.Errorf("found %d '%v' config types; expected 0 or 1", len(listResp.Payload.Data), model.ZrokProxyConfig) + return errors.Errorf("found %d '%v' config types; expected 0 or 1", len(listResp.Payload.Data), sdk.ZrokProxyConfig) } else { - logrus.Infof("found '%v' config type with id '%v'", model.ZrokProxyConfig, *(listResp.Payload.Data[0].ID)) + logrus.Infof("found '%v' config type with id '%v'", sdk.ZrokProxyConfig, *(listResp.Payload.Data[0].ID)) } return nil } func getIdentityId(identityName string) (string, error) { - zif, err := zrokdir.ZitiIdentityFile(identityName) + env, err := environment.LoadRoot() + if err != nil { + return "", errors.Wrap(err, "error opening environment root") + } + zif, err := env.ZitiIdentityNamed(identityName) if err != nil { - return "", errors.Wrapf(err, "error opening identity '%v' from zrokdir", identityName) + return "", errors.Wrapf(err, "error opening identity '%v' from environment", identityName) } zcfg, err := ziti.NewConfigFromFile(zif) if err != nil { @@ -177,7 +166,12 @@ func assertIdentity(zId string, edge *rest_management_api_client.ZitiEdgeManagem } func bootstrapIdentity(name string, edge *rest_management_api_client.ZitiEdgeManagement) (string, error) { - idc, err := zrokEdgeSdk.CreateIdentity(name, rest_model_edge.IdentityTypeDevice, nil, edge) + env, err := environment.LoadRoot() + if err != nil { + return "", errors.Wrap(err, "error loading environment root") + } + + idc, err := zrokEdgeSdk.CreateIdentity(name, restModelEdge.IdentityTypeDevice, nil, edge) if err != nil { return "", errors.Wrapf(err, "error creating '%v' identity", name) } @@ -195,7 +189,7 @@ func bootstrapIdentity(name string, edge *rest_management_api_client.ZitiEdgeMan if err != nil { return "", errors.Wrapf(err, "error encoding identity config '%v'", name) } - if err := zrokdir.SaveZitiIdentity(name, out.String()); err != nil { + if err := env.SaveZitiIdentityNamed(name, out.String()); err != nil { return "", errors.Wrapf(err, "error saving identity config '%v'", name) } return zId, nil diff --git a/controller/limits/accountRelaxAction.go b/controller/limits/accountRelaxAction.go index 82e66476a..7339b04ee 100644 --- a/controller/limits/accountRelaxAction.go +++ b/controller/limits/accountRelaxAction.go @@ -4,6 +4,7 @@ import ( "github.com/jmoiron/sqlx" "github.com/openziti/zrok/controller/store" "github.com/openziti/zrok/controller/zrokEdgeSdk" + "github.com/openziti/zrok/sdk" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -38,11 +39,11 @@ func (a *accountRelaxAction) HandleAccount(acct *store.Account, _, _ int64, _ *B for _, shr := range shrs { switch shr.ShareMode { - case "public": + case string(sdk.PublicShareMode): if err := relaxPublicShare(a.str, edge, shr, trx); err != nil { return errors.Wrap(err, "error relaxing public share") } - case "private": + case string(sdk.PrivateShareMode): if err := relaxPrivateShare(a.str, edge, shr, trx); err != nil { return errors.Wrap(err, "error relaxing private share") } diff --git a/controller/limits/environmentRelaxAction.go b/controller/limits/environmentRelaxAction.go index 19d8a115e..e4e7b657f 100644 --- a/controller/limits/environmentRelaxAction.go +++ b/controller/limits/environmentRelaxAction.go @@ -4,6 +4,7 @@ import ( "github.com/jmoiron/sqlx" "github.com/openziti/zrok/controller/store" "github.com/openziti/zrok/controller/zrokEdgeSdk" + "github.com/openziti/zrok/sdk" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -33,11 +34,11 @@ func (a *environmentRelaxAction) HandleEnvironment(env *store.Environment, rxByt for _, shr := range shrs { if !shr.Deleted { switch shr.ShareMode { - case "public": + case string(sdk.PublicShareMode): if err := relaxPublicShare(a.str, edge, shr, trx); err != nil { return err } - case "private": + case string(sdk.PrivateShareMode): if err := relaxPrivateShare(a.str, edge, shr, trx); err != nil { return err } diff --git a/controller/limits/shareRelaxAction.go b/controller/limits/shareRelaxAction.go index 55e0a2c9f..f90dd41c3 100644 --- a/controller/limits/shareRelaxAction.go +++ b/controller/limits/shareRelaxAction.go @@ -5,6 +5,7 @@ import ( "github.com/openziti/edge-api/rest_management_api_client" "github.com/openziti/zrok/controller/store" "github.com/openziti/zrok/controller/zrokEdgeSdk" + "github.com/openziti/zrok/sdk" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -28,11 +29,11 @@ func (a *shareRelaxAction) HandleShare(shr *store.Share, _, _ int64, _ *Bandwidt } switch shr.ShareMode { - case "public": + case string(sdk.PublicShareMode): if err := relaxPublicShare(a.str, edge, shr, trx); err != nil { return err } - case "private": + case string(sdk.PrivateShareMode): if err := relaxPrivateShare(a.str, edge, shr, trx); err != nil { return err } diff --git a/controller/share.go b/controller/share.go index 0471f59e8..860c106b3 100644 --- a/controller/share.go +++ b/controller/share.go @@ -7,6 +7,7 @@ import ( "github.com/openziti/zrok/controller/zrokEdgeSdk" "github.com/openziti/zrok/rest_model_zrok" "github.com/openziti/zrok/rest_server_zrok/operations/share" + "github.com/openziti/zrok/sdk" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -68,7 +69,7 @@ func (h *shareHandler) Handle(params share.ShareParams, principal *rest_model_zr var shrZId string var frontendEndpoints []string switch params.Body.ShareMode { - case "public": + case string(sdk.PublicShareMode): if len(params.Body.FrontendSelection) < 1 { logrus.Info("no frontend selection provided") return share.NewShareNotFound() @@ -94,7 +95,7 @@ func (h *shareHandler) Handle(params share.ShareParams, principal *rest_model_zr return share.NewShareInternalServerError() } - case "private": + case string(sdk.PrivateShareMode): logrus.Info("doing private") shrZId, frontendEndpoints, err = newPrivateResourceAllocator().allocate(envZId, shrToken, params, edge) if err != nil { @@ -123,7 +124,7 @@ func (h *shareHandler) Handle(params share.ShareParams, principal *rest_model_zr } if len(frontendEndpoints) > 0 { sshr.FrontendEndpoint = &frontendEndpoints[0] - } else if sshr.ShareMode == "private" { + } else if sshr.ShareMode == string(sdk.PrivateShareMode) { sshr.FrontendEndpoint = &sshr.ShareMode } diff --git a/controller/sharePrivate.go b/controller/sharePrivate.go index 52853515e..96830cdb5 100644 --- a/controller/sharePrivate.go +++ b/controller/sharePrivate.go @@ -3,8 +3,8 @@ package controller import ( "github.com/openziti/edge-api/rest_management_api_client" "github.com/openziti/zrok/controller/zrokEdgeSdk" - "github.com/openziti/zrok/model" "github.com/openziti/zrok/rest_server_zrok/operations/share" + "github.com/openziti/zrok/sdk" ) type privateResourceAllocator struct{} @@ -14,9 +14,9 @@ func newPrivateResourceAllocator() *privateResourceAllocator { } func (a *privateResourceAllocator) allocate(envZId, shrToken string, params share.ShareParams, edge *rest_management_api_client.ZitiEdgeManagement) (shrZId string, frontendEndpoints []string, err error) { - var authUsers []*model.AuthUser + var authUsers []*sdk.AuthUser for _, authUser := range params.Body.AuthUsers { - authUsers = append(authUsers, &model.AuthUser{authUser.Username, authUser.Password}) + authUsers = append(authUsers, &sdk.AuthUser{authUser.Username, authUser.Password}) } cfgZId, err := zrokEdgeSdk.CreateConfig(zrokProxyConfigId, envZId, shrToken, params.Body.AuthScheme, authUsers, edge) if err != nil { diff --git a/controller/sharePublic.go b/controller/sharePublic.go index cb63ace60..b7522b4c5 100644 --- a/controller/sharePublic.go +++ b/controller/sharePublic.go @@ -3,8 +3,8 @@ package controller import ( "github.com/openziti/edge-api/rest_management_api_client" "github.com/openziti/zrok/controller/zrokEdgeSdk" - "github.com/openziti/zrok/model" "github.com/openziti/zrok/rest_server_zrok/operations/share" + "github.com/openziti/zrok/sdk" ) type publicResourceAllocator struct{} @@ -14,9 +14,9 @@ func newPublicResourceAllocator() *publicResourceAllocator { } func (a *publicResourceAllocator) allocate(envZId, shrToken string, frontendZIds, frontendTemplates []string, params share.ShareParams, edge *rest_management_api_client.ZitiEdgeManagement) (shrZId string, frontendEndpoints []string, err error) { - var authUsers []*model.AuthUser + var authUsers []*sdk.AuthUser for _, authUser := range params.Body.AuthUsers { - authUsers = append(authUsers, &model.AuthUser{authUser.Username, authUser.Password}) + authUsers = append(authUsers, &sdk.AuthUser{authUser.Username, authUser.Password}) } cfgId, err := zrokEdgeSdk.CreateConfig(zrokProxyConfigId, envZId, shrToken, params.Body.AuthScheme, authUsers, edge) if err != nil { diff --git a/controller/startup.go b/controller/startup.go index 19ce1cebc..cdbd27599 100644 --- a/controller/startup.go +++ b/controller/startup.go @@ -6,7 +6,7 @@ import ( "github.com/openziti/edge-api/rest_management_api_client" "github.com/openziti/edge-api/rest_management_api_client/config" "github.com/openziti/zrok/controller/zrokEdgeSdk" - "github.com/openziti/zrok/model" + "github.com/openziti/zrok/sdk" "github.com/pkg/errors" "github.com/sirupsen/logrus" "time" @@ -36,7 +36,7 @@ func inspectZiti() error { } func findZrokProxyConfigType(edge *rest_management_api_client.ZitiEdgeManagement) error { - filter := fmt.Sprintf("name=\"%v\"", model.ZrokProxyConfig) + filter := fmt.Sprintf("name=\"%v\"", sdk.ZrokProxyConfig) limit := int64(100) offset := int64(0) listReq := &config.ListConfigTypesParams{ @@ -53,7 +53,7 @@ func findZrokProxyConfigType(edge *rest_management_api_client.ZitiEdgeManagement if len(listResp.Payload.Data) != 1 { return errors.Errorf("expected 1 zrok proxy config type, found %d", len(listResp.Payload.Data)) } - logrus.Infof("found '%v' config type with id '%v'", model.ZrokProxyConfig, *(listResp.Payload.Data[0].ID)) + logrus.Infof("found '%v' config type with id '%v'", sdk.ZrokProxyConfig, *(listResp.Payload.Data[0].ID)) zrokProxyConfigId = *(listResp.Payload.Data[0].ID) return nil diff --git a/controller/zrokEdgeSdk/config.go b/controller/zrokEdgeSdk/config.go index 46a67ba7b..b0b57509c 100644 --- a/controller/zrokEdgeSdk/config.go +++ b/controller/zrokEdgeSdk/config.go @@ -6,23 +6,23 @@ import ( "github.com/openziti/edge-api/rest_management_api_client" "github.com/openziti/edge-api/rest_management_api_client/config" "github.com/openziti/edge-api/rest_model" - "github.com/openziti/zrok/model" + "github.com/openziti/zrok/sdk" "github.com/sirupsen/logrus" "time" ) -func CreateConfig(cfgTypeZId, envZId, shrToken string, authSchemeStr string, authUsers []*model.AuthUser, edge *rest_management_api_client.ZitiEdgeManagement) (cfgZId string, err error) { - authScheme, err := model.ParseAuthScheme(authSchemeStr) +func CreateConfig(cfgTypeZId, envZId, shrToken string, authSchemeStr string, authUsers []*sdk.AuthUser, edge *rest_management_api_client.ZitiEdgeManagement) (cfgZId string, err error) { + authScheme, err := sdk.ParseAuthScheme(authSchemeStr) if err != nil { return "", err } - cfg := &model.ProxyConfig{ + cfg := &sdk.ProxyConfig{ AuthScheme: authScheme, } - if cfg.AuthScheme == model.Basic { - cfg.BasicAuth = &model.BasicAuth{} + if cfg.AuthScheme == sdk.Basic { + cfg.BasicAuth = &sdk.BasicAuth{} for _, authUser := range authUsers { - cfg.BasicAuth.Users = append(cfg.BasicAuth.Users, &model.AuthUser{Username: authUser.Username, Password: authUser.Password}) + cfg.BasicAuth.Users = append(cfg.BasicAuth.Users, &sdk.AuthUser{Username: authUser.Username, Password: authUser.Password}) } } cfgCrt := &rest_model.ConfigCreate{ diff --git a/docs/_attic/overview.md b/docs/_attic/overview.md index d098ac634..9aa3a3d7f 100644 --- a/docs/_attic/overview.md +++ b/docs/_attic/overview.md @@ -56,15 +56,15 @@ When the user runs `zrok enable` from their shell, here's what happens: 2. The `zrok` controller creates a new OpenZiti identity for the environment and enrolls it. 3. The `zrok` controller creates an edge router policy associating the new OpenZiti identity with `#all` edge routers. 4. The `zrok` controller returns the entire SDK configuration back to the `zrok enable` client. -5. The `zrok enable` client then stores the OpenZiti identity along with a few other housekeeping details in the user's `~/.zrok` folder (we refer to this as `zrokdir`, conceptually in the code). -6. With the OpenZiti identity and configuration details stored in the user's `zrokdir`, the user is then able to create any number of binding endpoints using the `zrok http` command. +5. The `zrok enable` client then stores the OpenZiti identity along with a few other housekeeping details in the user's `~/.zrok` folder. +6. With the OpenZiti identity and configuration details stored in the user's `environment`, the user is then able to create any number of binding endpoints using the `zrok http` command. ### The `zrok http` Flow When a `zrok` user issues a `zrok http` command for an endpoint, here's what happens: -1. The `zrok http` client gathers the necessary identity details from the `zrokdir` (this was all staged during `zrok enable`). +1. The `zrok http` client gathers the necessary identity details from the `environment` (this was all staged during `zrok enable`). 2. The `zrok http` client reaches out to the `zrok` controller with an authenticated `tunnel` request, asking to have a new endpoint binding created. 3. The `zrok` controller allocates a new "service name" for the binding. 4. The `zrok` controller creates a `zrok.proxy.v1` configuration describing the user's requested authentication details (currently: `none` or `basic`). diff --git a/docs/getting-started.md b/docs/getting-started.md index 53395cd68..1271a9f81 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -102,13 +102,13 @@ The `zrok` executable defaults to using the `zrok` service instance at `api.zrok ``` $ zrok config set apiEndpoint https://zrok.mydomain.com -[WARNING]: unable to open zrokdir metadata; ignoring +[WARNING]: unable to open environment metadata; ignoring zrok configuration updated ``` :::note -The `WARNING` about `zrokdir metadata` is ignorable. Running the `zrok config set` command writes a small piece of metadata into a `.zrok` folder inside your home directory. This allows `zrok` to identify the version of its settings, providing a mechanism to upgrade your installation as new versions are released. This `WARNING` is letting you know that your current environment has not been initialized by `zrok`. +The `WARNING` about `environment metadata` is ignorable. Running the `zrok config set` command writes a small piece of metadata into a `.zrok` folder inside your home directory. This allows `zrok` to identify the version of its settings, providing a mechanism to upgrade your installation as new versions are released. This `WARNING` is letting you know that your current environment has not been initialized by `zrok`. ::: You can use the `zrok status` command to inspect the state of your local _environment_. `zrok` refers to each shell where you install and `enable` a copy of `zrok` as as an _environment_. diff --git a/endpoints/proxy/frontend.go b/endpoints/proxy/frontend.go index be6393357..c82c677c3 100644 --- a/endpoints/proxy/frontend.go +++ b/endpoints/proxy/frontend.go @@ -6,9 +6,9 @@ import ( "github.com/openziti/sdk-golang/ziti" "github.com/openziti/zrok/endpoints" "github.com/openziti/zrok/endpoints/publicProxy/notFoundUi" - "github.com/openziti/zrok/model" + "github.com/openziti/zrok/environment" + "github.com/openziti/zrok/sdk" "github.com/openziti/zrok/util" - "github.com/openziti/zrok/zrokdir" "github.com/pkg/errors" "github.com/sirupsen/logrus" "net" @@ -40,15 +40,19 @@ type Frontend struct { } func NewFrontend(cfg *FrontendConfig) (*Frontend, error) { - zCfgPath, err := zrokdir.ZitiIdentityFile(cfg.IdentityName) + env, err := environment.LoadRoot() if err != nil { - return nil, errors.Wrapf(err, "error getting ziti identity '%v' from zrokdir", cfg.IdentityName) + return nil, errors.Wrap(err, "error loading environment root") + } + zCfgPath, err := env.ZitiIdentityNamed(cfg.IdentityName) + if err != nil { + return nil, errors.Wrapf(err, "error getting ziti identity '%v' from environment", cfg.IdentityName) } zCfg, err := ziti.NewConfigFromFile(zCfgPath) if err != nil { return nil, errors.Wrap(err, "error loading config") } - zCfg.ConfigTypes = []string{model.ZrokProxyConfig} + zCfg.ConfigTypes = []string{sdk.ZrokProxyConfig} zCtx, err := ziti.NewContext(zCfg) if err != nil { return nil, errors.Wrap(err, "error loading ziti context") @@ -117,7 +121,7 @@ func serviceTargetProxy(cfg *FrontendConfig, ctx ziti.Context) *httputil.Reverse director := func(req *http.Request) { targetShrToken := cfg.ShrToken if svc, found := endpoints.GetRefreshedService(targetShrToken, ctx); found { - if cfg, found := svc.Config[model.ZrokProxyConfig]; found { + if cfg, found := svc.Config[sdk.ZrokProxyConfig]; found { logrus.Debugf("auth model: %v", cfg) } else { logrus.Warn("no config!") @@ -149,15 +153,15 @@ func serviceTargetProxy(cfg *FrontendConfig, ctx ziti.Context) *httputil.Reverse func authHandler(shrToken string, handler http.Handler, realm string, cfg *FrontendConfig, ctx ziti.Context) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { if svc, found := endpoints.GetRefreshedService(shrToken, ctx); found { - if cfg, found := svc.Config[model.ZrokProxyConfig]; found { + if cfg, found := svc.Config[sdk.ZrokProxyConfig]; found { if scheme, found := cfg["auth_scheme"]; found { switch scheme { - case string(model.None): + case string(sdk.None): logrus.Debugf("auth scheme none '%v'", shrToken) handler.ServeHTTP(w, r) return - case string(model.Basic): + case string(sdk.Basic): logrus.Debugf("auth scheme basic '%v", shrToken) inUser, inPass, ok := r.BasicAuth() if !ok { diff --git a/endpoints/publicProxy/config.go b/endpoints/publicProxy/config.go index fd6fe6c04..cb939e7e4 100644 --- a/endpoints/publicProxy/config.go +++ b/endpoints/publicProxy/config.go @@ -13,7 +13,7 @@ type Config struct { func DefaultConfig() *Config { return &Config{ - Identity: "frontend", + Identity: "public", Address: "0.0.0.0:8080", } } diff --git a/endpoints/publicProxy/http.go b/endpoints/publicProxy/http.go index e9aab21b8..4403c4b1f 100644 --- a/endpoints/publicProxy/http.go +++ b/endpoints/publicProxy/http.go @@ -7,9 +7,9 @@ import ( "github.com/openziti/zrok/endpoints" "github.com/openziti/zrok/endpoints/publicProxy/healthUi" "github.com/openziti/zrok/endpoints/publicProxy/notFoundUi" - "github.com/openziti/zrok/model" + "github.com/openziti/zrok/environment" + "github.com/openziti/zrok/sdk" "github.com/openziti/zrok/util" - "github.com/openziti/zrok/zrokdir" "github.com/pkg/errors" "github.com/sirupsen/logrus" "net" @@ -26,15 +26,19 @@ type httpFrontend struct { } func NewHTTP(cfg *Config) (*httpFrontend, error) { - zCfgPath, err := zrokdir.ZitiIdentityFile(cfg.Identity) + env, err := environment.LoadRoot() if err != nil { - return nil, errors.Wrapf(err, "error getting ziti identity '%v' from zrokdir", cfg.Identity) + return nil, errors.Wrap(err, "error loading environment root") + } + zCfgPath, err := env.ZitiIdentityNamed(cfg.Identity) + if err != nil { + return nil, errors.Wrapf(err, "error getting ziti identity '%v' from environment", cfg.Identity) } zCfg, err := ziti.NewConfigFromFile(zCfgPath) if err != nil { return nil, errors.Wrap(err, "error loading config") } - zCfg.ConfigTypes = []string{model.ZrokProxyConfig} + zCfg.ConfigTypes = []string{sdk.ZrokProxyConfig} zCtx, err := ziti.NewContext(zCfg) if err != nil { return nil, errors.Wrap(err, "error loading ziti context") @@ -95,7 +99,7 @@ func hostTargetReverseProxy(cfg *Config, ctx ziti.Context) *httputil.ReverseProx director := func(req *http.Request) { targetShrToken := resolveService(cfg.HostMatch, req.Host) if svc, found := endpoints.GetRefreshedService(targetShrToken, ctx); found { - if cfg, found := svc.Config[model.ZrokProxyConfig]; found { + if cfg, found := svc.Config[sdk.ZrokProxyConfig]; found { logrus.Debugf("auth model: %v", cfg) } else { logrus.Warn("no config!") @@ -129,15 +133,15 @@ func authHandler(handler http.Handler, realm string, cfg *Config, ctx ziti.Conte shrToken := resolveService(cfg.HostMatch, r.Host) if shrToken != "" { if svc, found := endpoints.GetRefreshedService(shrToken, ctx); found { - if cfg, found := svc.Config[model.ZrokProxyConfig]; found { + if cfg, found := svc.Config[sdk.ZrokProxyConfig]; found { if scheme, found := cfg["auth_scheme"]; found { switch scheme { - case string(model.None): + case string(sdk.None): logrus.Debugf("auth scheme none '%v'", shrToken) handler.ServeHTTP(w, r) return - case string(model.Basic): + case string(sdk.Basic): logrus.Debugf("auth scheme basic '%v", shrToken) inUser, inPass, ok := r.BasicAuth() if !ok { diff --git a/endpoints/tcpTunnel/frontend.go b/endpoints/tcpTunnel/frontend.go index 217a63530..375c6f2d2 100644 --- a/endpoints/tcpTunnel/frontend.go +++ b/endpoints/tcpTunnel/frontend.go @@ -3,8 +3,8 @@ package tcpTunnel import ( "github.com/openziti/sdk-golang/ziti" "github.com/openziti/zrok/endpoints" - "github.com/openziti/zrok/model" - "github.com/openziti/zrok/zrokdir" + "github.com/openziti/zrok/environment" + "github.com/openziti/zrok/sdk" "github.com/pkg/errors" "github.com/sirupsen/logrus" "net" @@ -29,15 +29,19 @@ func NewFrontend(cfg *FrontendConfig) (*Frontend, error) { if err != nil { return nil, errors.Wrapf(err, "error resolving tcp address '%v'", cfg.BindAddress) } - zCfgPath, err := zrokdir.ZitiIdentityFile(cfg.IdentityName) + env, err := environment.LoadRoot() if err != nil { - return nil, errors.Wrapf(err, "error getting ziti identity '%v' from zrokdir", cfg.IdentityName) + return nil, errors.Wrap(err, "error loading environment root") + } + zCfgPath, err := env.ZitiIdentityNamed(cfg.IdentityName) + if err != nil { + return nil, errors.Wrapf(err, "error getting ziti identity '%v' from environment", cfg.IdentityName) } zCfg, err := ziti.NewConfigFromFile(zCfgPath) if err != nil { return nil, errors.Wrap(err, "error loading config") } - zCfg.ConfigTypes = []string{model.ZrokProxyConfig} + zCfg.ConfigTypes = []string{sdk.ZrokProxyConfig} zCtx, err := ziti.NewContext(zCfg) if err != nil { return nil, errors.Wrap(err, "error loading ziti context") diff --git a/endpoints/udpTunnel/frontend.go b/endpoints/udpTunnel/frontend.go index 954433cbc..3e837bb89 100644 --- a/endpoints/udpTunnel/frontend.go +++ b/endpoints/udpTunnel/frontend.go @@ -3,8 +3,8 @@ package udpTunnel import ( "github.com/openziti/sdk-golang/ziti" "github.com/openziti/zrok/endpoints" - "github.com/openziti/zrok/model" - "github.com/openziti/zrok/zrokdir" + "github.com/openziti/zrok/environment" + "github.com/openziti/zrok/sdk" "github.com/pkg/errors" "github.com/sirupsen/logrus" "net" @@ -99,15 +99,19 @@ func NewFrontend(cfg *FrontendConfig) (*Frontend, error) { if err != nil { return nil, errors.Wrapf(err, "error resolving udp address '%v'", cfg.BindAddress) } - zCfgPath, err := zrokdir.ZitiIdentityFile(cfg.IdentityName) + env, err := environment.LoadRoot() if err != nil { - return nil, errors.Wrapf(err, "error getting ziti identity '%v' from zrokdir", cfg.IdentityName) + return nil, errors.Wrap(err, "error loading environment root") + } + zCfgPath, err := env.ZitiIdentityNamed(cfg.IdentityName) + if err != nil { + return nil, errors.Wrapf(err, "error getting ziti identity '%v' from environment", cfg.IdentityName) } zCfg, err := ziti.NewConfigFromFile(zCfgPath) if err != nil { return nil, errors.Wrap(err, "error loading config") } - zCfg.ConfigTypes = []string{model.ZrokProxyConfig} + zCfg.ConfigTypes = []string{sdk.ZrokProxyConfig} zCtx, err := ziti.NewContext(zCfg) if err != nil { return nil, errors.Wrap(err, "error loading ziti context") diff --git a/environment/api.go b/environment/api.go new file mode 100644 index 000000000..213d9ed49 --- /dev/null +++ b/environment/api.go @@ -0,0 +1,39 @@ +package environment + +import ( + "github.com/openziti/zrok/environment/env_core" + "github.com/openziti/zrok/environment/env_v0_3" + "github.com/openziti/zrok/environment/env_v0_4" + "github.com/pkg/errors" +) + +func LoadRoot() (env_core.Root, error) { + if assert, err := env_v0_4.Assert(); assert && err == nil { + return env_v0_4.Load() + } else if assert, err := env_v0_3.Assert(); assert && err == nil { + return env_v0_3.Load() + } else { + return nil, err + } +} + +func IsLatest(r env_core.Root) bool { + if r == nil { + return false + } + if r.Metadata() == nil { + return false + } + if r.Metadata().V == env_v0_4.V { + return true + } + return false +} + +func UpdateRoot(r env_core.Root) (env_core.Root, error) { + newR, err := env_v0_4.Update(r) + if err != nil { + return nil, errors.Wrap(err, "unable to update environment") + } + return newR, nil +} diff --git a/environment/env_core/model.go b/environment/env_core/model.go new file mode 100644 index 000000000..8c8384291 --- /dev/null +++ b/environment/env_core/model.go @@ -0,0 +1,43 @@ +package env_core + +import "github.com/openziti/zrok/rest_client_zrok" + +// Root is the primary interface encapsulating the on-disk environment data. +type Root interface { + Metadata() *Metadata + Obliterate() error + + HasConfig() (bool, error) + Config() *Config + SetConfig(cfg *Config) error + + Client() (*rest_client_zrok.Zrok, error) + ApiEndpoint() (string, string) + + IsEnabled() bool + Environment() *Environment + SetEnvironment(env *Environment) error + DeleteEnvironment() error + + PublicIdentityName() string + EnvironmentIdentityName() string + + ZitiIdentityNamed(name string) (string, error) + SaveZitiIdentityNamed(name, data string) error + DeleteZitiIdentityNamed(name string) error +} + +type Environment struct { + Token string + ZitiIdentity string + ApiEndpoint string +} + +type Config struct { + ApiEndpoint string +} + +type Metadata struct { + V string + RootPath string +} diff --git a/environment/env_v0_3/api.go b/environment/env_v0_3/api.go new file mode 100644 index 000000000..b7bd285ce --- /dev/null +++ b/environment/env_v0_3/api.go @@ -0,0 +1,168 @@ +package env_v0_3 + +import ( + "github.com/go-openapi/runtime" + httptransport "github.com/go-openapi/runtime/client" + "github.com/go-openapi/strfmt" + "github.com/openziti/zrok/build" + "github.com/openziti/zrok/environment/env_core" + "github.com/openziti/zrok/rest_client_zrok" + "github.com/pkg/errors" + "net/url" + "os" + "path/filepath" + "regexp" +) + +func (r *Root) Metadata() *env_core.Metadata { + return r.meta +} + +func (r *Root) HasConfig() (bool, error) { + return r.cfg != nil, nil +} + +func (r *Root) Config() *env_core.Config { + return r.cfg +} + +func (r *Root) SetConfig(cfg *env_core.Config) error { + if err := assertMetadata(); err != nil { + return err + } + if err := saveConfig(cfg); err != nil { + return err + } + r.cfg = cfg + return nil +} + +func (r *Root) Client() (*rest_client_zrok.Zrok, error) { + apiEndpoint, _ := r.ApiEndpoint() + apiUrl, err := url.Parse(apiEndpoint) + if err != nil { + return nil, errors.Wrapf(err, "error parsing api endpoint '%v'", r) + } + transport := httptransport.New(apiUrl.Host, "/api/v1", []string{apiUrl.Scheme}) + transport.Producers["application/zrok.v1+json"] = runtime.JSONProducer() + transport.Consumers["application/zrok.v1+json"] = runtime.JSONConsumer() + + zrok := rest_client_zrok.New(transport, strfmt.Default) + v, err := zrok.Metadata.Version(nil) + if err != nil { + return nil, errors.Wrapf(err, "error getting version from api endpoint '%v': %v", apiEndpoint, err) + } + // allow reported version string to be optionally prefixed with + // "refs/heads/" or "refs/tags/" + re := regexp.MustCompile(`^(refs/(heads|tags)/)?` + build.Series) + if !re.MatchString(string(v.Payload)) { + return nil, errors.Errorf("expected a '%v' version, received: '%v'", build.Series, v.Payload) + } + + return zrok, nil +} + +func (r *Root) ApiEndpoint() (string, string) { + apiEndpoint := "https://api.zrok.io" + from := "binary" + + if r.Config() != nil && r.Config().ApiEndpoint != "" { + apiEndpoint = r.Config().ApiEndpoint + from = "config" + } + + env := os.Getenv("ZROK_API_ENDPOINT") + if env != "" { + apiEndpoint = env + from = "ZROK_API_ENDPOINT" + } + + if r.IsEnabled() { + apiEndpoint = r.Environment().ApiEndpoint + from = "env" + } + + return apiEndpoint, from +} + +func (r *Root) Environment() *env_core.Environment { + return r.env +} + +func (r *Root) SetEnvironment(env *env_core.Environment) error { + if err := assertMetadata(); err != nil { + return err + } + if err := saveEnvironment(env); err != nil { + return err + } + r.env = env + return nil +} + +func (r *Root) DeleteEnvironment() error { + ef, err := environmentFile() + if err != nil { + return errors.Wrap(err, "error getting environment file") + } + if err := os.Remove(ef); err != nil { + return errors.Wrap(err, "error removing environment file") + } + r.env = nil + return nil +} + +func (r *Root) IsEnabled() bool { + return r.env != nil +} + +func (r *Root) PublicIdentityName() string { + return "frontend" +} + +func (r *Root) EnvironmentIdentityName() string { + return "backend" +} + +func (r *Root) ZitiIdentityNamed(name string) (string, error) { + return identityFile(name) +} + +func (r *Root) SaveZitiIdentityNamed(name, data string) error { + if err := assertMetadata(); err != nil { + return err + } + zif, err := r.ZitiIdentityNamed(name) + if err != nil { + return err + } + if err := os.MkdirAll(filepath.Dir(zif), os.FileMode(0700)); err != nil { + return errors.Wrapf(err, "error creating environment path '%v'", filepath.Dir(zif)) + } + if err := os.WriteFile(zif, []byte(data), os.FileMode(0600)); err != nil { + return errors.Wrapf(err, "error writing ziti identity file '%v'", zif) + } + return nil +} + +func (r *Root) DeleteZitiIdentityNamed(name string) error { + zif, err := r.ZitiIdentityNamed(name) + if err != nil { + return errors.Wrapf(err, "error getting ziti identity file path for '%v'", name) + } + if err := os.Remove(zif); err != nil { + return errors.Wrapf(err, "error removing ziti identity file '%v'", zif) + } + return nil +} + +func (r *Root) Obliterate() error { + zrd, err := rootDir() + if err != nil { + return err + } + if err := os.RemoveAll(zrd); err != nil { + return err + } + return nil +} diff --git a/environment/env_v0_3/dirs.go b/environment/env_v0_3/dirs.go new file mode 100644 index 000000000..ec382f7fe --- /dev/null +++ b/environment/env_v0_3/dirs.go @@ -0,0 +1,55 @@ +package env_v0_3 + +import ( + "fmt" + "os" + "path/filepath" +) + +func rootDir() (string, error) { + home, err := os.UserHomeDir() + if err != nil { + return "", err + } + return filepath.Join(home, ".zrok"), nil +} + +func metadataFile() (string, error) { + zrd, err := rootDir() + if err != nil { + return "", err + } + return filepath.Join(zrd, "metadata.json"), nil +} + +func configFile() (string, error) { + zrd, err := rootDir() + if err != nil { + return "", err + } + return filepath.Join(zrd, "config.json"), nil +} + +func environmentFile() (string, error) { + zrd, err := rootDir() + if err != nil { + return "", err + } + return filepath.Join(zrd, "environment.json"), nil +} + +func identitiesDir() (string, error) { + zrd, err := rootDir() + if err != nil { + return "", err + } + return filepath.Join(zrd, "identities"), nil +} + +func identityFile(name string) (string, error) { + idd, err := identitiesDir() + if err != nil { + return "", err + } + return filepath.Join(idd, fmt.Sprintf("%v.json", name)), nil +} diff --git a/environment/env_v0_3/root.go b/environment/env_v0_3/root.go new file mode 100644 index 000000000..f80345936 --- /dev/null +++ b/environment/env_v0_3/root.go @@ -0,0 +1,262 @@ +package env_v0_3 + +import ( + "encoding/json" + "github.com/openziti/zrok/environment/env_core" + "github.com/pkg/errors" + "os" + "path/filepath" +) + +const V = "v0.3" + +type Root struct { + meta *env_core.Metadata + cfg *env_core.Config + env *env_core.Environment +} + +func Assert() (bool, error) { + exists, err := rootExists() + if err != nil { + return true, err + } + if exists { + meta, err := loadMetadata() + if err != nil { + return true, err + } + return meta.V == V, nil + } + return false, nil +} + +func Load() (*Root, error) { + r := &Root{} + exists, err := rootExists() + if err != nil { + return nil, err + } + if exists { + if meta, err := loadMetadata(); err == nil { + r.meta = meta + } else { + return nil, err + } + + if cfg, err := loadConfig(); err == nil { + r.cfg = cfg + } + + if env, err := loadEnvironment(); err == nil { + r.env = env + } + + } else { + root, err := rootDir() + if err != nil { + return nil, err + } + r.meta = &env_core.Metadata{ + V: V, + RootPath: root, + } + } + return r, nil +} + +func rootExists() (bool, error) { + mf, err := metadataFile() + if err != nil { + return false, err + } + _, err = os.Stat(mf) + if os.IsNotExist(err) { + return false, nil + } + if err != nil { + return false, err + } + return true, nil +} + +func assertMetadata() error { + exists, err := rootExists() + if err != nil { + return err + } + if !exists { + if err := writeMetadata(); err != nil { + return err + } + } + return nil +} + +func loadMetadata() (*env_core.Metadata, error) { + mf, err := metadataFile() + if err != nil { + return nil, err + } + data, err := os.ReadFile(mf) + if err != nil { + return nil, err + } + m := &metadata{} + if err := json.Unmarshal(data, m); err != nil { + return nil, errors.Wrapf(err, "error unmarshaling metadata file '%v'", mf) + } + if m.V != V { + return nil, errors.Errorf("got metadata version '%v', expected '%v'", m.V, V) + } + rf, err := rootDir() + if err != nil { + return nil, err + } + out := &env_core.Metadata{ + V: m.V, + RootPath: rf, + } + return out, nil +} + +func writeMetadata() error { + mf, err := metadataFile() + if err != nil { + return err + } + data, err := json.Marshal(&metadata{V: V}) + if err != nil { + return err + } + if err := os.MkdirAll(filepath.Dir(mf), os.FileMode(0700)); err != nil { + return err + } + if err := os.WriteFile(mf, data, os.FileMode(0600)); err != nil { + return err + } + return nil +} + +func loadConfig() (*env_core.Config, error) { + cf, err := configFile() + if err != nil { + return nil, errors.Wrap(err, "error getting config file path") + } + data, err := os.ReadFile(cf) + if err != nil { + return nil, errors.Wrapf(err, "error reading config file '%v'", cf) + } + cfg := &config{} + if err := json.Unmarshal(data, cfg); err != nil { + return nil, errors.Wrapf(err, "error unmarshaling config file '%v'", cf) + } + out := &env_core.Config{ + ApiEndpoint: cfg.ApiEndpoint, + } + return out, nil +} + +func saveConfig(cfg *env_core.Config) error { + in := &config{ApiEndpoint: cfg.ApiEndpoint} + data, err := json.MarshalIndent(in, "", " ") + if err != nil { + return errors.Wrap(err, "error marshaling config") + } + cf, err := configFile() + if err != nil { + return errors.Wrap(err, "error getting config file path") + } + if err := os.MkdirAll(filepath.Dir(cf), os.FileMode(0700)); err != nil { + return errors.Wrapf(err, "error creating environment path '%v'", filepath.Dir(cf)) + } + if err := os.WriteFile(cf, data, os.FileMode(0600)); err != nil { + return errors.Wrap(err, "error saving config file") + } + return nil +} + +func isEnabled() (bool, error) { + ef, err := environmentFile() + if err != nil { + return false, errors.Wrap(err, "error getting environment file path") + } + _, err = os.Stat(ef) + if os.IsNotExist(err) { + return false, nil + } + if err != nil { + return false, errors.Wrapf(err, "error stat-ing environment file '%v'", ef) + } + return true, nil +} + +func loadEnvironment() (*env_core.Environment, error) { + ef, err := environmentFile() + if err != nil { + return nil, errors.Wrap(err, "error getting environment file") + } + data, err := os.ReadFile(ef) + if err != nil { + return nil, errors.Wrapf(err, "error reading environment file '%v'", ef) + } + env := &environment{} + if err := json.Unmarshal(data, env); err != nil { + return nil, errors.Wrapf(err, "error unmarshaling environment file '%v'", ef) + } + out := &env_core.Environment{ + Token: env.Token, + ZitiIdentity: env.ZId, + ApiEndpoint: env.ApiEndpoint, + } + return out, nil +} + +func saveEnvironment(env *env_core.Environment) error { + in := &environment{ + Token: env.Token, + ZId: env.ZitiIdentity, + ApiEndpoint: env.ApiEndpoint, + } + data, err := json.MarshalIndent(in, "", " ") + if err != nil { + return errors.Wrap(err, "error marshaling environment") + } + ef, err := environmentFile() + if err != nil { + return errors.Wrap(err, "error getting environment file") + } + if err := os.MkdirAll(filepath.Dir(ef), os.FileMode(0700)); err != nil { + return errors.Wrapf(err, "error creating environment path '%v'", filepath.Dir(ef)) + } + if err := os.WriteFile(ef, data, os.FileMode(0600)); err != nil { + return errors.Wrap(err, "error saving environment file") + } + return nil +} + +func deleteEnvironment() error { + ef, err := environmentFile() + if err != nil { + return errors.Wrap(err, "error getting environment file") + } + if err := os.Remove(ef); err != nil { + return errors.Wrap(err, "error removing environment file") + } + + return nil +} + +type metadata struct { + V string `json:"v"` +} + +type config struct { + ApiEndpoint string `json:"api_endpoint"` +} + +type environment struct { + Token string `json:"zrok_token"` + ZId string `json:"ziti_identity"` + ApiEndpoint string `json:"api_endpoint"` +} diff --git a/environment/env_v0_4/api.go b/environment/env_v0_4/api.go new file mode 100644 index 000000000..3e08202b4 --- /dev/null +++ b/environment/env_v0_4/api.go @@ -0,0 +1,168 @@ +package env_v0_4 + +import ( + "github.com/go-openapi/runtime" + httptransport "github.com/go-openapi/runtime/client" + "github.com/go-openapi/strfmt" + "github.com/openziti/zrok/build" + "github.com/openziti/zrok/environment/env_core" + "github.com/openziti/zrok/rest_client_zrok" + "github.com/pkg/errors" + "net/url" + "os" + "path/filepath" + "regexp" +) + +func (r *Root) Metadata() *env_core.Metadata { + return r.meta +} + +func (r *Root) HasConfig() (bool, error) { + return r.cfg != nil, nil +} + +func (r *Root) Config() *env_core.Config { + return r.cfg +} + +func (r *Root) SetConfig(cfg *env_core.Config) error { + if err := assertMetadata(); err != nil { + return err + } + if err := saveConfig(cfg); err != nil { + return err + } + r.cfg = cfg + return nil +} + +func (r *Root) Client() (*rest_client_zrok.Zrok, error) { + apiEndpoint, _ := r.ApiEndpoint() + apiUrl, err := url.Parse(apiEndpoint) + if err != nil { + return nil, errors.Wrapf(err, "error parsing api endpoint '%v'", r) + } + transport := httptransport.New(apiUrl.Host, "/api/v1", []string{apiUrl.Scheme}) + transport.Producers["application/zrok.v1+json"] = runtime.JSONProducer() + transport.Consumers["application/zrok.v1+json"] = runtime.JSONConsumer() + + zrok := rest_client_zrok.New(transport, strfmt.Default) + v, err := zrok.Metadata.Version(nil) + if err != nil { + return nil, errors.Wrapf(err, "error getting version from api endpoint '%v': %v", apiEndpoint, err) + } + // allow reported version string to be optionally prefixed with + // "refs/heads/" or "refs/tags/" + re := regexp.MustCompile(`^(refs/(heads|tags)/)?` + build.Series) + if !re.MatchString(string(v.Payload)) { + return nil, errors.Errorf("expected a '%v' version, received: '%v'", build.Series, v.Payload) + } + + return zrok, nil +} + +func (r *Root) ApiEndpoint() (string, string) { + apiEndpoint := "https://api.zrok.io" + from := "binary" + + if r.Config() != nil && r.Config().ApiEndpoint != "" { + apiEndpoint = r.Config().ApiEndpoint + from = "config" + } + + env := os.Getenv("ZROK_API_ENDPOINT") + if env != "" { + apiEndpoint = env + from = "ZROK_API_ENDPOINT" + } + + if r.IsEnabled() { + apiEndpoint = r.Environment().ApiEndpoint + from = "env" + } + + return apiEndpoint, from +} + +func (r *Root) Environment() *env_core.Environment { + return r.env +} + +func (r *Root) SetEnvironment(env *env_core.Environment) error { + if err := assertMetadata(); err != nil { + return err + } + if err := saveEnvironment(env); err != nil { + return err + } + r.env = env + return nil +} + +func (r *Root) DeleteEnvironment() error { + ef, err := environmentFile() + if err != nil { + return errors.Wrap(err, "error getting environment file") + } + if err := os.Remove(ef); err != nil { + return errors.Wrap(err, "error removing environment file") + } + r.env = nil + return nil +} + +func (r *Root) IsEnabled() bool { + return r.env != nil +} + +func (r *Root) PublicIdentityName() string { + return "public" +} + +func (r *Root) EnvironmentIdentityName() string { + return "environment" +} + +func (r *Root) ZitiIdentityNamed(name string) (string, error) { + return identityFile(name) +} + +func (r *Root) SaveZitiIdentityNamed(name, data string) error { + if err := assertMetadata(); err != nil { + return err + } + zif, err := r.ZitiIdentityNamed(name) + if err != nil { + return err + } + if err := os.MkdirAll(filepath.Dir(zif), os.FileMode(0700)); err != nil { + return errors.Wrapf(err, "error creating environment path '%v'", filepath.Dir(zif)) + } + if err := os.WriteFile(zif, []byte(data), os.FileMode(0600)); err != nil { + return errors.Wrapf(err, "error writing ziti identity file '%v'", zif) + } + return nil +} + +func (r *Root) DeleteZitiIdentityNamed(name string) error { + zif, err := r.ZitiIdentityNamed(name) + if err != nil { + return errors.Wrapf(err, "error getting ziti identity file path for '%v'", name) + } + if err := os.Remove(zif); err != nil { + return errors.Wrapf(err, "error removing ziti identity file '%v'", zif) + } + return nil +} + +func (r *Root) Obliterate() error { + zrd, err := rootDir() + if err != nil { + return err + } + if err := os.RemoveAll(zrd); err != nil { + return err + } + return nil +} diff --git a/environment/env_v0_4/dirs.go b/environment/env_v0_4/dirs.go new file mode 100644 index 000000000..b259fe094 --- /dev/null +++ b/environment/env_v0_4/dirs.go @@ -0,0 +1,55 @@ +package env_v0_4 + +import ( + "fmt" + "os" + "path/filepath" +) + +func rootDir() (string, error) { + home, err := os.UserHomeDir() + if err != nil { + return "", err + } + return filepath.Join(home, ".zrok"), nil +} + +func metadataFile() (string, error) { + zrd, err := rootDir() + if err != nil { + return "", err + } + return filepath.Join(zrd, "metadata.json"), nil +} + +func configFile() (string, error) { + zrd, err := rootDir() + if err != nil { + return "", err + } + return filepath.Join(zrd, "config.json"), nil +} + +func environmentFile() (string, error) { + zrd, err := rootDir() + if err != nil { + return "", err + } + return filepath.Join(zrd, "environment.json"), nil +} + +func identitiesDir() (string, error) { + zrd, err := rootDir() + if err != nil { + return "", err + } + return filepath.Join(zrd, "identities"), nil +} + +func identityFile(name string) (string, error) { + idd, err := identitiesDir() + if err != nil { + return "", err + } + return filepath.Join(idd, fmt.Sprintf("%v.json", name)), nil +} diff --git a/environment/env_v0_4/root.go b/environment/env_v0_4/root.go new file mode 100644 index 000000000..2a5343393 --- /dev/null +++ b/environment/env_v0_4/root.go @@ -0,0 +1,323 @@ +package env_v0_4 + +import ( + "encoding/json" + "fmt" + "github.com/openziti/zrok/environment/env_core" + "github.com/openziti/zrok/environment/env_v0_3" + "github.com/pkg/errors" + "os" + "path/filepath" +) + +const V = "v0.4" + +type Root struct { + meta *env_core.Metadata + cfg *env_core.Config + env *env_core.Environment +} + +func Assert() (bool, error) { + exists, err := rootExists() + if err != nil { + return true, err + } + if exists { + meta, err := loadMetadata() + if err != nil { + return true, err + } + return meta.V == V, nil + } + return false, nil +} + +func Load() (*Root, error) { + r := &Root{} + exists, err := rootExists() + if err != nil { + return nil, err + } + if exists { + if meta, err := loadMetadata(); err == nil { + r.meta = meta + } else { + return nil, err + } + + if cfg, err := loadConfig(); err == nil { + r.cfg = cfg + } + + if env, err := loadEnvironment(); err == nil { + r.env = env + } + + } else { + root, err := rootDir() + if err != nil { + return nil, err + } + r.meta = &env_core.Metadata{ + V: V, + RootPath: root, + } + } + return r, nil +} + +func Update(r env_core.Root) (env_core.Root, error) { + if r == nil || r.Metadata() == nil { + return nil, errors.Errorf("nil root") + } + if r.Metadata().V != env_v0_3.V { + return nil, errors.Errorf("expecting version '%v'", env_v0_3.V) + } + + newR := &Root{meta: r.Metadata(), cfg: r.Config(), env: r.Environment()} + + oldAccessF, err := r.ZitiIdentityNamed(r.PublicIdentityName()) + if err != nil { + return nil, err + } + _, err = os.Stat(oldAccessF) + if err == nil { + newAccessF, err := newR.ZitiIdentityNamed(newR.PublicIdentityName()) + if err != nil { + return nil, err + } + if err := os.Rename(oldAccessF, newAccessF); err != nil { + return nil, err + } + fmt.Printf("renamed '%v' -> '%v'\n", oldAccessF, newAccessF) + } else if !os.IsNotExist(err) { + return nil, err + } + + oldShareF, err := r.ZitiIdentityNamed(r.EnvironmentIdentityName()) + if err != nil { + return nil, err + } + _, err = os.Stat(oldShareF) + if err == nil { + newShareF, err := newR.ZitiIdentityNamed(newR.EnvironmentIdentityName()) + if err != nil { + return nil, err + } + if err := os.Rename(oldShareF, newShareF); err != nil { + return nil, err + } + fmt.Printf("renamed '%v' -> '%v'\n", oldShareF, newShareF) + } else if !os.IsNotExist(err) { + return nil, err + } + + if err := writeMetadata(); err != nil { + return nil, err + } + + meta, err := loadMetadata() + if err != nil { + return nil, err + } + newR.meta = meta + + return newR, nil +} + +func rootExists() (bool, error) { + mf, err := metadataFile() + if err != nil { + return false, err + } + _, err = os.Stat(mf) + if os.IsNotExist(err) { + return false, nil + } + if err != nil { + return false, err + } + return true, nil +} + +func assertMetadata() error { + exists, err := rootExists() + if err != nil { + return err + } + if !exists { + if err := writeMetadata(); err != nil { + return err + } + } + return nil +} + +func loadMetadata() (*env_core.Metadata, error) { + mf, err := metadataFile() + if err != nil { + return nil, err + } + data, err := os.ReadFile(mf) + if err != nil { + return nil, err + } + m := &metadata{} + if err := json.Unmarshal(data, m); err != nil { + return nil, errors.Wrapf(err, "error unmarshaling metadata file '%v'", mf) + } + if m.V != V { + return nil, errors.Errorf("got metadata version '%v', expected '%v'", m.V, V) + } + rf, err := rootDir() + if err != nil { + return nil, err + } + out := &env_core.Metadata{ + V: m.V, + RootPath: rf, + } + return out, nil +} + +func writeMetadata() error { + mf, err := metadataFile() + if err != nil { + return err + } + data, err := json.Marshal(&metadata{V: V}) + if err != nil { + return err + } + if err := os.MkdirAll(filepath.Dir(mf), os.FileMode(0700)); err != nil { + return err + } + if err := os.WriteFile(mf, data, os.FileMode(0600)); err != nil { + return err + } + return nil +} + +func loadConfig() (*env_core.Config, error) { + cf, err := configFile() + if err != nil { + return nil, errors.Wrap(err, "error getting config file path") + } + data, err := os.ReadFile(cf) + if err != nil { + return nil, errors.Wrapf(err, "error reading config file '%v'", cf) + } + cfg := &config{} + if err := json.Unmarshal(data, cfg); err != nil { + return nil, errors.Wrapf(err, "error unmarshaling config file '%v'", cf) + } + out := &env_core.Config{ + ApiEndpoint: cfg.ApiEndpoint, + } + return out, nil +} + +func saveConfig(cfg *env_core.Config) error { + in := &config{ApiEndpoint: cfg.ApiEndpoint} + data, err := json.MarshalIndent(in, "", " ") + if err != nil { + return errors.Wrap(err, "error marshaling config") + } + cf, err := configFile() + if err != nil { + return errors.Wrap(err, "error getting config file path") + } + if err := os.MkdirAll(filepath.Dir(cf), os.FileMode(0700)); err != nil { + return errors.Wrapf(err, "error creating environment path '%v'", filepath.Dir(cf)) + } + if err := os.WriteFile(cf, data, os.FileMode(0600)); err != nil { + return errors.Wrap(err, "error saving config file") + } + return nil +} + +func isEnabled() (bool, error) { + ef, err := environmentFile() + if err != nil { + return false, errors.Wrap(err, "error getting environment file path") + } + _, err = os.Stat(ef) + if os.IsNotExist(err) { + return false, nil + } + if err != nil { + return false, errors.Wrapf(err, "error stat-ing environment file '%v'", ef) + } + return true, nil +} + +func loadEnvironment() (*env_core.Environment, error) { + ef, err := environmentFile() + if err != nil { + return nil, errors.Wrap(err, "error getting environment file") + } + data, err := os.ReadFile(ef) + if err != nil { + return nil, errors.Wrapf(err, "error reading environment file '%v'", ef) + } + env := &environment{} + if err := json.Unmarshal(data, env); err != nil { + return nil, errors.Wrapf(err, "error unmarshaling environment file '%v'", ef) + } + out := &env_core.Environment{ + Token: env.Token, + ZitiIdentity: env.ZId, + ApiEndpoint: env.ApiEndpoint, + } + return out, nil +} + +func saveEnvironment(env *env_core.Environment) error { + in := &environment{ + Token: env.Token, + ZId: env.ZitiIdentity, + ApiEndpoint: env.ApiEndpoint, + } + data, err := json.MarshalIndent(in, "", " ") + if err != nil { + return errors.Wrap(err, "error marshaling environment") + } + ef, err := environmentFile() + if err != nil { + return errors.Wrap(err, "error getting environment file") + } + if err := os.MkdirAll(filepath.Dir(ef), os.FileMode(0700)); err != nil { + return errors.Wrapf(err, "error creating environment path '%v'", filepath.Dir(ef)) + } + if err := os.WriteFile(ef, data, os.FileMode(0600)); err != nil { + return errors.Wrap(err, "error saving environment file") + } + return nil +} + +func deleteEnvironment() error { + ef, err := environmentFile() + if err != nil { + return errors.Wrap(err, "error getting environment file") + } + if err := os.Remove(ef); err != nil { + return errors.Wrap(err, "error removing environment file") + } + + return nil +} + +type metadata struct { + V string `json:"v"` +} + +type config struct { + ApiEndpoint string `json:"api_endpoint"` +} + +type environment struct { + Token string `json:"zrok_token"` + ZId string `json:"ziti_identity"` + ApiEndpoint string `json:"api_endpoint"` +} diff --git a/model/metrics.go b/model/metrics.go deleted file mode 100644 index 33f9ca210..000000000 --- a/model/metrics.go +++ /dev/null @@ -1,12 +0,0 @@ -package model - -type Metrics struct { - Namespace string - Sessions map[string]SessionMetrics -} - -type SessionMetrics struct { - BytesRead int64 - BytesWritten int64 - LastUpdate int64 -} diff --git a/sdk/access.go b/sdk/access.go new file mode 100644 index 000000000..46c5af4ed --- /dev/null +++ b/sdk/access.go @@ -0,0 +1,56 @@ +package sdk + +import ( + httptransport "github.com/go-openapi/runtime/client" + "github.com/openziti/zrok/environment/env_core" + "github.com/openziti/zrok/rest_client_zrok/share" + "github.com/openziti/zrok/rest_model_zrok" + "github.com/pkg/errors" +) + +func CreateAccess(root env_core.Root, request *AccessRequest) (*Access, error) { + if !root.IsEnabled() { + return nil, errors.New("environment is not enabled; enable with 'zrok enable' first!") + } + + out := share.NewAccessParams() + out.Body = &rest_model_zrok.AccessRequest{ + ShrToken: request.ShareToken, + EnvZID: root.Environment().ZitiIdentity, + } + + zrok, err := root.Client() + if err != nil { + return nil, errors.Wrap(err, "error getting zrok client") + } + auth := httptransport.APIKeyAuth("X-TOKEN", "header", root.Environment().Token) + + in, err := zrok.Share.Access(out, auth) + if err != nil { + return nil, errors.Wrap(err, "unable to create access") + } + + return &Access{Token: in.Payload.FrontendToken, ShareToken: request.ShareToken, BackendMode: BackendMode(in.Payload.BackendMode)}, nil +} + +func DeleteAccess(root env_core.Root, acc *Access) error { + out := share.NewUnaccessParams() + out.Body = &rest_model_zrok.UnaccessRequest{ + FrontendToken: acc.Token, + ShrToken: acc.ShareToken, + EnvZID: root.Environment().ZitiIdentity, + } + + zrok, err := root.Client() + if err != nil { + return errors.Wrap(err, "error getting zrok client") + } + auth := httptransport.APIKeyAuth("X-TOKEN", "header", root.Environment().Token) + + _, err = zrok.Share.Unaccess(out, auth) + if err != nil { + return errors.Wrap(err, "error deleting access") + } + + return nil +} diff --git a/model/config.go b/sdk/config.go similarity index 85% rename from model/config.go rename to sdk/config.go index b6c03e4e4..e9d4c318c 100644 --- a/model/config.go +++ b/sdk/config.go @@ -1,16 +1,9 @@ -package model +package sdk import "github.com/pkg/errors" const ZrokProxyConfig = "zrok.proxy.v1" -type AuthScheme string - -const ( - None AuthScheme = "none" - Basic AuthScheme = "basic" -) - type ProxyConfig struct { AuthScheme AuthScheme `json:"auth_scheme"` BasicAuth *BasicAuth `json:"basic_auth"` diff --git a/sdk/dialer.go b/sdk/dialer.go new file mode 100644 index 000000000..2283ea6c1 --- /dev/null +++ b/sdk/dialer.go @@ -0,0 +1,32 @@ +package sdk + +import ( + "github.com/openziti/sdk-golang/ziti" + "github.com/openziti/sdk-golang/ziti/edge" + "github.com/openziti/zrok/environment/env_core" + "github.com/pkg/errors" +) + +func NewDialer(shrToken string, root env_core.Root) (edge.Conn, error) { + zif, err := root.ZitiIdentityNamed(root.EnvironmentIdentityName()) + if err != nil { + return nil, errors.Wrap(err, "error getting ziti identity path") + } + + zcfg, err := ziti.NewConfigFromFile(zif) + if err != nil { + return nil, errors.Wrap(err, "error loading ziti identity") + } + + zctx, err := ziti.NewContext(zcfg) + if err != nil { + return nil, errors.Wrap(err, "error getting ziti context") + } + + conn, err := zctx.Dial(shrToken) + if err != nil { + return nil, errors.Wrapf(err, "error dialing '%v'", shrToken) + } + + return conn, nil +} diff --git a/sdk/examples/pastebin/README.md b/sdk/examples/pastebin/README.md new file mode 100644 index 000000000..e8cfac2d3 --- /dev/null +++ b/sdk/examples/pastebin/README.md @@ -0,0 +1,117 @@ +# "pastebin" SDK Example + +This `pastebin` example is a minimal `zrok` SDK application that implements a wormhole that makes redirecting file contents between multiple `zrok` environments very easy. + +The `pastebin` example is split into two separate commands. The `copyto` command takes a copy buffer from standard input. You can use it like this: + +``` +$ echo "this is a pastebin test" | copyto +access your pastebin using 'pastefrom b46p9j82z81f' +``` + +And then using another terminal window, you can access your pastebin data like this: + +``` +$ pastefrom b46p9j82z81f +this is a pastebin test +``` + +## The `copyto` Implementation + +The `copyto` utility is an illustration of how to implement an application that creates a share and exposes it to the `zrok` network. Let's look at each section of the implementation: + +```go + data, err := loadData() + if err != nil { + panic(err) + } +``` + +This first block of code is responsible for calling the `loadData` function, which loads the pastebin with data from `os.Stdin`. + +All SDK applications need to load the user's "root" from the `environment` package, like this: + +```go + root, err := environment.LoadRoot() + if err != nil { + panic(err) + } +``` + +The `root` is a structure that contains all of the user's environment detail and allows the SDK application to access the `zrok` service instance and the underlying OpenZiti network. + +Next, `copyto` will create a `zrok` share: + +```go + shr, err := sdk.CreateShare(root, &sdk.ShareRequest{ + BackendMode: sdk.TcpTunnelBackendMode, + ShareMode: sdk.PrivateShareMode, + Target: "pastebin", + }) + if err != nil { + panic(err) + } + + fmt.Printf("access your pastebin using 'pastefrom %v'\n", shr.Token) +``` + +The `sdk.CreateShare` call uses the loaded `environment` root along with the details of the share request (`sdk.ShareRequest`) to create the share that will be used to access the `pastebin`. + +For the `pastebin` application, we're using a `sdk.TcpTunnelBackendMode` backend mode (we're just using a single network connection that implements a reliable byte stream, so TCP works great). Tunnel backends only work with `private` shares as of `zrok` `v0.4`, so we're using `sdk.PrivateShareMode`. + +We'll set the `Target` to be `pastebin`, as that's just metadata describing the application. + +Finally, we emit the share token so the user can access the `pastebin` using the `pastefrom` command. + +Next, we'll use the SDK to create a listener for this share: + +```go + listener, err := sdk.NewListener(shr.Token, root) + if err != nil { + panic(err) + } +``` + +The `sdk.NewListener` establishes a network listener for the newly created share. This listener works just like a `net.Listener`. + +Next, we're going to add a shutdown hook so that `copyto` will delete the share when the application is terminated using `^C`: + +```go + c := make(chan os.Signal) + signal.Notify(c, os.Interrupt, syscall.SIGTERM) + go func() { + <-c + if err := sdk.DeleteShare(root, shr); err != nil { + panic(err) + } + _ = listener.Close() + os.Exit(0) + }() +``` + +This anonymous function runs waiting for a signal to exit. When that is received, it runs the `sdk.DeleteShare` function to remove the share that was created. This is how ephemeral shares work for the `zrok share` commands as well. + +And finally, we run in an infinite loop waiting for requests for the `pastebin` data from the network: + +```go + for { + if conn, err := listener.Accept(); err == nil { + go handle(conn, data) + } else { + panic(err) + } + } +``` + +## The "pastefrom" Implementation + +The `pastefrom` application works very similarly to `copyto`. The primary difference is that it "dials" the share through the SDK using `sdk.NewDialer`, which returns a `net.Conn`: + +```go + conn, err := sdk.NewDialer(shrToken, root) + if err != nil { + panic(err) + } +``` + +When this `sdk.NewDialer` function returns without an error, a bidirectional `net.Conn` has been established between the `copyto` "server" and the `pastefrom` "client". `pastefrom` then just reads the available data from the `net.Conn` and emits it to `os.Stdout`. \ No newline at end of file diff --git a/sdk/examples/pastebin/cmd/copyto/main.go b/sdk/examples/pastebin/cmd/copyto/main.go new file mode 100644 index 000000000..6c129b995 --- /dev/null +++ b/sdk/examples/pastebin/cmd/copyto/main.go @@ -0,0 +1,86 @@ +package main + +import ( + "errors" + "fmt" + "github.com/michaelquigley/pfxlog" + "github.com/openziti/zrok/environment" + "github.com/openziti/zrok/sdk" + "github.com/sirupsen/logrus" + "io" + "net" + "os" + "os/signal" + "syscall" +) + +func init() { + pfxlog.GlobalInit(logrus.WarnLevel, pfxlog.DefaultOptions()) +} + +func main() { + data, err := loadData() + if err != nil { + panic(err) + } + + root, err := environment.LoadRoot() + if err != nil { + panic(err) + } + + shr, err := sdk.CreateShare(root, &sdk.ShareRequest{ + BackendMode: sdk.TcpTunnelBackendMode, + ShareMode: sdk.PrivateShareMode, + Target: "pastebin", + }) + if err != nil { + panic(err) + } + + fmt.Printf("access your pastebin using 'pastefrom %v'\n", shr.Token) + + listener, err := sdk.NewListener(shr.Token, root) + if err != nil { + panic(err) + } + + c := make(chan os.Signal) + signal.Notify(c, os.Interrupt, syscall.SIGTERM) + go func() { + <-c + if err := sdk.DeleteShare(root, shr); err != nil { + panic(err) + } + _ = listener.Close() + os.Exit(0) + }() + + for { + if conn, err := listener.Accept(); err == nil { + go handle(conn, data) + } else { + panic(err) + } + } +} + +func loadData() ([]byte, error) { + stat, _ := os.Stdin.Stat() + if stat.Mode()&os.ModeCharDevice == 0 { + data, err := io.ReadAll(os.Stdin) + if err != nil { + return nil, err + } + return data, nil + } else { + return nil, errors.New("'copyto' requires input from stdin; direct your paste buffer into stdin") + } +} + +func handle(conn net.Conn, data []byte) { + _, err := conn.Write(data) + if err != nil { + fmt.Printf("error: %v\n", err) + } +} diff --git a/sdk/examples/pastebin/cmd/pastefrom/main.go b/sdk/examples/pastebin/cmd/pastefrom/main.go new file mode 100644 index 000000000..56ba4e1a7 --- /dev/null +++ b/sdk/examples/pastebin/cmd/pastefrom/main.go @@ -0,0 +1,48 @@ +package main + +import ( + "fmt" + "github.com/openziti/zrok/environment" + "github.com/openziti/zrok/sdk" + "os" +) + +const MAX_PASTE_SIZE = 64 * 1024 + +func main() { + if len(os.Args) < 2 { + panic("usage: pastefrom ") + } + shrToken := os.Args[1] + + root, err := environment.LoadRoot() + if err != nil { + panic(err) + } + + acc, err := sdk.CreateAccess(root, &sdk.AccessRequest{ShareToken: shrToken}) + if err != nil { + panic(err) + } + defer func() { + if err := sdk.DeleteAccess(root, acc); err != nil { + panic(err) + } + }() + + conn, err := sdk.NewDialer(shrToken, root) + if err != nil { + panic(err) + } + defer func() { + _ = conn.Close() + }() + + buf := make([]byte, MAX_PASTE_SIZE) + n, err := conn.Read(buf) + if err != nil { + panic(err) + } + + fmt.Printf(string(buf[:n])) +} diff --git a/sdk/listener.go b/sdk/listener.go new file mode 100644 index 000000000..4aece922f --- /dev/null +++ b/sdk/listener.go @@ -0,0 +1,37 @@ +package sdk + +import ( + "github.com/openziti/sdk-golang/ziti" + "github.com/openziti/sdk-golang/ziti/edge" + "github.com/openziti/zrok/environment/env_core" + "github.com/pkg/errors" + "time" +) + +func NewListener(shrToken string, root env_core.Root) (edge.Listener, error) { + return NewListenerWithOptions(shrToken, root, &ziti.ListenOptions{ConnectTimeout: 30 * time.Second, MaxConnections: 64}) +} + +func NewListenerWithOptions(shrToken string, root env_core.Root, opts *ziti.ListenOptions) (edge.Listener, error) { + zif, err := root.ZitiIdentityNamed(root.EnvironmentIdentityName()) + if err != nil { + return nil, errors.Wrap(err, "error getting ziti identity path") + } + + zcfg, err := ziti.NewConfigFromFile(zif) + if err != nil { + return nil, errors.Wrap(err, "error loading ziti identity") + } + + zctx, err := ziti.NewContext(zcfg) + if err != nil { + return nil, errors.Wrap(err, "error getting ziti context") + } + + listener, err := zctx.ListenWithOptions(shrToken, opts) + if err != nil { + return nil, errors.Wrap(err, "error creating listener") + } + + return listener, nil +} diff --git a/sdk/model.go b/sdk/model.go new file mode 100644 index 000000000..1cf9a2486 --- /dev/null +++ b/sdk/model.go @@ -0,0 +1,57 @@ +package sdk + +type BackendMode string + +const ( + ProxyBackendMode BackendMode = "proxy" + WebBackendMode BackendMode = "web" + TcpTunnelBackendMode BackendMode = "tcpTunnel" + UdpTunnelBackendMode BackendMode = "udpTunnel" +) + +type ShareMode string + +const ( + PrivateShareMode ShareMode = "private" + PublicShareMode ShareMode = "public" +) + +type ShareRequest struct { + BackendMode BackendMode + ShareMode ShareMode + Frontends []string + Auth []string + Target string +} + +type Share struct { + Token string +} + +type AccessRequest struct { + ShareToken string +} + +type Access struct { + Token string + ShareToken string + BackendMode BackendMode +} + +type Metrics struct { + Namespace string + Sessions map[string]SessionMetrics +} + +type SessionMetrics struct { + BytesRead int64 + BytesWritten int64 + LastUpdate int64 +} + +type AuthScheme string + +const ( + None AuthScheme = "none" + Basic AuthScheme = "basic" +) diff --git a/sdk/share.go b/sdk/share.go new file mode 100644 index 000000000..2f843e145 --- /dev/null +++ b/sdk/share.go @@ -0,0 +1,99 @@ +package sdk + +import ( + httptransport "github.com/go-openapi/runtime/client" + "github.com/openziti/zrok/environment/env_core" + "github.com/openziti/zrok/rest_client_zrok/share" + "github.com/openziti/zrok/rest_model_zrok" + "github.com/pkg/errors" + "strings" +) + +func CreateShare(root env_core.Root, request *ShareRequest) (*Share, error) { + if !root.IsEnabled() { + return nil, errors.New("environment is not enabled; enable with 'zrok enable' first!") + } + + var err error + var out *share.ShareParams + + switch request.ShareMode { + case PrivateShareMode: + out = newPrivateShare(root, request) + case PublicShareMode: + out = newPublicShare(root, request) + default: + return nil, errors.Errorf("unknown share mode '%v'", request.ShareMode) + } + + if len(request.Auth) > 0 { + out.Body.AuthScheme = string(Basic) + for _, pair := range request.Auth { + tokens := strings.Split(pair, ":") + if len(tokens) == 2 { + out.Body.AuthUsers = append(out.Body.AuthUsers, &rest_model_zrok.AuthUser{Username: strings.TrimSpace(tokens[0]), Password: strings.TrimSpace(tokens[1])}) + } else { + return nil, errors.Errorf("invalid username:password pair '%v'", pair) + } + } + } + + zrok, err := root.Client() + if err != nil { + return nil, errors.Wrap(err, "error getting zrok client") + } + auth := httptransport.APIKeyAuth("X-TOKEN", "header", root.Environment().Token) + + in, err := zrok.Share.Share(out, auth) + if err != nil { + return nil, errors.Wrap(err, "unable to create share") + } + + return &Share{Token: in.Payload.ShrToken}, nil +} + +func newPrivateShare(root env_core.Root, request *ShareRequest) *share.ShareParams { + req := share.NewShareParams() + req.Body = &rest_model_zrok.ShareRequest{ + EnvZID: root.Environment().ZitiIdentity, + ShareMode: string(request.ShareMode), + BackendMode: string(request.BackendMode), + BackendProxyEndpoint: request.Target, + AuthScheme: string(None), + } + return req +} + +func newPublicShare(root env_core.Root, request *ShareRequest) *share.ShareParams { + req := share.NewShareParams() + req.Body = &rest_model_zrok.ShareRequest{ + EnvZID: root.Environment().ZitiIdentity, + ShareMode: string(request.ShareMode), + FrontendSelection: request.Frontends, + BackendMode: string(request.BackendMode), + BackendProxyEndpoint: request.Target, + AuthScheme: string(None), + } + return req +} + +func DeleteShare(root env_core.Root, shr *Share) error { + req := share.NewUnshareParams() + req.Body = &rest_model_zrok.UnshareRequest{ + EnvZID: root.Environment().ZitiIdentity, + ShrToken: shr.Token, + } + + zrok, err := root.Client() + if err != nil { + return errors.Wrap(err, "error getting zrok client") + } + auth := httptransport.APIKeyAuth("X-TOKEN", "header", root.Environment().Token) + + _, err = zrok.Share.Unshare(req, auth) + if err != nil { + return errors.Wrap(err, "error deleting share") + } + + return nil +} diff --git a/zrokdir/client.go b/zrokdir/client.go deleted file mode 100644 index 2f41b47b0..000000000 --- a/zrokdir/client.go +++ /dev/null @@ -1,61 +0,0 @@ -package zrokdir - -import ( - "github.com/go-openapi/runtime" - httptransport "github.com/go-openapi/runtime/client" - "github.com/go-openapi/strfmt" - "github.com/openziti/zrok/build" - "github.com/openziti/zrok/rest_client_zrok" - "github.com/pkg/errors" - "net/url" - "os" - "regexp" -) - -func (zrd *ZrokDir) Client() (*rest_client_zrok.Zrok, error) { - apiEndpoint, _ := zrd.ApiEndpoint() - apiUrl, err := url.Parse(apiEndpoint) - if err != nil { - return nil, errors.Wrapf(err, "error parsing api endpoint '%v'", zrd) - } - transport := httptransport.New(apiUrl.Host, "/api/v1", []string{apiUrl.Scheme}) - transport.Producers["application/zrok.v1+json"] = runtime.JSONProducer() - transport.Consumers["application/zrok.v1+json"] = runtime.JSONConsumer() - - zrok := rest_client_zrok.New(transport, strfmt.Default) - v, err := zrok.Metadata.Version(nil) - if err != nil { - return nil, errors.Wrapf(err, "error getting version from api endpoint '%v': %v", apiEndpoint, err) - } - // allow reported version string to be optionally prefixed with - // "refs/heads/" or "refs/tags/" - re := regexp.MustCompile(`^(refs/(heads|tags)/)?` + build.Series) - if ! re.MatchString(string(v.Payload)) { - return nil, errors.Errorf("expected a '%v' version, received: '%v'", build.Series, v.Payload) - } - - return zrok, nil -} - -func (zrd *ZrokDir) ApiEndpoint() (apiEndpoint string, from string) { - apiEndpoint = "https://api.zrok.io" - from = "binary" - - if zrd.Cfg != nil && zrd.Cfg.ApiEndpoint != "" { - apiEndpoint = zrd.Cfg.ApiEndpoint - from = "config" - } - - env := os.Getenv("ZROK_API_ENDPOINT") - if env != "" { - apiEndpoint = env - from = "ZROK_API_ENDPOINT" - } - - if zrd.Env != nil && zrd.Env.ApiEndpoint != "" { - apiEndpoint = zrd.Env.ApiEndpoint - from = "env" - } - - return apiEndpoint, from -} diff --git a/zrokdir/config.go b/zrokdir/config.go deleted file mode 100644 index d8e77cfdc..000000000 --- a/zrokdir/config.go +++ /dev/null @@ -1,61 +0,0 @@ -package zrokdir - -import ( - "encoding/json" - "github.com/pkg/errors" - "os" - "path/filepath" -) - -type Config struct { - ApiEndpoint string `json:"api_endpoint"` -} - -func hasConfig() (bool, error) { - cf, err := configFile() - if err != nil { - return false, errors.Wrap(err, "error getting config file path") - } - _, err = os.Stat(cf) - if os.IsNotExist(err) { - return false, nil - } - if err != nil { - return false, errors.Wrapf(err, "error stat-ing config file '%v'", cf) - } - return true, nil -} - -func loadConfig() (*Config, error) { - cf, err := configFile() - if err != nil { - return nil, errors.Wrap(err, "error getting config file path") - } - data, err := os.ReadFile(cf) - if err != nil { - return nil, errors.Wrapf(err, "error reading config file '%v'", cf) - } - cfg := &Config{} - if err := json.Unmarshal(data, cfg); err != nil { - return nil, errors.Wrapf(err, "error unmarshaling config file '%v'", cf) - } - return cfg, nil -} - -func saveConfig(cfg *Config) error { - data, err := json.MarshalIndent(cfg, "", " ") - if err != nil { - return errors.Wrap(err, "error marshaling config") - } - cf, err := configFile() - if err != nil { - return errors.Wrap(err, "error getting config file path") - } - if err := os.MkdirAll(filepath.Dir(cf), os.FileMode(0700)); err != nil { - return errors.Wrapf(err, "error creating zrokdir path '%v'", filepath.Dir(cf)) - } - if err := os.WriteFile(cf, data, os.FileMode(0600)); err != nil { - return errors.Wrap(err, "error saving config file") - } - return nil -} diff --git a/zrokdir/environment.go b/zrokdir/environment.go deleted file mode 100644 index 56b502276..000000000 --- a/zrokdir/environment.go +++ /dev/null @@ -1,75 +0,0 @@ -package zrokdir - -import ( - "encoding/json" - "github.com/pkg/errors" - "os" - "path/filepath" -) - -type Environment struct { - Token string `json:"zrok_token"` - ZId string `json:"ziti_identity"` - ApiEndpoint string `json:"api_endpoint"` -} - -func hasEnvironment() (bool, error) { - ef, err := environmentFile() - if err != nil { - return false, errors.Wrap(err, "error getting environment file path") - } - _, err = os.Stat(ef) - if os.IsNotExist(err) { - return false, nil - } - if err != nil { - return false, errors.Wrapf(err, "error stat-ing environment file '%v'", ef) - } - return true, nil -} - -func loadEnvironment() (*Environment, error) { - ef, err := environmentFile() - if err != nil { - return nil, errors.Wrap(err, "error getting environment file") - } - data, err := os.ReadFile(ef) - if err != nil { - return nil, errors.Wrapf(err, "error reading environment file '%v'", ef) - } - env := &Environment{} - if err := json.Unmarshal(data, env); err != nil { - return nil, errors.Wrapf(err, "error unmarshaling environment file '%v'", ef) - } - return env, nil -} - -func saveEnvironment(env *Environment) error { - data, err := json.MarshalIndent(env, "", " ") - if err != nil { - return errors.Wrap(err, "error marshaling environment") - } - ef, err := environmentFile() - if err != nil { - return errors.Wrap(err, "error getting environment file") - } - if err := os.MkdirAll(filepath.Dir(ef), os.FileMode(0700)); err != nil { - return errors.Wrapf(err, "error creating zrokdir path '%v'", filepath.Dir(ef)) - } - if err := os.WriteFile(ef, data, os.FileMode(0600)); err != nil { - return errors.Wrap(err, "error saving environment file") - } - return nil -} - -func DeleteEnvironment() error { - ef, err := environmentFile() - if err != nil { - return errors.Wrap(err, "error getting environment file") - } - if err := os.Remove(ef); err != nil { - return errors.Wrap(err, "error removing environment file") - } - - return nil -} diff --git a/zrokdir/identity.go b/zrokdir/identity.go deleted file mode 100644 index 4c912ebc0..000000000 --- a/zrokdir/identity.go +++ /dev/null @@ -1,36 +0,0 @@ -package zrokdir - -import ( - "github.com/pkg/errors" - "os" - "path/filepath" -) - -func ZitiIdentityFile(name string) (string, error) { - return identityFile(name) -} - -func SaveZitiIdentity(name, data string) error { - zif, err := ZitiIdentityFile(name) - if err != nil { - return err - } - if err := os.MkdirAll(filepath.Dir(zif), os.FileMode(0700)); err != nil { - return errors.Wrapf(err, "error creating zrokdir path '%v'", filepath.Dir(zif)) - } - if err := os.WriteFile(zif, []byte(data), os.FileMode(0600)); err != nil { - return errors.Wrapf(err, "error writing ziti identity file '%v'", zif) - } - return nil -} - -func DeleteZitiIdentity(name string) error { - zif, err := ZitiIdentityFile(name) - if err != nil { - return errors.Wrapf(err, "error getting ziti identity file path for '%v'", name) - } - if err := os.Remove(zif); err != nil { - return errors.Wrapf(err, "error removing ziti identity file '%v'", zif) - } - return nil -} diff --git a/zrokdir/version.go b/zrokdir/version.go deleted file mode 100644 index e073aa99f..000000000 --- a/zrokdir/version.go +++ /dev/null @@ -1,53 +0,0 @@ -package zrokdir - -import ( - "encoding/json" - "github.com/openziti/zrok/tui" - "github.com/pkg/errors" - "os" - "path/filepath" -) - -const V = "v0.3" - -type Metadata struct { - V string `json:"v"` -} - -func checkMetadata() error { - mf, err := metadataFile() - if err != nil { - return err - } - data, err := os.ReadFile(mf) - if err != nil { - tui.Warning("unable to open zrokdir metadata; ignoring\n") - return nil - } - m := &Metadata{} - if err := json.Unmarshal(data, m); err != nil { - return errors.Wrapf(err, "error unmarshaling metadata file '%v'", mf) - } - if m.V != V { - return errors.Errorf("invalid zrokdir metadata version '%v'", m.V) - } - return nil -} - -func writeMetadata() error { - mf, err := metadataFile() - if err != nil { - return err - } - data, err := json.Marshal(&Metadata{V: V}) - if err != nil { - return err - } - if err := os.MkdirAll(filepath.Dir(mf), os.FileMode(0700)); err != nil { - return err - } - if err := os.WriteFile(mf, data, os.FileMode(0600)); err != nil { - return err - } - return nil -} diff --git a/zrokdir/zrokdir.go b/zrokdir/zrokdir.go deleted file mode 100644 index 9c8d0c710..000000000 --- a/zrokdir/zrokdir.go +++ /dev/null @@ -1,179 +0,0 @@ -package zrokdir - -import ( - "fmt" - "github.com/pkg/errors" - "os" - "path/filepath" - "strings" -) - -type ZrokDir struct { - Env *Environment - Cfg *Config - identities map[string]struct{} -} - -func Initialize() (*ZrokDir, error) { - zrd, err := zrokDir() - if err != nil { - return nil, errors.Wrap(err, "error getting zrokdir path") - } - if err := os.MkdirAll(zrd, os.FileMode(0700)); err != nil { - return nil, errors.Wrapf(err, "error creating zrokdir root path '%v'", zrd) - } - if err := DeleteEnvironment(); err != nil { - return nil, errors.Wrap(err, "error deleting environment") - } - idd, err := identitiesDir() - if err != nil { - return nil, errors.Wrap(err, "error getting zrokdir identities path") - } - if err := os.MkdirAll(idd, os.FileMode(0700)); err != nil { - return nil, errors.Wrapf(err, "error creating zrokdir identities root path '%v'", idd) - } - return Load() -} - -func Load() (*ZrokDir, error) { - if err := checkMetadata(); err != nil { - return nil, err - } - - zrd := &ZrokDir{} - - ids, err := listIdentities() - if err != nil { - return nil, err - } - zrd.identities = ids - - hasCfg, err := hasConfig() - if err != nil { - return nil, err - } - if hasCfg { - cfg, err := loadConfig() - if err != nil { - return nil, err - } - zrd.Cfg = cfg - } - - hasEnv, err := hasEnvironment() - if err != nil { - return nil, err - } - if hasEnv { - env, err := loadEnvironment() - if err != nil { - return nil, err - } - zrd.Env = env - } - - return zrd, nil -} - -func (zrd *ZrokDir) Save() error { - if err := writeMetadata(); err != nil { - return errors.Wrap(err, "error saving metadata") - } - if zrd.Env != nil { - if err := saveEnvironment(zrd.Env); err != nil { - return errors.Wrap(err, "error saving environment") - } - } - if zrd.Cfg != nil { - if err := saveConfig(zrd.Cfg); err != nil { - return errors.Wrap(err, "error saving config") - } - } - return nil -} - -func Obliterate() error { - zrd, err := zrokDir() - if err != nil { - return err - } - if err := os.RemoveAll(zrd); err != nil { - return err - } - return nil -} - -func listIdentities() (map[string]struct{}, error) { - ids := make(map[string]struct{}) - - idd, err := identitiesDir() - if err != nil { - return nil, errors.Wrap(err, "error getting zrokdir identities path") - } - _, err = os.Stat(idd) - if os.IsNotExist(err) { - return ids, nil - } - if err != nil { - return nil, errors.Wrapf(err, "error stat-ing zrokdir identities root '%v'", idd) - } - des, err := os.ReadDir(idd) - if err != nil { - return nil, errors.Wrapf(err, "error listing zrokdir identities from '%v'", idd) - } - for _, de := range des { - if strings.HasSuffix(de.Name(), ".json") && !de.IsDir() { - name := strings.TrimSuffix(de.Name(), ".json") - ids[name] = struct{}{} - } - } - return ids, nil -} - -func configFile() (string, error) { - zrd, err := zrokDir() - if err != nil { - return "", err - } - return filepath.Join(zrd, "config.json"), nil -} - -func environmentFile() (string, error) { - zrd, err := zrokDir() - if err != nil { - return "", err - } - return filepath.Join(zrd, "environment.json"), nil -} - -func identityFile(name string) (string, error) { - idd, err := identitiesDir() - if err != nil { - return "", err - } - return filepath.Join(idd, fmt.Sprintf("%v.json", name)), nil -} - -func identitiesDir() (string, error) { - zrd, err := zrokDir() - if err != nil { - return "", err - } - return filepath.Join(zrd, "identities"), nil -} - -func metadataFile() (string, error) { - zrd, err := zrokDir() - if err != nil { - return "", err - } - return filepath.Join(zrd, "metadata.json"), nil -} - -func zrokDir() (string, error) { - home, err := os.UserHomeDir() - if err != nil { - return "", err - } - return filepath.Join(home, ".zrok"), nil -}