mirror of
https://github.com/openziti/zrok.git
synced 2024-11-21 23:53:19 +01:00
commit
0d43b5566b
39
CHANGELOG.md
39
CHANGELOG.md
@ -1,3 +1,42 @@
|
||||
# v0.3.0 (WiP)
|
||||
|
||||
## CLI/zrok Client Changes
|
||||
|
||||
### Versioning
|
||||
|
||||
The `zrok` client now checks the version of the configured API endpoint before attempting to connect. This means a `zrok` client will only work with the same major/minor versions.
|
||||
|
||||
This means that a `v0.3` client will NOT work with a `v0.2` service. This also means that breaking API changes will require a minor revision change. A breaking change made in `v0.3` will provoke a new `v0.4` series to begin.
|
||||
|
||||
## API Changes
|
||||
|
||||
Naming has been streamlined:
|
||||
|
||||
* The `tunnel` operations are all tagged with `service`.
|
||||
* `tunnel.Tunnel` becomes `service.Share`
|
||||
* `tunnel.Untunnel` becomes `service.Unshare`
|
||||
* `TunnelRequest` and `TunnelResponse` become `ShareRequest` and `ShareResponse`
|
||||
* `UntunnelRequest` becomes `UnshareRequest`.
|
||||
|
||||
Sharing now includes the new mode options:
|
||||
|
||||
* `ShareRequest` now includes a `ShareMode` enum which includes `public` and `private` values
|
||||
* `ShareRequest` now includes a `BackendMode` enum which includes `proxy`, `web`, and `dav` values
|
||||
|
||||
## Frontend Selection; Private Shares
|
||||
|
||||
The `zrok` model has been extended to include support for both a "public share" (exposing a backend through the globally-available `frontend` instances), and also a "private share" (exposing a backend service to a user who instantiates a private, local `frontend`).
|
||||
|
||||
### Underlying Schema Changes
|
||||
|
||||
* Added new `frontends` table
|
||||
* Added new `availability_type` enumeration for use in the new `frontends` table
|
||||
* Made the `account_id` column of the `environments` table `NULL`-able; a `NULL` value in the `account_id` column signifies an "ephemeral" environment
|
||||
|
||||
## Loop Test Shutdown Hook
|
||||
|
||||
The `zrok test loop` command now includes a shutdown hook to allow premature cancellation of a running test.
|
||||
|
||||
# v0.2.18
|
||||
|
||||
* DEFECT: Token generation has been improved to use an alphabet consisting of `[a-zA-Z0-9]`. Service token generation continues to use a case-insensitive alphabet consisting of `[a-z0-9]` to be DNS-safe.
|
||||
|
@ -26,3 +26,5 @@ swagger generate client -P rest_model_zrok.Principal -f "$zrokSpec" -c rest_clie
|
||||
|
||||
echo "...generating js client"
|
||||
openapi -s specs/zrok.yml -o ui/src/api -l js
|
||||
|
||||
git checkout rest_server_zrok/configure_zrok.go
|
||||
|
@ -1,21 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ctrl-01.zrok.io
|
||||
ssh -i ~/.ssh/nf-zrok-ubuntu ctrl-01.zrok.io sudo systemctl stop zrok-ctrl
|
||||
scp -i ~/.ssh/nf-zrok-ubuntu ~/local/zrok/bin/zrok ctrl-01.zrok.io:local/zrok/bin/zrok
|
||||
ssh -i ~/.ssh/nf-zrok-ubuntu ctrl-01.zrok.io sudo systemctl start zrok-ctrl
|
||||
|
||||
# ctrl-02.zrok.io
|
||||
ssh -i ~/.ssh/nf-zrok-ubuntu ctrl-02.zrok.io sudo systemctl stop zrok-ctrl
|
||||
scp -i ~/.ssh/nf-zrok-ubuntu ~/local/zrok/bin/zrok ctrl-02.zrok.io:local/zrok/bin/zrok
|
||||
ssh -i ~/.ssh/nf-zrok-ubuntu ctrl-02.zrok.io sudo systemctl start zrok-ctrl
|
||||
|
||||
# in-01.zrok.io
|
||||
ssh -i ~/.ssh/nf-zrok-ubuntu in-01.zrok.io sudo systemctl stop zrok-http-frontend
|
||||
scp -i ~/.ssh/nf-zrok-ubuntu ~/local/zrok/bin/zrok in-01.zrok.io:local/zrok/bin/zrok
|
||||
ssh -i ~/.ssh/nf-zrok-ubuntu in-01.zrok.io sudo systemctl start zrok-http-frontend
|
||||
|
||||
# in-02.zrok.io
|
||||
ssh -i ~/.ssh/nf-zrok-ubuntu in-02.zrok.io sudo systemctl stop zrok-http-frontend
|
||||
scp -i ~/.ssh/nf-zrok-ubuntu ~/local/zrok/bin/zrok in-02.zrok.io:local/zrok/bin/zrok
|
||||
ssh -i ~/.ssh/nf-zrok-ubuntu in-02.zrok.io sudo systemctl start zrok-http-frontend
|
@ -5,10 +5,12 @@ import "fmt"
|
||||
var Version string
|
||||
var Hash string
|
||||
|
||||
const Series = "v0.3"
|
||||
|
||||
func String() string {
|
||||
if Version != "" {
|
||||
return fmt.Sprintf("%v [%v]", Version, Hash)
|
||||
} else {
|
||||
return "<developer_build>"
|
||||
return Series + ".x [developer build]"
|
||||
}
|
||||
}
|
||||
|
152
cmd/zrok/accessPrivate.go
Normal file
152
cmd/zrok/accessPrivate.go
Normal file
@ -0,0 +1,152 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/go-openapi/runtime"
|
||||
httptransport "github.com/go-openapi/runtime/client"
|
||||
"github.com/openziti-test-kitchen/zrok/endpoints"
|
||||
"github.com/openziti-test-kitchen/zrok/endpoints/privateFrontend"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_client_zrok"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_client_zrok/share"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_model_zrok"
|
||||
"github.com/openziti-test-kitchen/zrok/tui"
|
||||
"github.com/openziti-test-kitchen/zrok/zrokdir"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func init() {
|
||||
accessCmd.AddCommand(newAccessPrivateCommand().cmd)
|
||||
}
|
||||
|
||||
type accessPrivateCommand struct {
|
||||
cmd *cobra.Command
|
||||
bindAddress string
|
||||
}
|
||||
|
||||
func newAccessPrivateCommand() *accessPrivateCommand {
|
||||
cmd := &cobra.Command{
|
||||
Use: "private <shareToken>",
|
||||
Short: "Create a private frontend to access a share",
|
||||
Args: cobra.ExactArgs(1),
|
||||
}
|
||||
command := &accessPrivateCommand{cmd: cmd}
|
||||
cmd.Run = command.run
|
||||
cmd.Flags().StringVarP(&command.bindAddress, "bind", "b", "127.0.0.1:9191", "The address to bind the private frontend")
|
||||
return command
|
||||
}
|
||||
|
||||
func (cmd *accessPrivateCommand) run(_ *cobra.Command, args []string) {
|
||||
shrToken := args[0]
|
||||
|
||||
endpointUrl, err := url.Parse("http://" + cmd.bindAddress)
|
||||
if err != nil {
|
||||
if !panicInstead {
|
||||
tui.Error("invalid endpoint address", err)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
|
||||
zrd, err := zrokdir.Load()
|
||||
if err != nil {
|
||||
tui.Error("unable to load zrokdir", err)
|
||||
}
|
||||
|
||||
if zrd.Env == nil {
|
||||
tui.Error("unable to load environment; did you 'zrok enable'?", nil)
|
||||
}
|
||||
|
||||
zrok, err := zrd.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)
|
||||
req := share.NewAccessParams()
|
||||
req.Body = &rest_model_zrok.AccessRequest{
|
||||
ShrToken: shrToken,
|
||||
EnvZID: zrd.Env.ZId,
|
||||
}
|
||||
accessResp, err := zrok.Share.Access(req, auth)
|
||||
if err != nil {
|
||||
if !panicInstead {
|
||||
tui.Error("unable to access", err)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
logrus.Infof("allocated frontend '%v'", accessResp.Payload.FrontendToken)
|
||||
|
||||
cfg := privateFrontend.DefaultConfig("backend")
|
||||
cfg.ShrToken = shrToken
|
||||
cfg.Address = cmd.bindAddress
|
||||
cfg.RequestsChan = make(chan *endpoints.Request, 1024)
|
||||
|
||||
c := make(chan os.Signal)
|
||||
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
|
||||
go func() {
|
||||
<-c
|
||||
cmd.destroy(accessResp.Payload.FrontendToken, zrd.Env.ZId, shrToken, zrok, auth)
|
||||
os.Exit(0)
|
||||
}()
|
||||
|
||||
frontend, err := privateFrontend.NewHTTP(cfg)
|
||||
if err != nil {
|
||||
if !panicInstead {
|
||||
tui.Error("unable to create private frontend", err)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
|
||||
go func() {
|
||||
if err := frontend.Run(); err != nil {
|
||||
if !panicInstead {
|
||||
tui.Error("unable to run frontend", err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
mdl := newAccessModel(shrToken, endpointUrl.String())
|
||||
logrus.SetOutput(mdl)
|
||||
prg := tea.NewProgram(mdl, tea.WithAltScreen())
|
||||
mdl.prg = prg
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case req := <-cfg.RequestsChan:
|
||||
if req != nil {
|
||||
prg.Send(req)
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
if _, err := prg.Run(); err != nil {
|
||||
tui.Error("An error occurred", err)
|
||||
}
|
||||
|
||||
close(cfg.RequestsChan)
|
||||
cmd.destroy(accessResp.Payload.FrontendToken, zrd.Env.ZId, shrToken, zrok, auth)
|
||||
}
|
||||
|
||||
func (cmd *accessPrivateCommand) destroy(frotendName, envZId, shrToken string, zrok *rest_client_zrok.Zrok, auth runtime.ClientAuthInfoWriter) {
|
||||
logrus.Debugf("shutting down '%v'", shrToken)
|
||||
req := share.NewUnaccessParams()
|
||||
req.Body = &rest_model_zrok.UnaccessRequest{
|
||||
FrontendToken: frotendName,
|
||||
ShrToken: shrToken,
|
||||
EnvZID: envZId,
|
||||
}
|
||||
if _, err := zrok.Share.Unaccess(req, auth); err == nil {
|
||||
logrus.Debugf("shutdown complete")
|
||||
} else {
|
||||
logrus.Errorf("error shutting down: %v", err)
|
||||
}
|
||||
}
|
56
cmd/zrok/accessPublic.go
Normal file
56
cmd/zrok/accessPublic.go
Normal file
@ -0,0 +1,56 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/michaelquigley/cf"
|
||||
"github.com/openziti-test-kitchen/zrok/endpoints/publicFrontend"
|
||||
"github.com/openziti-test-kitchen/zrok/tui"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func init() {
|
||||
accessCmd.AddCommand(newAccessPublicCommand().cmd)
|
||||
}
|
||||
|
||||
type accessPublicCommand struct {
|
||||
cmd *cobra.Command
|
||||
}
|
||||
|
||||
func newAccessPublicCommand() *accessPublicCommand {
|
||||
cmd := &cobra.Command{
|
||||
Use: "public [<configPath>]",
|
||||
Aliases: []string{"fe"},
|
||||
Short: "Create a public access HTTP frontend",
|
||||
Args: cobra.RangeArgs(0, 1),
|
||||
}
|
||||
command := &accessPublicCommand{cmd: cmd}
|
||||
cmd.Run = command.run
|
||||
return command
|
||||
}
|
||||
|
||||
func (self *accessPublicCommand) run(_ *cobra.Command, args []string) {
|
||||
cfg := publicFrontend.DefaultConfig()
|
||||
if len(args) == 1 {
|
||||
if err := cfg.Load(args[0]); err != nil {
|
||||
if !panicInstead {
|
||||
tui.Error(fmt.Sprintf("unable to load configuration '%v'", args[0]), err)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
logrus.Infof(cf.Dump(cfg, cf.DefaultOptions()))
|
||||
frontend, err := publicFrontend.NewHTTP(cfg)
|
||||
if err != nil {
|
||||
if !panicInstead {
|
||||
tui.Error("unable to create http frontend", err)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
if err := frontend.Run(); err != nil {
|
||||
if !panicInstead {
|
||||
tui.Error("unable to run http frontend", err)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
}
|
202
cmd/zrok/accessTui.go
Normal file
202
cmd/zrok/accessTui.go
Normal file
@ -0,0 +1,202 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/muesli/reflow/wordwrap"
|
||||
"github.com/openziti-test-kitchen/zrok/endpoints"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const accessTuiBacklog = 256
|
||||
|
||||
type accessModel struct {
|
||||
shrToken string
|
||||
localEndpoint string
|
||||
requests []*endpoints.Request
|
||||
log []string
|
||||
showLog bool
|
||||
width int
|
||||
height int
|
||||
prg *tea.Program
|
||||
}
|
||||
|
||||
type accessLogLine string
|
||||
|
||||
func newAccessModel(shrToken, localEndpoint string) *accessModel {
|
||||
return &accessModel{
|
||||
shrToken: shrToken,
|
||||
localEndpoint: localEndpoint,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *accessModel) Init() tea.Cmd { return nil }
|
||||
|
||||
func (m *accessModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
switch msg := msg.(type) {
|
||||
case *endpoints.Request:
|
||||
m.requests = append(m.requests, msg)
|
||||
if len(m.requests) > accessTuiBacklog {
|
||||
m.requests = m.requests[1:]
|
||||
}
|
||||
|
||||
case accessLogLine:
|
||||
m.showLog = true
|
||||
m.adjustPaneHeights()
|
||||
|
||||
m.log = append(m.log, string(msg))
|
||||
if len(m.log) > accessTuiBacklog {
|
||||
m.log = m.log[1:]
|
||||
}
|
||||
|
||||
case tea.WindowSizeMsg:
|
||||
m.width = msg.Width
|
||||
accessHeaderStyle.Width(m.width - 2)
|
||||
accessRequestsStyle.Width(m.width - 2)
|
||||
accessLogStyle.Width(m.width - 2)
|
||||
|
||||
m.height = msg.Height
|
||||
m.adjustPaneHeights()
|
||||
|
||||
case tea.KeyMsg:
|
||||
switch msg.String() {
|
||||
case "ctrl+c", "q":
|
||||
return m, tea.Quit
|
||||
case "ctrl+l":
|
||||
return m, tea.ClearScreen
|
||||
case "l":
|
||||
m.showLog = !m.showLog
|
||||
m.adjustPaneHeights()
|
||||
}
|
||||
}
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (m *accessModel) View() string {
|
||||
var panes string
|
||||
if m.showLog {
|
||||
panes = lipgloss.JoinVertical(lipgloss.Left,
|
||||
accessRequestsStyle.Render(m.renderRequests()),
|
||||
accessLogStyle.Render(m.renderLog()),
|
||||
)
|
||||
} else {
|
||||
panes = accessRequestsStyle.Render(m.renderRequests())
|
||||
}
|
||||
|
||||
return lipgloss.JoinVertical(
|
||||
lipgloss.Left,
|
||||
accessHeaderStyle.Render(fmt.Sprintf("%v -> %v", m.localEndpoint, m.shrToken)),
|
||||
panes,
|
||||
)
|
||||
}
|
||||
|
||||
func (m *accessModel) adjustPaneHeights() {
|
||||
if !m.showLog {
|
||||
accessRequestsStyle.Height(m.height - 5)
|
||||
} else {
|
||||
splitHeight := m.height - 5
|
||||
accessRequestsStyle.Height(splitHeight/2 - 1)
|
||||
accessLogStyle.Height(splitHeight/2 - 1)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *accessModel) renderRequests() string {
|
||||
var requestLines []string
|
||||
for _, req := range m.requests {
|
||||
reqLine := fmt.Sprintf("%v %v -> %v %v",
|
||||
timeStyle.Render(req.Stamp.Format(time.RFC850)),
|
||||
addressStyle.Render(req.RemoteAddr),
|
||||
m.renderMethod(req.Method),
|
||||
req.Path,
|
||||
)
|
||||
reqLineWrapped := wordwrap.String(reqLine, m.width-2)
|
||||
splitWrapped := strings.Split(reqLineWrapped, "\n")
|
||||
for _, splitLine := range splitWrapped {
|
||||
splitLine := strings.ReplaceAll(splitLine, "\n", "")
|
||||
if splitLine != "" {
|
||||
requestLines = append(requestLines, splitLine)
|
||||
}
|
||||
}
|
||||
}
|
||||
maxRows := accessRequestsStyle.GetHeight()
|
||||
startRow := 0
|
||||
if len(requestLines) > maxRows {
|
||||
startRow = len(requestLines) - maxRows
|
||||
}
|
||||
out := ""
|
||||
for i := startRow; i < len(requestLines); i++ {
|
||||
outLine := requestLines[i]
|
||||
if i < len(requestLines)-1 {
|
||||
outLine += "\n"
|
||||
}
|
||||
out += outLine
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (m *accessModel) renderMethod(method string) string {
|
||||
switch strings.ToLower(method) {
|
||||
case "get":
|
||||
return getStyle.Render(method)
|
||||
case "post":
|
||||
return postStyle.Render(method)
|
||||
default:
|
||||
return otherMethodStyle.Render(method)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *accessModel) renderLog() string {
|
||||
var splitLines []string
|
||||
for _, line := range m.log {
|
||||
wrapped := wordwrap.String(line, m.width-2)
|
||||
wrappedLines := strings.Split(wrapped, "\n")
|
||||
for _, wrappedLine := range wrappedLines {
|
||||
splitLine := strings.ReplaceAll(wrappedLine, "\n", "")
|
||||
if splitLine != "" {
|
||||
splitLines = append(splitLines, splitLine)
|
||||
}
|
||||
}
|
||||
}
|
||||
maxRows := accessLogStyle.GetHeight()
|
||||
startRow := 0
|
||||
if len(splitLines) > maxRows {
|
||||
startRow = len(splitLines) - maxRows
|
||||
}
|
||||
out := ""
|
||||
for i := startRow; i < len(splitLines); i++ {
|
||||
outLine := splitLines[i]
|
||||
if i < len(splitLines)-1 {
|
||||
outLine += "\n"
|
||||
}
|
||||
out += outLine
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (m *accessModel) Write(p []byte) (n int, err error) {
|
||||
in := string(p)
|
||||
lines := strings.Split(in, "\n")
|
||||
for _, line := range lines {
|
||||
cleanLine := strings.ReplaceAll(line, "\n", "")
|
||||
if cleanLine != "" {
|
||||
m.prg.Send(accessLogLine(cleanLine))
|
||||
}
|
||||
}
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
var accessHeaderStyle = lipgloss.NewStyle().
|
||||
BorderStyle(lipgloss.RoundedBorder()).
|
||||
BorderForeground(lipgloss.Color("63")).
|
||||
Align(lipgloss.Center)
|
||||
|
||||
var accessRequestsStyle = lipgloss.NewStyle().
|
||||
BorderStyle(lipgloss.RoundedBorder()).
|
||||
BorderForeground(lipgloss.Color("63"))
|
||||
|
||||
var accessLogStyle = lipgloss.NewStyle().
|
||||
BorderStyle(lipgloss.RoundedBorder()).
|
||||
BorderForeground(lipgloss.Color("63"))
|
44
cmd/zrok/adminBootstrap.go
Normal file
44
cmd/zrok/adminBootstrap.go
Normal file
@ -0,0 +1,44 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/michaelquigley/cf"
|
||||
"github.com/openziti-test-kitchen/zrok/controller"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func init() {
|
||||
adminCmd.AddCommand(newAdminBootstrap().cmd)
|
||||
}
|
||||
|
||||
type adminBootstrap struct {
|
||||
cmd *cobra.Command
|
||||
skipCtrl bool
|
||||
skipFrontend bool
|
||||
}
|
||||
|
||||
func newAdminBootstrap() *adminBootstrap {
|
||||
cmd := &cobra.Command{
|
||||
Use: "bootstrap <configPath>",
|
||||
Short: "Bootstrap the underlying Ziti network for zrok",
|
||||
Args: cobra.ExactArgs(1),
|
||||
}
|
||||
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, "Slip frontend identity bootstrapping")
|
||||
return command
|
||||
}
|
||||
|
||||
func (cmd *adminBootstrap) run(_ *cobra.Command, args []string) {
|
||||
configPath := args[0]
|
||||
inCfg, err := controller.LoadConfig(configPath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
logrus.Infof(cf.Dump(inCfg, cf.DefaultOptions()))
|
||||
if err := controller.Bootstrap(cmd.skipCtrl, cmd.skipFrontend, inCfg); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
logrus.Info("bootstrap complete!")
|
||||
}
|
59
cmd/zrok/adminCreateFrontend.go
Normal file
59
cmd/zrok/adminCreateFrontend.go
Normal file
@ -0,0 +1,59 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/openziti-test-kitchen/zrok/rest_client_zrok/admin"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_model_zrok"
|
||||
"github.com/openziti-test-kitchen/zrok/zrokdir"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func init() {
|
||||
adminCreateCmd.AddCommand(newAdminCreateFrontendCommand().cmd)
|
||||
}
|
||||
|
||||
type adminCreateFrontendCommand struct {
|
||||
cmd *cobra.Command
|
||||
}
|
||||
|
||||
func newAdminCreateFrontendCommand() *adminCreateFrontendCommand {
|
||||
cmd := &cobra.Command{
|
||||
Use: "frontend <zitiId> <publicName> <urlTemplate>",
|
||||
Aliases: []string{"fe"},
|
||||
Short: "Create a global public frontend",
|
||||
Args: cobra.ExactArgs(3),
|
||||
}
|
||||
command := &adminCreateFrontendCommand{cmd: cmd}
|
||||
cmd.Run = command.run
|
||||
return command
|
||||
}
|
||||
|
||||
func (cmd *adminCreateFrontendCommand) run(_ *cobra.Command, args []string) {
|
||||
zId := args[0]
|
||||
publicName := args[1]
|
||||
urlTemplate := args[2]
|
||||
|
||||
zrd, err := zrokdir.Load()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
zrok, err := zrd.Client()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
req := admin.NewCreateFrontendParams()
|
||||
req.Body = &rest_model_zrok.CreateFrontendRequest{
|
||||
ZID: zId,
|
||||
PublicName: publicName,
|
||||
URLTemplate: urlTemplate,
|
||||
}
|
||||
|
||||
resp, err := zrok.Admin.CreateFrontend(req, mustGetAdminAuth())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
logrus.Infof("created global public frontend '%v'", resp.Payload.Token)
|
||||
}
|
67
cmd/zrok/adminCreateIdentity.go
Normal file
67
cmd/zrok/adminCreateIdentity.go
Normal file
@ -0,0 +1,67 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_client_zrok/admin"
|
||||
"github.com/openziti-test-kitchen/zrok/zrokdir"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"os"
|
||||
)
|
||||
|
||||
func init() {
|
||||
adminCreateCmd.AddCommand(newAdminCreateIdentity().cmd)
|
||||
}
|
||||
|
||||
type adminCreateIdentity struct {
|
||||
cmd *cobra.Command
|
||||
}
|
||||
|
||||
func newAdminCreateIdentity() *adminCreateIdentity {
|
||||
cmd := &cobra.Command{
|
||||
Use: "identity <name>",
|
||||
Aliases: []string{"id"},
|
||||
Short: "Create an identity and policies for a public frontend",
|
||||
Args: cobra.ExactArgs(1),
|
||||
}
|
||||
command := &adminCreateIdentity{cmd: cmd}
|
||||
cmd.Run = command.run
|
||||
return command
|
||||
}
|
||||
|
||||
func (cmd *adminCreateIdentity) run(_ *cobra.Command, args []string) {
|
||||
name := args[0]
|
||||
|
||||
zif, err := zrokdir.ZitiIdentityFile(name)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if _, err := os.Stat(zif); err == nil {
|
||||
logrus.Errorf("identity '%v' already exists at '%v'", name, zif)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
zrd, err := zrokdir.Load()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
zrok, err := zrd.Client()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
req := admin.NewCreateIdentityParams()
|
||||
req.Body.Name = name
|
||||
|
||||
resp, err := zrok.Admin.CreateIdentity(req, mustGetAdminAuth())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if err := zrokdir.SaveZitiIdentity(name, resp.Payload.Cfg); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Printf("zrok identity '%v' created with ziti id '%v'\n", name, resp.Payload.Identity)
|
||||
}
|
53
cmd/zrok/adminDeleteFrontend.go
Normal file
53
cmd/zrok/adminDeleteFrontend.go
Normal file
@ -0,0 +1,53 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/openziti-test-kitchen/zrok/rest_client_zrok/admin"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_model_zrok"
|
||||
"github.com/openziti-test-kitchen/zrok/zrokdir"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func init() {
|
||||
adminDeleteCmd.AddCommand(newAdminDeleteFrontendCommand().cmd)
|
||||
}
|
||||
|
||||
type adminDeleteFrontendCommand struct {
|
||||
cmd *cobra.Command
|
||||
}
|
||||
|
||||
func newAdminDeleteFrontendCommand() *adminDeleteFrontendCommand {
|
||||
cmd := &cobra.Command{
|
||||
Use: "frontend <frontendToken>",
|
||||
Aliases: []string{"fe"},
|
||||
Short: "Delete a global public frontend",
|
||||
Args: cobra.ExactArgs(1),
|
||||
}
|
||||
command := &adminDeleteFrontendCommand{cmd: cmd}
|
||||
cmd.Run = command.run
|
||||
return command
|
||||
}
|
||||
|
||||
func (cmd *adminDeleteFrontendCommand) run(_ *cobra.Command, args []string) {
|
||||
feToken := args[0]
|
||||
|
||||
zrd, err := zrokdir.Load()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
zrok, err := zrd.Client()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
req := admin.NewDeleteFrontendParams()
|
||||
req.Body = &rest_model_zrok.DeleteFrontendRequest{FrontendToken: feToken}
|
||||
|
||||
_, err = zrok.Admin.DeleteFrontend(req, mustGetAdminAuth())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
logrus.Infof("deleted global frontend '%v'", feToken)
|
||||
}
|
@ -8,25 +8,25 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(newGcCmd().cmd)
|
||||
adminCmd.AddCommand(newAdminGcCommand().cmd)
|
||||
}
|
||||
|
||||
type gcCmd struct {
|
||||
type adminGcCommand struct {
|
||||
cmd *cobra.Command
|
||||
}
|
||||
|
||||
func newGcCmd() *gcCmd {
|
||||
func newAdminGcCommand() *adminGcCommand {
|
||||
cmd := &cobra.Command{
|
||||
Use: "gc <configPath>",
|
||||
Short: "Garbage collect a zrok instance",
|
||||
Args: cobra.ExactArgs(1),
|
||||
}
|
||||
c := &gcCmd{cmd: cmd}
|
||||
cmd.Run = c.run
|
||||
return c
|
||||
command := &adminGcCommand{cmd: cmd}
|
||||
cmd.Run = command.run
|
||||
return command
|
||||
}
|
||||
|
||||
func (gc *gcCmd) run(_ *cobra.Command, args []string) {
|
||||
func (gc *adminGcCommand) run(_ *cobra.Command, args []string) {
|
||||
cfg, err := controller.LoadConfig(args[0])
|
||||
if err != nil {
|
||||
panic(err)
|
79
cmd/zrok/adminGenerate.go
Normal file
79
cmd/zrok/adminGenerate.go
Normal file
@ -0,0 +1,79 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/jaevor/go-nanoid"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_client_zrok/admin"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_model_zrok"
|
||||
"github.com/openziti-test-kitchen/zrok/zrokdir"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func init() {
|
||||
adminCmd.AddCommand(newAdminGenerateCommand().cmd)
|
||||
}
|
||||
|
||||
type adminGenerateCommand struct {
|
||||
cmd *cobra.Command
|
||||
amount int
|
||||
}
|
||||
|
||||
func newAdminGenerateCommand() *adminGenerateCommand {
|
||||
cmd := &cobra.Command{
|
||||
Use: "generate",
|
||||
Short: "Generate invite tokens (default: 5)",
|
||||
Args: cobra.ExactArgs(0),
|
||||
}
|
||||
command := &adminGenerateCommand{cmd: cmd}
|
||||
cmd.Run = command.run
|
||||
|
||||
cmd.Flags().IntVar(&command.amount, "amount", 5, "Amount of tokens to generate")
|
||||
|
||||
return command
|
||||
}
|
||||
|
||||
func (cmd *adminGenerateCommand) run(_ *cobra.Command, args []string) {
|
||||
var err error
|
||||
tokens := make([]string, cmd.amount)
|
||||
for i := 0; i < int(cmd.amount); i++ {
|
||||
tokens[i], err = createToken()
|
||||
if err != nil {
|
||||
logrus.Error("error creating token", err)
|
||||
}
|
||||
}
|
||||
|
||||
zrd, err := zrokdir.Load()
|
||||
if err != nil {
|
||||
logrus.Error("error loading zrokdir", err)
|
||||
}
|
||||
|
||||
zrok, err := zrd.Client()
|
||||
if err != nil {
|
||||
if !panicInstead {
|
||||
logrus.Error("error creating zrok api client", err)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
req := admin.NewInviteTokenGenerateParams()
|
||||
req.Body = &rest_model_zrok.InviteTokenGenerateRequest{
|
||||
Tokens: tokens,
|
||||
}
|
||||
_, err = zrok.Admin.InviteTokenGenerate(req, mustGetAdminAuth())
|
||||
if err != nil {
|
||||
if !panicInstead {
|
||||
logrus.Error("error creating invite tokens", err)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
fmt.Printf("generated %d tokens\n", len(tokens))
|
||||
}
|
||||
|
||||
func createToken() (string, error) {
|
||||
gen, err := nanoid.CustomASCII("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", 12)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return gen(), nil
|
||||
}
|
67
cmd/zrok/adminListFrontends.go
Normal file
67
cmd/zrok/adminListFrontends.go
Normal file
@ -0,0 +1,67 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_client_zrok/admin"
|
||||
"github.com/openziti-test-kitchen/zrok/zrokdir"
|
||||
"github.com/spf13/cobra"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
func init() {
|
||||
adminListCmd.AddCommand(newAdminListFrontendsCommand().cmd)
|
||||
}
|
||||
|
||||
type adminListFrontendsCommand struct {
|
||||
cmd *cobra.Command
|
||||
}
|
||||
|
||||
func newAdminListFrontendsCommand() *adminListFrontendsCommand {
|
||||
cmd := &cobra.Command{
|
||||
Use: "frontends",
|
||||
Aliases: []string{"fes"},
|
||||
Short: "List global public frontends",
|
||||
Args: cobra.ExactArgs(0),
|
||||
}
|
||||
command := &adminListFrontendsCommand{cmd: cmd}
|
||||
cmd.Run = command.run
|
||||
return command
|
||||
}
|
||||
|
||||
func (cmd *adminListFrontendsCommand) run(_ *cobra.Command, _ []string) {
|
||||
zrd, err := zrokdir.Load()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
zrok, err := zrd.Client()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
req := admin.NewListFrontendsParams()
|
||||
resp, err := zrok.Admin.ListFrontends(req, mustGetAdminAuth())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
t := table.NewWriter()
|
||||
t.SetOutputMirror(os.Stdout)
|
||||
t.SetStyle(table.StyleColoredDark)
|
||||
t.AppendHeader(table.Row{"Token", "zId", "Public Name", "Url Template", "Created At", "Updated At"})
|
||||
for _, pfe := range resp.Payload {
|
||||
t.AppendRow(table.Row{
|
||||
pfe.Token,
|
||||
pfe.ZID,
|
||||
pfe.PublicName,
|
||||
pfe.URLTemplate,
|
||||
time.UnixMilli(pfe.CreatedAt),
|
||||
time.UnixMilli(pfe.UpdatedAt),
|
||||
})
|
||||
}
|
||||
t.Render()
|
||||
fmt.Println()
|
||||
}
|
64
cmd/zrok/adminUpdateFrontend.go
Normal file
64
cmd/zrok/adminUpdateFrontend.go
Normal file
@ -0,0 +1,64 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/openziti-test-kitchen/zrok/rest_client_zrok/admin"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_model_zrok"
|
||||
"github.com/openziti-test-kitchen/zrok/zrokdir"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func init() {
|
||||
adminUpdateCmd.AddCommand(newAdminUpdateFrontendCommand().cmd)
|
||||
}
|
||||
|
||||
type adminUpdateFrontendCommand struct {
|
||||
cmd *cobra.Command
|
||||
newPublicName string
|
||||
newUrlTemplate string
|
||||
}
|
||||
|
||||
func newAdminUpdateFrontendCommand() *adminUpdateFrontendCommand {
|
||||
cmd := &cobra.Command{
|
||||
Use: "frontend <frontendToken>",
|
||||
Aliases: []string{"fe"},
|
||||
Short: "Update a global public frontend",
|
||||
Args: cobra.ExactArgs(1),
|
||||
}
|
||||
command := &adminUpdateFrontendCommand{cmd: cmd}
|
||||
cmd.Flags().StringVar(&command.newPublicName, "public-name", "", "Specify a new value for the public name")
|
||||
cmd.Flags().StringVar(&command.newUrlTemplate, "url-template", "", "Specify a new value for the url template")
|
||||
cmd.Run = command.run
|
||||
return command
|
||||
}
|
||||
func (cmd *adminUpdateFrontendCommand) run(_ *cobra.Command, args []string) {
|
||||
feToken := args[0]
|
||||
|
||||
if cmd.newPublicName == "" && cmd.newUrlTemplate == "" {
|
||||
panic("must specify at least one of public name or url template")
|
||||
}
|
||||
|
||||
zrd, err := zrokdir.Load()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
zrok, err := zrd.Client()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
req := admin.NewUpdateFrontendParams()
|
||||
req.Body = &rest_model_zrok.UpdateFrontendRequest{
|
||||
FrontendToken: feToken,
|
||||
PublicName: cmd.newPublicName,
|
||||
URLTemplate: cmd.newUrlTemplate,
|
||||
}
|
||||
|
||||
_, err = zrok.Admin.UpdateFrontend(req, mustGetAdminAuth())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
logrus.Infof("updated global frontend '%v'", feToken)
|
||||
}
|
46
cmd/zrok/configGet.go
Normal file
46
cmd/zrok/configGet.go
Normal file
@ -0,0 +1,46 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/openziti-test-kitchen/zrok/zrokdir"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func init() {
|
||||
configCmd.AddCommand(newConfigGetCommand().cmd)
|
||||
}
|
||||
|
||||
type configGetCommand struct {
|
||||
cmd *cobra.Command
|
||||
}
|
||||
|
||||
func newConfigGetCommand() *configGetCommand {
|
||||
cmd := &cobra.Command{
|
||||
Use: "get <configName>",
|
||||
Short: "Get a value from the environment config",
|
||||
Args: cobra.ExactArgs(1),
|
||||
}
|
||||
command := &configGetCommand{cmd: cmd}
|
||||
cmd.Run = command.run
|
||||
return command
|
||||
}
|
||||
|
||||
func (cmd *configGetCommand) run(_ *cobra.Command, args []string) {
|
||||
configName := args[0]
|
||||
|
||||
zrd, err := zrokdir.Load()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
switch configName {
|
||||
case "apiEndpoint":
|
||||
if zrd.Cfg != nil && zrd.Cfg.ApiEndpoint != "" {
|
||||
fmt.Printf("apiEndpoint = %v\n", zrd.Cfg.ApiEndpoint)
|
||||
} else {
|
||||
fmt.Println("apiEndpoint = <unset>")
|
||||
}
|
||||
default:
|
||||
fmt.Printf("unknown config name '%v'\n", configName)
|
||||
}
|
||||
}
|
58
cmd/zrok/configSet.go
Normal file
58
cmd/zrok/configSet.go
Normal file
@ -0,0 +1,58 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/openziti-test-kitchen/zrok/zrokdir"
|
||||
"github.com/spf13/cobra"
|
||||
"os"
|
||||
)
|
||||
|
||||
func init() {
|
||||
configCmd.AddCommand(newConfigSetCommand().cmd)
|
||||
}
|
||||
|
||||
type configSetCommand struct {
|
||||
cmd *cobra.Command
|
||||
}
|
||||
|
||||
func newConfigSetCommand() *configSetCommand {
|
||||
cmd := &cobra.Command{
|
||||
Use: "set <configName> <value>",
|
||||
Short: "Set a value into the environment config",
|
||||
Args: cobra.ExactArgs(2),
|
||||
}
|
||||
command := &configSetCommand{cmd: cmd}
|
||||
cmd.Run = command.run
|
||||
return command
|
||||
}
|
||||
|
||||
func (cmd *configSetCommand) run(_ *cobra.Command, args []string) {
|
||||
configName := args[0]
|
||||
value := args[1]
|
||||
|
||||
zrd, err := zrokdir.Load()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
modified := false
|
||||
switch configName {
|
||||
case "apiEndpoint":
|
||||
if zrd.Cfg == nil {
|
||||
zrd.Cfg = &zrokdir.Config{}
|
||||
}
|
||||
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)
|
||||
}
|
||||
fmt.Println("zrok configuration updated")
|
||||
}
|
||||
}
|
@ -3,8 +3,9 @@ package main
|
||||
import (
|
||||
"fmt"
|
||||
httptransport "github.com/go-openapi/runtime/client"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_client_zrok/identity"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_client_zrok/environment"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_model_zrok"
|
||||
"github.com/openziti-test-kitchen/zrok/tui"
|
||||
"github.com/openziti-test-kitchen/zrok/zrokdir"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
@ -29,40 +30,45 @@ func newDisableCommand() *disableCommand {
|
||||
return command
|
||||
}
|
||||
|
||||
func (cmd *disableCommand) run(_ *cobra.Command, args []string) {
|
||||
env, err := zrokdir.LoadEnvironment()
|
||||
func (cmd *disableCommand) run(_ *cobra.Command, _ []string) {
|
||||
zrd, err := zrokdir.Load()
|
||||
if err != nil {
|
||||
if !panicInstead {
|
||||
showError("could not load environment; not active?", err)
|
||||
tui.Error("unable to load zrokdir", err)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
zrok, err := zrokdir.ZrokClient(env.ApiEndpoint)
|
||||
|
||||
if zrd.Env == nil {
|
||||
tui.Error("no environment found; nothing to disable!", nil)
|
||||
}
|
||||
|
||||
zrok, err := zrd.Client()
|
||||
if err != nil {
|
||||
if !panicInstead {
|
||||
showError("could not create zrok service client", err)
|
||||
tui.Error("could not create zrok client", err)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
auth := httptransport.APIKeyAuth("X-TOKEN", "header", env.Token)
|
||||
req := identity.NewDisableParams()
|
||||
auth := httptransport.APIKeyAuth("X-TOKEN", "header", zrd.Env.Token)
|
||||
req := environment.NewDisableParams()
|
||||
req.Body = &rest_model_zrok.DisableRequest{
|
||||
Identity: env.ZId,
|
||||
Identity: zrd.Env.ZId,
|
||||
}
|
||||
_, err = zrok.Identity.Disable(req, auth)
|
||||
_, err = zrok.Environment.Disable(req, auth)
|
||||
if err != nil {
|
||||
logrus.Warnf("service cleanup failed (%v); will clean up local environment", err)
|
||||
logrus.Warnf("share cleanup failed (%v); will clean up local environment", err)
|
||||
}
|
||||
if err := zrokdir.DeleteEnvironment(); err != nil {
|
||||
if !panicInstead {
|
||||
showError("error removing zrok environment", err)
|
||||
tui.Error("error removing zrok environment", err)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
if err := zrokdir.DeleteZitiIdentity("backend"); err != nil {
|
||||
if !panicInstead {
|
||||
showError("error removing zrok backend identity", err)
|
||||
tui.Error("error removing zrok backend identity", err)
|
||||
}
|
||||
}
|
||||
fmt.Printf("zrok environment '%v' disabled for '%v'\n", env.ZId, env.Token)
|
||||
fmt.Println("zrok environment disabled...")
|
||||
}
|
||||
|
@ -2,16 +2,18 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/charmbracelet/bubbles/spinner"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
httptransport "github.com/go-openapi/runtime/client"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_client_zrok/identity"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_client_zrok/environment"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_model_zrok"
|
||||
"github.com/openziti-test-kitchen/zrok/tui"
|
||||
"github.com/openziti-test-kitchen/zrok/zrokdir"
|
||||
"github.com/shirou/gopsutil/v3/host"
|
||||
"github.com/spf13/cobra"
|
||||
"os"
|
||||
user2 "os/user"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@ -36,11 +38,10 @@ func newEnableCommand() *enableCommand {
|
||||
}
|
||||
|
||||
func (cmd *enableCommand) run(_ *cobra.Command, args []string) {
|
||||
env, err := zrokdir.LoadEnvironment()
|
||||
if err == nil {
|
||||
showError(fmt.Sprintf("you already have an environment '%v' for '%v'", env.ZId, env.Token), nil)
|
||||
zrd, err := zrokdir.Load()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
token := args[0]
|
||||
|
||||
hostName, hostDetail, err := getHost()
|
||||
@ -55,38 +56,70 @@ func (cmd *enableCommand) run(_ *cobra.Command, args []string) {
|
||||
if cmd.description == "<user>@<hostname>" {
|
||||
cmd.description = fmt.Sprintf("%v@%v", user.Username, hostName)
|
||||
}
|
||||
|
||||
zrok, err := zrokdir.ZrokClient(apiEndpoint)
|
||||
zrok, err := zrd.Client()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
cmd.endpointError(zrd.ApiEndpoint())
|
||||
tui.Error("error creating service client", err)
|
||||
}
|
||||
auth := httptransport.APIKeyAuth("X-TOKEN", "header", token)
|
||||
req := identity.NewEnableParams()
|
||||
req := environment.NewEnableParams()
|
||||
req.Body = &rest_model_zrok.EnableRequest{
|
||||
Description: cmd.description,
|
||||
Host: hostDetail,
|
||||
}
|
||||
resp, err := zrok.Identity.Enable(req, auth)
|
||||
|
||||
var prg *tea.Program
|
||||
var mdl enableTuiModel
|
||||
var done = make(chan struct{})
|
||||
go func() {
|
||||
mdl = newEnableTuiModel()
|
||||
mdl.msg = "contacting the zrok service..."
|
||||
prg = tea.NewProgram(mdl)
|
||||
if _, err := prg.Run(); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
close(done)
|
||||
if mdl.quitting {
|
||||
os.Exit(1)
|
||||
}
|
||||
}()
|
||||
|
||||
resp, err := zrok.Environment.Enable(req, auth)
|
||||
if err != nil {
|
||||
if !panicInstead {
|
||||
showError("the zrok service returned an error", err)
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
prg.Send(fmt.Sprintf("the zrok service returned an error: %v\n", err))
|
||||
prg.Quit()
|
||||
<-done
|
||||
cmd.endpointError(zrd.ApiEndpoint())
|
||||
os.Exit(1)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
if err := zrokdir.SaveEnvironment(&zrokdir.Environment{Token: token, ZId: resp.Payload.Identity, ApiEndpoint: apiEndpoint}); err != nil {
|
||||
if !panicInstead {
|
||||
showError("there was an error saving the new environment", err)
|
||||
}
|
||||
panic(err)
|
||||
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 {
|
||||
prg.Send(fmt.Sprintf("there was an error saving the new environment: %v", err))
|
||||
prg.Quit()
|
||||
<-done
|
||||
os.Exit(1)
|
||||
}
|
||||
if err := zrokdir.SaveZitiIdentity("backend", resp.Payload.Cfg); err != nil {
|
||||
if !panicInstead {
|
||||
showError("there was an error writing the environment file", err)
|
||||
}
|
||||
panic(err)
|
||||
prg.Send(fmt.Sprintf("there was an error writing the environment: %v", err))
|
||||
prg.Quit()
|
||||
<-done
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("zrok environment '%v' enabled for '%v'\n", resp.Payload.Identity, token)
|
||||
prg.Send(fmt.Sprintf("the zrok environment was successfully enabled..."))
|
||||
prg.Quit()
|
||||
<-done
|
||||
}
|
||||
|
||||
func (cmd *enableCommand) endpointError(apiEndpoint, _ string) {
|
||||
fmt.Printf("%v\n\n", tui.ErrorStyle.Render("there was a problem enabling your environment!"))
|
||||
fmt.Printf("you are trying to use the zrok service at: %v\n\n", tui.CodeStyle.Render(apiEndpoint))
|
||||
fmt.Printf("you can change your zrok service endpoint using this command:\n\n")
|
||||
fmt.Printf("%v\n\n", tui.CodeStyle.Render("$ zrok config set apiEndpoint <newEndpoint>"))
|
||||
fmt.Printf("(where newEndpoint is something like: %v)\n\n", tui.CodeStyle.Render("https://some.zrok.io"))
|
||||
}
|
||||
|
||||
func getHost() (string, string, error) {
|
||||
@ -99,14 +132,51 @@ func getHost() (string, string, error) {
|
||||
return info.Hostname, thisHost, nil
|
||||
}
|
||||
|
||||
var errorStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#D90166")).Background(lipgloss.Color("0"))
|
||||
type enableTuiModel struct {
|
||||
spinner spinner.Model
|
||||
msg string
|
||||
quitting bool
|
||||
}
|
||||
|
||||
func showError(msg string, err error) {
|
||||
errorLabel := errorStyle.Render("ERROR:")
|
||||
if err != nil {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "%v %v (%v)\n", errorLabel, msg, strings.TrimSpace(err.Error()))
|
||||
} else {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "%v %v\n", errorLabel, msg)
|
||||
func newEnableTuiModel() enableTuiModel {
|
||||
s := spinner.New()
|
||||
s.Spinner = spinner.Dot
|
||||
s.Style = tui.WarningStyle
|
||||
return enableTuiModel{spinner: s}
|
||||
}
|
||||
os.Exit(1)
|
||||
|
||||
func (m enableTuiModel) Init() tea.Cmd { return m.spinner.Tick }
|
||||
|
||||
func (m enableTuiModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
switch msg := msg.(type) {
|
||||
case string:
|
||||
m.msg = msg
|
||||
return m, nil
|
||||
|
||||
case tea.KeyMsg:
|
||||
switch msg.String() {
|
||||
case "q", "esc", "ctrl+c":
|
||||
m.quitting = true
|
||||
return m, tea.Quit
|
||||
|
||||
default:
|
||||
return m, nil
|
||||
}
|
||||
|
||||
case struct{}:
|
||||
return m, tea.Quit
|
||||
|
||||
default:
|
||||
var cmd tea.Cmd
|
||||
m.spinner, cmd = m.spinner.Update(msg)
|
||||
return m, cmd
|
||||
}
|
||||
}
|
||||
|
||||
func (m enableTuiModel) View() string {
|
||||
str := fmt.Sprintf("%s %s\n", m.spinner.View(), m.msg)
|
||||
if m.quitting {
|
||||
return str
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
package endpoint_ui
|
||||
package endpointUi
|
||||
|
||||
import "embed"
|
||||
|
@ -388,6 +388,8 @@
|
||||
</div>
|
||||
<div id="container">
|
||||
<div id="info">
|
||||
<h1>{{ .RequestedPath }}</h1>
|
||||
|
||||
<h2>{{ .Now }}</h2>
|
||||
|
||||
<h3>At This Endpoint:</h3>
|
@ -1,238 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
ui "github.com/gizak/termui/v3"
|
||||
"github.com/gizak/termui/v3/widgets"
|
||||
"github.com/go-openapi/runtime"
|
||||
httptransport "github.com/go-openapi/runtime/client"
|
||||
tb "github.com/nsf/termbox-go"
|
||||
"github.com/openziti-test-kitchen/zrok/endpoints/backend"
|
||||
"github.com/openziti-test-kitchen/zrok/model"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_client_zrok"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_client_zrok/tunnel"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_model_zrok"
|
||||
"github.com/openziti-test-kitchen/zrok/zrokdir"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
func init() {
|
||||
httpCmd.AddCommand(newHttpBackendCommand().cmd)
|
||||
}
|
||||
|
||||
type httpBackendCommand struct {
|
||||
quiet bool
|
||||
basicAuth []string
|
||||
cmd *cobra.Command
|
||||
}
|
||||
|
||||
func newHttpBackendCommand() *httpBackendCommand {
|
||||
cmd := &cobra.Command{
|
||||
Use: "backend <targetEndpoint>",
|
||||
Aliases: []string{"be"},
|
||||
Short: "Create an HTTP binding",
|
||||
Args: cobra.ExactArgs(1),
|
||||
}
|
||||
command := &httpBackendCommand{cmd: cmd}
|
||||
cmd.Flags().BoolVarP(&command.quiet, "quiet", "q", false, "Disable TUI 'chrome' for quiet operation")
|
||||
cmd.Flags().StringArrayVar(&command.basicAuth, "basic-auth", []string{}, "Basic authentication users (<username:password>,...")
|
||||
cmd.Run = command.run
|
||||
return command
|
||||
}
|
||||
|
||||
func (self *httpBackendCommand) run(_ *cobra.Command, args []string) {
|
||||
targetEndpoint, err := url.Parse(args[0])
|
||||
if err != nil {
|
||||
if !panicInstead {
|
||||
showError("invalid target endpoint URL", err)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
if targetEndpoint.Scheme == "" {
|
||||
targetEndpoint.Scheme = "https"
|
||||
}
|
||||
|
||||
if !self.quiet {
|
||||
if err := ui.Init(); err != nil {
|
||||
if !panicInstead {
|
||||
showError("unable to initialize user interface", err)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
defer ui.Close()
|
||||
tb.SetInputMode(tb.InputEsc)
|
||||
}
|
||||
|
||||
env, err := zrokdir.LoadEnvironment()
|
||||
if err != nil {
|
||||
ui.Close()
|
||||
if !panicInstead {
|
||||
showError("unable to load environment; did you 'zrok enable'?", err)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
zif, err := zrokdir.ZitiIdentityFile("backend")
|
||||
if err != nil {
|
||||
ui.Close()
|
||||
if !panicInstead {
|
||||
showError("unable to load ziti identity configuration", err)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
cfg := &backend.Config{
|
||||
IdentityPath: zif,
|
||||
EndpointAddress: targetEndpoint.String(),
|
||||
}
|
||||
|
||||
zrok, err := zrokdir.ZrokClient(env.ApiEndpoint)
|
||||
if err != nil {
|
||||
ui.Close()
|
||||
if !panicInstead {
|
||||
showError("unable to create zrok client", err)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
auth := httptransport.APIKeyAuth("X-TOKEN", "header", env.Token)
|
||||
req := tunnel.NewTunnelParams()
|
||||
req.Body = &rest_model_zrok.TunnelRequest{
|
||||
ZID: env.ZId,
|
||||
Endpoint: cfg.EndpointAddress,
|
||||
AuthScheme: string(model.None),
|
||||
}
|
||||
if len(self.basicAuth) > 0 {
|
||||
logrus.Infof("configuring basic auth")
|
||||
req.Body.AuthScheme = string(model.Basic)
|
||||
for _, pair := range self.basicAuth {
|
||||
tokens := strings.Split(pair, ":")
|
||||
if len(tokens) == 2 {
|
||||
req.Body.AuthUsers = append(req.Body.AuthUsers, &rest_model_zrok.AuthUser{Username: strings.TrimSpace(tokens[0]), Password: strings.TrimSpace(tokens[1])})
|
||||
} else {
|
||||
panic(errors.Errorf("invalid username:password pair '%v'", pair))
|
||||
}
|
||||
}
|
||||
}
|
||||
resp, err := zrok.Tunnel.Tunnel(req, auth)
|
||||
if err != nil {
|
||||
ui.Close()
|
||||
if !panicInstead {
|
||||
showError("unable to create tunnel", err)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
cfg.Service = resp.Payload.SvcName
|
||||
|
||||
c := make(chan os.Signal)
|
||||
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
|
||||
go func() {
|
||||
<-c
|
||||
self.destroy(env.ZId, cfg, zrok, auth)
|
||||
os.Exit(0)
|
||||
}()
|
||||
|
||||
httpProxy, err := backend.NewHTTP(cfg)
|
||||
if err != nil {
|
||||
ui.Close()
|
||||
if !panicInstead {
|
||||
showError("unable to create http backend", err)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
|
||||
go func() {
|
||||
if err := httpProxy.Run(); err != nil {
|
||||
if !panicInstead {
|
||||
showError("unable to run http proxy", err)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
}()
|
||||
|
||||
if !self.quiet {
|
||||
ui.Clear()
|
||||
w, h := ui.TerminalDimensions()
|
||||
|
||||
p := widgets.NewParagraph()
|
||||
p.Border = true
|
||||
p.Title = " access your zrok service "
|
||||
p.Text = fmt.Sprintf("%v%v", strings.Repeat(" ", (((w-12)-len(resp.Payload.ProxyEndpoint))/2)-1), resp.Payload.ProxyEndpoint)
|
||||
p.TextStyle = ui.Style{Fg: ui.ColorWhite}
|
||||
p.PaddingTop = 1
|
||||
p.SetRect(5, 5, w-10, 10)
|
||||
|
||||
lastRequests := float64(0)
|
||||
var requestData []float64
|
||||
spk := widgets.NewSparkline()
|
||||
spk.Title = " requests "
|
||||
spk.Data = requestData
|
||||
spk.LineColor = ui.ColorCyan
|
||||
|
||||
slg := widgets.NewSparklineGroup(spk)
|
||||
slg.SetRect(5, 11, w-10, h-5)
|
||||
|
||||
ui.Render(p, slg)
|
||||
|
||||
ticker := time.NewTicker(time.Second).C
|
||||
uiEvents := ui.PollEvents()
|
||||
for {
|
||||
select {
|
||||
case e := <-uiEvents:
|
||||
switch e.Type {
|
||||
case ui.ResizeEvent:
|
||||
ui.Clear()
|
||||
w, h = ui.TerminalDimensions()
|
||||
p.SetRect(5, 5, w-10, 10)
|
||||
slg.SetRect(5, 11, w-10, h-5)
|
||||
ui.Render(p, slg)
|
||||
|
||||
case ui.KeyboardEvent:
|
||||
switch e.ID {
|
||||
case "q", "<C-c>":
|
||||
ui.Close()
|
||||
self.destroy(env.ZId, cfg, zrok, auth)
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
|
||||
case <-ticker:
|
||||
currentRequests := float64(httpProxy.Requests())
|
||||
deltaRequests := currentRequests - lastRequests
|
||||
requestData = append(requestData, deltaRequests)
|
||||
lastRequests = currentRequests
|
||||
requestData = append(requestData, deltaRequests)
|
||||
for len(requestData) > w-17 {
|
||||
requestData = requestData[1:]
|
||||
}
|
||||
spk.Title = fmt.Sprintf(" requests (%d) ", int(currentRequests))
|
||||
spk.Data = requestData
|
||||
ui.Render(p, slg)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logrus.Infof("access your zrok service: %v", resp.Payload.ProxyEndpoint)
|
||||
for {
|
||||
time.Sleep(30 * time.Second)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (self *httpBackendCommand) destroy(id string, cfg *backend.Config, zrok *rest_client_zrok.Zrok, auth runtime.ClientAuthInfoWriter) {
|
||||
logrus.Debugf("shutting down '%v'", cfg.Service)
|
||||
req := tunnel.NewUntunnelParams()
|
||||
req.Body = &rest_model_zrok.UntunnelRequest{
|
||||
ZID: id,
|
||||
SvcName: cfg.Service,
|
||||
}
|
||||
if _, err := zrok.Tunnel.Untunnel(req, auth); err == nil {
|
||||
logrus.Debugf("shutdown complete")
|
||||
} else {
|
||||
logrus.Errorf("error shutting down: %v", err)
|
||||
}
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/michaelquigley/cf"
|
||||
"github.com/openziti-test-kitchen/zrok/endpoints/frontend"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func init() {
|
||||
httpCmd.AddCommand(newHttpFrontendCommand().cmd)
|
||||
}
|
||||
|
||||
type httpFrontendCommand struct {
|
||||
cmd *cobra.Command
|
||||
}
|
||||
|
||||
func newHttpFrontendCommand() *httpFrontendCommand {
|
||||
cmd := &cobra.Command{
|
||||
Use: "frontend [<configPath>]",
|
||||
Aliases: []string{"fe"},
|
||||
Short: "Create an HTTP frontend",
|
||||
Args: cobra.RangeArgs(0, 1),
|
||||
}
|
||||
command := &httpFrontendCommand{cmd: cmd}
|
||||
cmd.Run = command.run
|
||||
return command
|
||||
}
|
||||
|
||||
func (self *httpFrontendCommand) run(_ *cobra.Command, args []string) {
|
||||
cfg := frontend.DefaultConfig()
|
||||
if len(args) == 1 {
|
||||
if err := cfg.Load(args[0]); err != nil {
|
||||
if !panicInstead {
|
||||
showError(fmt.Sprintf("unable to load configuration '%v'", args[0]), err)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
logrus.Infof(cf.Dump(cfg, cf.DefaultOptions()))
|
||||
httpListener, err := frontend.NewHTTP(cfg)
|
||||
if err != nil {
|
||||
if !panicInstead {
|
||||
showError("unable to create http frontend", err)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
if err := httpListener.Run(); err != nil {
|
||||
if !panicInstead {
|
||||
showError("unable to run http frontend", err)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
}
|
@ -2,10 +2,17 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_client_zrok/identity"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/charmbracelet/bubbles/textinput"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_client_zrok/account"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_model_zrok"
|
||||
"github.com/openziti-test-kitchen/zrok/tui"
|
||||
"github.com/openziti-test-kitchen/zrok/util"
|
||||
"github.com/openziti-test-kitchen/zrok/zrokdir"
|
||||
"github.com/openziti/foundation/v2/term"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@ -15,6 +22,8 @@ func init() {
|
||||
|
||||
type inviteCommand struct {
|
||||
cmd *cobra.Command
|
||||
token string
|
||||
tui inviteTui
|
||||
}
|
||||
|
||||
func newInviteCommand() *inviteCommand {
|
||||
@ -23,42 +32,200 @@ func newInviteCommand() *inviteCommand {
|
||||
Short: "Invite a new user to zrok",
|
||||
Args: cobra.ExactArgs(0),
|
||||
}
|
||||
command := &inviteCommand{cmd: cmd}
|
||||
command := &inviteCommand{
|
||||
cmd: cmd,
|
||||
tui: newInviteTui(),
|
||||
}
|
||||
cmd.Run = command.run
|
||||
|
||||
cmd.Flags().StringVar(&command.token, "token", "", "Invite token required when zrok running in token store mode")
|
||||
|
||||
return command
|
||||
}
|
||||
|
||||
func (cmd *inviteCommand) run(_ *cobra.Command, _ []string) {
|
||||
email, err := term.Prompt("New Email: ")
|
||||
zrd, err := zrokdir.Load()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
confirm, err := term.Prompt("Confirm Email: ")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if confirm != email {
|
||||
showError("entered emails do not match... aborting!", nil)
|
||||
tui.Error("error loading zrokdir", err)
|
||||
}
|
||||
|
||||
zrok, err := zrokdir.ZrokClient(apiEndpoint)
|
||||
zrok, err := zrd.Client()
|
||||
if err != nil {
|
||||
if !panicInstead {
|
||||
showError("error creating zrok api client", err)
|
||||
cmd.endpointError(zrd.ApiEndpoint())
|
||||
tui.Error("error creating zrok api client", err)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
req := identity.NewCreateAccountParams()
|
||||
req.Body = &rest_model_zrok.AccountRequest{
|
||||
|
||||
if _, err := tea.NewProgram(&cmd.tui).Run(); err != nil {
|
||||
tui.Error("unable to run interface", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if cmd.tui.done {
|
||||
email := cmd.tui.inputs[0].Value()
|
||||
|
||||
req := account.NewInviteParams()
|
||||
req.Body = &rest_model_zrok.InviteRequest{
|
||||
Email: email,
|
||||
Token: cmd.token,
|
||||
}
|
||||
_, err = zrok.Identity.CreateAccount(req)
|
||||
_, err = zrok.Account.Invite(req)
|
||||
if err != nil {
|
||||
if !panicInstead {
|
||||
showError("error creating account", err)
|
||||
}
|
||||
panic(err)
|
||||
cmd.endpointError(zrd.ApiEndpoint())
|
||||
tui.Error("error creating invitation", err)
|
||||
}
|
||||
|
||||
fmt.Printf("registration invitation sent to '%v'!\n", email)
|
||||
fmt.Printf("invitation sent to '%v'!\n", email)
|
||||
}
|
||||
}
|
||||
|
||||
func (cmd *inviteCommand) endpointError(apiEndpoint, _ string) {
|
||||
fmt.Printf("%v\n\n", tui.ErrorStyle.Render("there was a problem creating an invitation!"))
|
||||
fmt.Printf("you are trying to use the zrok service at: %v\n\n", tui.CodeStyle.Render(apiEndpoint))
|
||||
fmt.Printf("you can change your zrok service endpoint using this command:\n\n")
|
||||
fmt.Printf("%v\n\n", tui.CodeStyle.Render("$ zrok config set apiEndpoint <newEndpoint>"))
|
||||
fmt.Printf("(where newEndpoint is something like: %v)\n\n", tui.CodeStyle.Render("https://some.zrok.io"))
|
||||
}
|
||||
|
||||
type inviteTui struct {
|
||||
focusIndex int
|
||||
msg string
|
||||
inputs []textinput.Model
|
||||
cursorMode textinput.CursorMode
|
||||
done bool
|
||||
|
||||
msgOk string
|
||||
msgMismatch string
|
||||
focusedStyle lipgloss.Style
|
||||
blurredStyle lipgloss.Style
|
||||
errorStyle lipgloss.Style
|
||||
cursorStyle lipgloss.Style
|
||||
noStyle lipgloss.Style
|
||||
helpStyle lipgloss.Style
|
||||
focusedButton string
|
||||
blurredButton string
|
||||
}
|
||||
|
||||
func newInviteTui() inviteTui {
|
||||
m := inviteTui{
|
||||
inputs: make([]textinput.Model, 2),
|
||||
}
|
||||
m.focusedStyle = tui.WarningStyle.Copy()
|
||||
m.blurredStyle = tui.CodeStyle.Copy()
|
||||
m.errorStyle = tui.ErrorStyle.Copy()
|
||||
m.cursorStyle = m.focusedStyle.Copy()
|
||||
m.noStyle = lipgloss.NewStyle()
|
||||
m.helpStyle = m.blurredStyle.Copy()
|
||||
m.focusedButton = m.focusedStyle.Copy().Render("[ Submit ]")
|
||||
m.blurredButton = fmt.Sprintf("[ %v ]", m.blurredStyle.Render("Submit"))
|
||||
m.msgOk = m.noStyle.Render("enter and confirm your email address...")
|
||||
m.msg = m.msgOk
|
||||
m.msgMismatch = m.errorStyle.Render("email is invalid or does not match confirmation...")
|
||||
|
||||
var t textinput.Model
|
||||
for i := range m.inputs {
|
||||
t = textinput.New()
|
||||
t.CursorStyle = m.cursorStyle
|
||||
t.CharLimit = 96
|
||||
|
||||
switch i {
|
||||
case 0:
|
||||
t.Placeholder = "Email Address"
|
||||
t.Focus()
|
||||
t.PromptStyle = m.focusedStyle
|
||||
t.TextStyle = m.focusedStyle
|
||||
case 1:
|
||||
t.Placeholder = "Confirm Email"
|
||||
}
|
||||
|
||||
m.inputs[i] = t
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
func (m inviteTui) Init() tea.Cmd { return textinput.Blink }
|
||||
|
||||
func (m *inviteTui) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
switch msg := msg.(type) {
|
||||
case tea.KeyMsg:
|
||||
switch msg.String() {
|
||||
case "ctrl+c", "esc":
|
||||
return m, tea.Quit
|
||||
|
||||
case "tab", "shift+tab", "enter", "up", "down":
|
||||
s := msg.String()
|
||||
|
||||
if s == "enter" && m.focusIndex == len(m.inputs) {
|
||||
if util.IsValidEmail(m.inputs[0].Value()) && m.inputs[0].Value() == m.inputs[1].Value() {
|
||||
m.done = true
|
||||
return m, tea.Quit
|
||||
}
|
||||
m.msg = m.msgMismatch
|
||||
return m, nil
|
||||
}
|
||||
|
||||
if s == "up" || s == "shift+tab" {
|
||||
m.msg = m.msgOk
|
||||
m.focusIndex--
|
||||
} else {
|
||||
m.msg = m.msgOk
|
||||
m.focusIndex++
|
||||
}
|
||||
|
||||
if m.focusIndex > len(m.inputs) {
|
||||
m.focusIndex = 0
|
||||
} else if m.focusIndex < 0 {
|
||||
m.focusIndex = len(m.inputs)
|
||||
}
|
||||
|
||||
cmds := make([]tea.Cmd, len(m.inputs))
|
||||
for i := 0; i <= len(m.inputs)-1; i++ {
|
||||
if i == m.focusIndex {
|
||||
cmds[i] = m.inputs[i].Focus()
|
||||
m.inputs[i].PromptStyle = m.focusedStyle
|
||||
m.inputs[i].TextStyle = m.focusedStyle
|
||||
continue
|
||||
}
|
||||
m.inputs[i].Blur()
|
||||
m.inputs[i].PromptStyle = m.noStyle
|
||||
m.inputs[i].TextStyle = m.noStyle
|
||||
}
|
||||
|
||||
return m, tea.Batch(cmds...)
|
||||
}
|
||||
}
|
||||
|
||||
cmd := m.updateInputs(msg)
|
||||
|
||||
return m, cmd
|
||||
}
|
||||
|
||||
func (m *inviteTui) updateInputs(msg tea.Msg) tea.Cmd {
|
||||
cmds := make([]tea.Cmd, len(m.inputs))
|
||||
for i := range m.inputs {
|
||||
m.inputs[i], cmds[i] = m.inputs[i].Update(msg)
|
||||
}
|
||||
return tea.Batch(cmds...)
|
||||
}
|
||||
|
||||
func (m inviteTui) View() string {
|
||||
var b strings.Builder
|
||||
b.WriteString(fmt.Sprintf("\n%v\n\n", m.msg))
|
||||
|
||||
for i := range m.inputs {
|
||||
b.WriteString(m.inputs[i].View())
|
||||
if i < len(m.inputs)-1 {
|
||||
b.WriteRune('\n')
|
||||
}
|
||||
}
|
||||
|
||||
button := &m.blurredButton
|
||||
if m.focusIndex == len(m.inputs) {
|
||||
button = &m.focusedButton
|
||||
}
|
||||
_, _ = fmt.Fprintf(&b, "\n\n%s\n\n", *button)
|
||||
|
||||
return b.String()
|
||||
}
|
||||
|
@ -8,8 +8,9 @@ import (
|
||||
httptransport "github.com/go-openapi/runtime/client"
|
||||
"github.com/openziti-test-kitchen/zrok/model"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_client_zrok"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_client_zrok/tunnel"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_client_zrok/share"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_model_zrok"
|
||||
"github.com/openziti-test-kitchen/zrok/tui"
|
||||
"github.com/openziti-test-kitchen/zrok/util"
|
||||
"github.com/openziti-test-kitchen/zrok/zrokdir"
|
||||
"github.com/openziti/sdk-golang/ziti"
|
||||
@ -20,6 +21,9 @@ import (
|
||||
"io"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
@ -39,6 +43,7 @@ type loopCmd struct {
|
||||
maxDwellMs int
|
||||
minPacingMs int
|
||||
maxPacingMs int
|
||||
frontendSelection []string
|
||||
}
|
||||
|
||||
func newLoopCmd() *loopCmd {
|
||||
@ -59,6 +64,7 @@ func newLoopCmd() *loopCmd {
|
||||
cmd.Flags().IntVar(&r.maxDwellMs, "max-dwell-ms", 1000, "Maximum dwell time in milliseconds")
|
||||
cmd.Flags().IntVar(&r.minPacingMs, "min-pacing-ms", 0, "Minimum pacing in milliseconds")
|
||||
cmd.Flags().IntVar(&r.maxPacingMs, "max-pacing-ms", 0, "Maximum pacing in milliseconds")
|
||||
cmd.Flags().StringArrayVar(&r.frontendSelection, "frontends", []string{"public"}, "Selected frontends to use for the share")
|
||||
return r
|
||||
}
|
||||
|
||||
@ -69,21 +75,32 @@ func (r *loopCmd) run(_ *cobra.Command, _ []string) {
|
||||
loopers = append(loopers, l)
|
||||
go l.run()
|
||||
}
|
||||
c := make(chan os.Signal)
|
||||
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
|
||||
go func() {
|
||||
<-c
|
||||
for _, looper := range loopers {
|
||||
looper.stop = true
|
||||
}
|
||||
}()
|
||||
for _, l := range loopers {
|
||||
<-l.done
|
||||
}
|
||||
totalMismatches := 0
|
||||
totalXfer := int64(0)
|
||||
totalLoops := int64(0)
|
||||
for _, l := range loopers {
|
||||
deltaSeconds := l.stopTime.Sub(l.startTime).Seconds()
|
||||
xfer := int64(float64(l.bytes) / deltaSeconds)
|
||||
totalXfer += xfer
|
||||
totalMismatches += l.mismatches
|
||||
xferSec := util.BytesToSize(xfer)
|
||||
logrus.Infof("looper #%d: %d mismatches, %s/sec", l.id, l.mismatches, xferSec)
|
||||
totalLoops += l.loops
|
||||
logrus.Infof("looper #%d: %d loops, %d mismatches, %s/sec", l.id, l.loops, l.mismatches, xferSec)
|
||||
}
|
||||
totalXferSec := util.BytesToSize(totalXfer)
|
||||
logrus.Infof("total: %d mismatches, %s/sec", totalMismatches, totalXferSec)
|
||||
logrus.Infof("total: %d loops, %d mismatches, %s/sec", totalLoops, totalMismatches, totalXferSec)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
type looper struct {
|
||||
@ -94,13 +111,15 @@ type looper struct {
|
||||
listener edge.Listener
|
||||
zif string
|
||||
zrok *rest_client_zrok.Zrok
|
||||
service string
|
||||
shrToken string
|
||||
proxyEndpoint string
|
||||
auth runtime.ClientAuthInfoWriter
|
||||
mismatches int
|
||||
bytes int64
|
||||
loops int64
|
||||
startTime time.Time
|
||||
stopTime time.Time
|
||||
stop bool
|
||||
}
|
||||
|
||||
func newLooper(id int, cmd *loopCmd) *looper {
|
||||
@ -116,7 +135,7 @@ func (l *looper) run() {
|
||||
defer logrus.Infof("stopping #%d", l.id)
|
||||
|
||||
l.startup()
|
||||
logrus.Infof("looper #%d, service: %v, frontend: %v", l.id, l.service, l.proxyEndpoint)
|
||||
logrus.Infof("looper #%d, shrToken: %v, frontend: %v", l.id, l.shrToken, l.proxyEndpoint)
|
||||
go l.serviceListener()
|
||||
l.dwell()
|
||||
l.iterate()
|
||||
@ -134,7 +153,7 @@ func (l *looper) serviceListener() {
|
||||
ConnectTimeout: 5 * time.Minute,
|
||||
MaxConnections: 10,
|
||||
}
|
||||
if l.listener, err = ziti.NewContextWithConfig(zcfg).ListenWithOptions(l.service, &opts); err == nil {
|
||||
if l.listener, err = ziti.NewContextWithConfig(zcfg).ListenWithOptions(l.shrToken, &opts); err == nil {
|
||||
if err := http.Serve(l.listener, l); err != nil {
|
||||
logrus.Errorf("looper #%d, error serving: %v", l.id, err)
|
||||
}
|
||||
@ -152,33 +171,41 @@ func (l *looper) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
func (l *looper) startup() {
|
||||
logrus.Infof("starting #%d", l.id)
|
||||
|
||||
var err error
|
||||
l.env, err = zrokdir.LoadEnvironment()
|
||||
zrd, err := zrokdir.Load()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if zrd.Env == nil {
|
||||
tui.Error("unable to load environment; did you 'zrok enable'?", nil)
|
||||
}
|
||||
l.env = zrd.Env
|
||||
|
||||
l.zif, err = zrokdir.ZitiIdentityFile("backend")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
l.zrok, err = zrokdir.ZrokClient(l.env.ApiEndpoint)
|
||||
l.zrok, err = zrd.Client()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
l.auth = httptransport.APIKeyAuth("x-token", "header", l.env.Token)
|
||||
tunnelReq := tunnel.NewTunnelParams()
|
||||
tunnelReq.Body = &rest_model_zrok.TunnelRequest{
|
||||
ZID: l.env.ZId,
|
||||
Endpoint: fmt.Sprintf("looper#%d", l.id),
|
||||
tunnelReq := share.NewShareParams()
|
||||
tunnelReq.Body = &rest_model_zrok.ShareRequest{
|
||||
EnvZID: l.env.ZId,
|
||||
ShareMode: "public",
|
||||
FrontendSelection: l.cmd.frontendSelection,
|
||||
BackendMode: "proxy",
|
||||
BackendProxyEndpoint: fmt.Sprintf("looper#%d", l.id),
|
||||
AuthScheme: string(model.None),
|
||||
}
|
||||
tunnelReq.SetTimeout(60 * time.Second)
|
||||
tunnelResp, err := l.zrok.Tunnel.Tunnel(tunnelReq, l.auth)
|
||||
tunnelResp, err := l.zrok.Share.Share(tunnelReq, l.auth)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
l.service = tunnelResp.Payload.SvcName
|
||||
l.proxyEndpoint = tunnelResp.Payload.ProxyEndpoint
|
||||
l.shrToken = tunnelResp.Payload.ShrToken
|
||||
l.proxyEndpoint = tunnelResp.Payload.FrontendProxyEndpoints[0]
|
||||
}
|
||||
|
||||
func (l *looper) dwell() {
|
||||
@ -193,7 +220,7 @@ func (l *looper) iterate() {
|
||||
l.startTime = time.Now()
|
||||
defer func() { l.stopTime = time.Now() }()
|
||||
|
||||
for i := 0; i < l.cmd.iterations; i++ {
|
||||
for i := 0; i < l.cmd.iterations && !l.stop; i++ {
|
||||
if i > 0 && i%l.cmd.statusEvery == 0 {
|
||||
logrus.Infof("looper #%d: iteration #%d", l.id, i)
|
||||
}
|
||||
@ -228,6 +255,7 @@ func (l *looper) iterate() {
|
||||
pacingMs = rand.Intn(l.cmd.maxPacingMs-l.cmd.minPacingMs) + l.cmd.minPacingMs
|
||||
time.Sleep(time.Duration(pacingMs) * time.Millisecond)
|
||||
}
|
||||
l.loops++
|
||||
}
|
||||
}
|
||||
|
||||
@ -238,12 +266,12 @@ func (l *looper) shutdown() {
|
||||
}
|
||||
}
|
||||
|
||||
untunnelReq := tunnel.NewUntunnelParams()
|
||||
untunnelReq.Body = &rest_model_zrok.UntunnelRequest{
|
||||
ZID: l.env.ZId,
|
||||
SvcName: l.service,
|
||||
untunnelReq := share.NewUnshareParams()
|
||||
untunnelReq.Body = &rest_model_zrok.UnshareRequest{
|
||||
EnvZID: l.env.ZId,
|
||||
ShrToken: l.shrToken,
|
||||
}
|
||||
if _, err := l.zrok.Tunnel.Untunnel(untunnelReq, l.auth); err != nil {
|
||||
if _, err := l.zrok.Share.Unshare(untunnelReq, l.auth); err != nil {
|
||||
logrus.Errorf("error shutting down looper #%d: %v", l.id, err)
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ package main
|
||||
|
||||
import (
|
||||
"github.com/michaelquigley/pfxlog"
|
||||
"github.com/openziti-test-kitchen/zrok/zrokdir"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"os"
|
||||
@ -14,8 +13,15 @@ func init() {
|
||||
pfxlog.GlobalInit(logrus.InfoLevel, pfxlog.DefaultOptions().SetTrimPrefix("github.com/openziti-test-kitchen/"))
|
||||
rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "Enable verbose logging")
|
||||
rootCmd.PersistentFlags().BoolVarP(&panicInstead, "panic", "p", false, "Panic instead of showing pretty errors")
|
||||
zrokdir.AddZrokApiEndpointFlag(&apiEndpoint, rootCmd.PersistentFlags())
|
||||
rootCmd.AddCommand(httpCmd)
|
||||
rootCmd.AddCommand(accessCmd)
|
||||
adminCmd.AddCommand(adminCreateCmd)
|
||||
adminCmd.AddCommand(adminDeleteCmd)
|
||||
adminCmd.AddCommand(adminListCmd)
|
||||
adminCmd.AddCommand(adminUpdateCmd)
|
||||
rootCmd.AddCommand(adminCmd)
|
||||
rootCmd.AddCommand(configCmd)
|
||||
rootCmd.AddCommand(shareCmd)
|
||||
rootCmd.AddCommand(testCmd)
|
||||
}
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
@ -29,11 +35,50 @@ var rootCmd = &cobra.Command{
|
||||
}
|
||||
var verbose bool
|
||||
var panicInstead bool
|
||||
var apiEndpoint string
|
||||
|
||||
var httpCmd = &cobra.Command{
|
||||
Use: "http",
|
||||
Short: "HTTP endpoint operations",
|
||||
var accessCmd = &cobra.Command{
|
||||
Use: "access",
|
||||
Short: "Create frontend access for shares",
|
||||
}
|
||||
|
||||
var adminCmd = &cobra.Command{
|
||||
Use: "admin",
|
||||
Short: "Administration and operations functions",
|
||||
}
|
||||
|
||||
var adminCreateCmd = &cobra.Command{
|
||||
Use: "create",
|
||||
Short: "Create global resources",
|
||||
}
|
||||
|
||||
var adminDeleteCmd = &cobra.Command{
|
||||
Use: "delete",
|
||||
Short: "Delete global resources",
|
||||
}
|
||||
|
||||
var adminListCmd = &cobra.Command{
|
||||
Use: "list",
|
||||
Short: "List global resources",
|
||||
}
|
||||
|
||||
var adminUpdateCmd = &cobra.Command{
|
||||
Use: "update",
|
||||
Short: "Update global resources",
|
||||
}
|
||||
|
||||
var configCmd = &cobra.Command{
|
||||
Use: "config",
|
||||
Short: "Configure your zrok environment",
|
||||
}
|
||||
|
||||
var shareCmd = &cobra.Command{
|
||||
Use: "share",
|
||||
Short: "Create backend access for shares",
|
||||
}
|
||||
|
||||
var testCmd = &cobra.Command{
|
||||
Use: "test",
|
||||
Short: "Utilities for testing zrok deployments",
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
69
cmd/zrok/release.go
Normal file
69
cmd/zrok/release.go
Normal file
@ -0,0 +1,69 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
httptransport "github.com/go-openapi/runtime/client"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_client_zrok/share"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_model_zrok"
|
||||
"github.com/openziti-test-kitchen/zrok/tui"
|
||||
"github.com/openziti-test-kitchen/zrok/zrokdir"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(newReleaseCommand().cmd)
|
||||
}
|
||||
|
||||
type releaseCommand struct {
|
||||
cmd *cobra.Command
|
||||
}
|
||||
|
||||
func newReleaseCommand() *releaseCommand {
|
||||
cmd := &cobra.Command{
|
||||
Use: "release <shareToken>",
|
||||
Short: "Release a reserved share",
|
||||
Args: cobra.ExactArgs(1),
|
||||
}
|
||||
command := &releaseCommand{cmd: cmd}
|
||||
cmd.Run = command.run
|
||||
return command
|
||||
}
|
||||
|
||||
func (cmd *releaseCommand) run(_ *cobra.Command, args []string) {
|
||||
shrToken := args[0]
|
||||
zrd, err := zrokdir.Load()
|
||||
if err != nil {
|
||||
if !panicInstead {
|
||||
tui.Error("unable to load zrokdir", err)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if zrd.Env == nil {
|
||||
tui.Error("unable to load environment; did you 'zrok enable'?", nil)
|
||||
}
|
||||
|
||||
zrok, err := zrd.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)
|
||||
req := share.NewUnshareParams()
|
||||
req.Body = &rest_model_zrok.UnshareRequest{
|
||||
EnvZID: zrd.Env.ZId,
|
||||
ShrToken: shrToken,
|
||||
Reserved: true,
|
||||
}
|
||||
if _, err := zrok.Share.Unshare(req, auth); err != nil {
|
||||
if !panicInstead {
|
||||
tui.Error("error releasing share", err)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
|
||||
logrus.Infof("reserved share '%v' released", shrToken)
|
||||
}
|
124
cmd/zrok/reserve.go
Normal file
124
cmd/zrok/reserve.go
Normal file
@ -0,0 +1,124 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
httptransport "github.com/go-openapi/runtime/client"
|
||||
"github.com/openziti-test-kitchen/zrok/model"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_client_zrok/share"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_model_zrok"
|
||||
"github.com/openziti-test-kitchen/zrok/tui"
|
||||
"github.com/openziti-test-kitchen/zrok/zrokdir"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(newReserveCommand().cmd)
|
||||
}
|
||||
|
||||
type reserveCommand struct {
|
||||
basicAuth []string
|
||||
frontendSelection []string
|
||||
backendMode string
|
||||
cmd *cobra.Command
|
||||
}
|
||||
|
||||
func newReserveCommand() *reserveCommand {
|
||||
cmd := &cobra.Command{
|
||||
Use: "reserve <public|private> <target>",
|
||||
Short: "Create a reserved share",
|
||||
Args: cobra.ExactArgs(2),
|
||||
}
|
||||
command := &reserveCommand{cmd: cmd}
|
||||
cmd.Flags().StringArrayVar(&command.basicAuth, "basic-auth", []string{}, "Basic authentication users (<username:password>,...)")
|
||||
cmd.Flags().StringArrayVar(&command.frontendSelection, "frontends", []string{"public"}, "Selected frontends to use for the share")
|
||||
cmd.Flags().StringVar(&command.backendMode, "backend-mode", "proxy", "The backend mode {proxy, web}")
|
||||
cmd.Run = command.run
|
||||
return command
|
||||
}
|
||||
|
||||
func (cmd *reserveCommand) run(_ *cobra.Command, args []string) {
|
||||
shareMode := args[0]
|
||||
if shareMode != "public" && shareMode != "private" {
|
||||
tui.Error("invalid sharing mode; expecting 'public' or 'private'", nil)
|
||||
}
|
||||
|
||||
var target string
|
||||
switch cmd.backendMode {
|
||||
case "proxy":
|
||||
targetEndpoint, err := url.Parse(args[1])
|
||||
if err != nil {
|
||||
if !panicInstead {
|
||||
tui.Error("invalid target endpoint URL", err)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
if targetEndpoint.Scheme == "" {
|
||||
targetEndpoint.Scheme = "https"
|
||||
}
|
||||
target = targetEndpoint.String()
|
||||
|
||||
case "web":
|
||||
target = args[1]
|
||||
}
|
||||
|
||||
zrd, err := zrokdir.Load()
|
||||
if err != nil {
|
||||
if !panicInstead {
|
||||
tui.Error("error loading zrokdir", err)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if zrd.Env == nil {
|
||||
tui.Error("unable to load environment; did you 'zrok enable'?", nil)
|
||||
}
|
||||
|
||||
zrok, err := zrd.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)
|
||||
req := share.NewShareParams()
|
||||
req.Body = &rest_model_zrok.ShareRequest{
|
||||
EnvZID: zrd.Env.ZId,
|
||||
ShareMode: shareMode,
|
||||
BackendMode: cmd.backendMode,
|
||||
BackendProxyEndpoint: target,
|
||||
AuthScheme: string(model.None),
|
||||
Reserved: true,
|
||||
}
|
||||
if shareMode == "public" {
|
||||
req.Body.FrontendSelection = cmd.frontendSelection
|
||||
}
|
||||
if len(cmd.basicAuth) > 0 {
|
||||
logrus.Infof("configuring basic auth")
|
||||
req.Body.AuthScheme = string(model.Basic)
|
||||
for _, pair := range cmd.basicAuth {
|
||||
tokens := strings.Split(pair, ":")
|
||||
if len(tokens) == 2 {
|
||||
req.Body.AuthUsers = append(req.Body.AuthUsers, &rest_model_zrok.AuthUser{Username: strings.TrimSpace(tokens[0]), Password: strings.TrimSpace(tokens[1])})
|
||||
} else {
|
||||
panic(errors.Errorf("invalid username:password pair '%v'", pair))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resp, err := zrok.Share.Share(req, auth)
|
||||
if err != nil {
|
||||
if !panicInstead {
|
||||
tui.Error("unable to create tunnel", err)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
|
||||
logrus.Infof("your reserved share token is '%v'", resp.Payload.ShrToken)
|
||||
for _, fpe := range resp.Payload.FrontendProxyEndpoints {
|
||||
logrus.Infof("reserved frontend endpoint: %v", fpe)
|
||||
}
|
||||
}
|
253
cmd/zrok/sharePrivate.go
Normal file
253
cmd/zrok/sharePrivate.go
Normal file
@ -0,0 +1,253 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/go-openapi/runtime"
|
||||
httptransport "github.com/go-openapi/runtime/client"
|
||||
"github.com/openziti-test-kitchen/zrok/endpoints"
|
||||
"github.com/openziti-test-kitchen/zrok/endpoints/proxyBackend"
|
||||
"github.com/openziti-test-kitchen/zrok/endpoints/webBackend"
|
||||
"github.com/openziti-test-kitchen/zrok/model"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_client_zrok"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_client_zrok/share"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_model_zrok"
|
||||
"github.com/openziti-test-kitchen/zrok/tui"
|
||||
"github.com/openziti-test-kitchen/zrok/zrokdir"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func init() {
|
||||
shareCmd.AddCommand(newSharePrivateCommand().cmd)
|
||||
}
|
||||
|
||||
type sharePrivateCommand struct {
|
||||
basicAuth []string
|
||||
backendMode string
|
||||
headless bool
|
||||
cmd *cobra.Command
|
||||
}
|
||||
|
||||
func newSharePrivateCommand() *sharePrivateCommand {
|
||||
cmd := &cobra.Command{
|
||||
Use: "private <target>",
|
||||
Short: "Share a target resource privately",
|
||||
Args: cobra.ExactArgs(1),
|
||||
}
|
||||
command := &sharePrivateCommand{cmd: cmd}
|
||||
cmd.Flags().StringArrayVar(&command.basicAuth, "basic-auth", []string{}, "Basic authentication users (<username:password>,...")
|
||||
cmd.Flags().StringVar(&command.backendMode, "backend-mode", "proxy", "The backend mode {proxy, web}")
|
||||
cmd.Flags().BoolVar(&command.headless, "headless", false, "Disable TUI and run headless")
|
||||
cmd.Run = command.run
|
||||
return command
|
||||
}
|
||||
|
||||
func (cmd *sharePrivateCommand) run(_ *cobra.Command, args []string) {
|
||||
var target string
|
||||
|
||||
switch cmd.backendMode {
|
||||
case "proxy":
|
||||
targetEndpoint, err := url.Parse(args[0])
|
||||
if err != nil {
|
||||
if !panicInstead {
|
||||
tui.Error("invalid target endpoint URL", err)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
if targetEndpoint.Scheme == "" {
|
||||
targetEndpoint.Scheme = "https"
|
||||
}
|
||||
target = targetEndpoint.String()
|
||||
|
||||
case "web":
|
||||
target = args[0]
|
||||
|
||||
default:
|
||||
tui.Error(fmt.Sprintf("invalid backend mode '%v'; expected {proxy, web}", cmd.backendMode), nil)
|
||||
}
|
||||
|
||||
zrd, err := zrokdir.Load()
|
||||
if err != nil {
|
||||
if !panicInstead {
|
||||
tui.Error("unable to load zrokdir", err)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if zrd.Env == nil {
|
||||
tui.Error("unable to load environment; did you 'zrok enable'?", nil)
|
||||
}
|
||||
|
||||
zif, err := zrokdir.ZitiIdentityFile("backend")
|
||||
if err != nil {
|
||||
if !panicInstead {
|
||||
tui.Error("unable to load ziti identity configuration", err)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
|
||||
zrok, err := zrd.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)
|
||||
req := share.NewShareParams()
|
||||
req.Body = &rest_model_zrok.ShareRequest{
|
||||
EnvZID: zrd.Env.ZId,
|
||||
ShareMode: "private",
|
||||
BackendMode: cmd.backendMode,
|
||||
BackendProxyEndpoint: target,
|
||||
AuthScheme: string(model.None),
|
||||
}
|
||||
if len(cmd.basicAuth) > 0 {
|
||||
logrus.Infof("configuring basic auth")
|
||||
req.Body.AuthScheme = string(model.Basic)
|
||||
for _, pair := range cmd.basicAuth {
|
||||
tokens := strings.Split(pair, ":")
|
||||
if len(tokens) == 2 {
|
||||
req.Body.AuthUsers = append(req.Body.AuthUsers, &rest_model_zrok.AuthUser{Username: strings.TrimSpace(tokens[0]), Password: strings.TrimSpace(tokens[1])})
|
||||
} else {
|
||||
panic(errors.Errorf("invalid username:password pair '%v'", pair))
|
||||
}
|
||||
}
|
||||
}
|
||||
resp, err := zrok.Share.Share(req, auth)
|
||||
if err != nil {
|
||||
if !panicInstead {
|
||||
tui.Error("unable to create share", err)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c := make(chan os.Signal)
|
||||
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
|
||||
go func() {
|
||||
<-c
|
||||
cmd.destroy(zrd.Env.ZId, resp.Payload.ShrToken, zrok, auth)
|
||||
os.Exit(0)
|
||||
}()
|
||||
|
||||
requestsChan := make(chan *endpoints.Request, 1024)
|
||||
switch cmd.backendMode {
|
||||
case "proxy":
|
||||
cfg := &proxyBackend.Config{
|
||||
IdentityPath: zif,
|
||||
EndpointAddress: target,
|
||||
ShrToken: resp.Payload.ShrToken,
|
||||
RequestsChan: requestsChan,
|
||||
}
|
||||
_, err = cmd.proxyBackendMode(cfg)
|
||||
if err != nil {
|
||||
if !panicInstead {
|
||||
tui.Error("unable to create proxy backend handler", err)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
|
||||
case "web":
|
||||
cfg := &webBackend.Config{
|
||||
IdentityPath: zif,
|
||||
WebRoot: target,
|
||||
ShrToken: resp.Payload.ShrToken,
|
||||
RequestsChan: requestsChan,
|
||||
}
|
||||
_, err = cmd.webBackendMode(cfg)
|
||||
if err != nil {
|
||||
if !panicInstead {
|
||||
tui.Error("unable to create web backend handler", err)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
|
||||
default:
|
||||
tui.Error("invalid backend mode", nil)
|
||||
}
|
||||
|
||||
if cmd.headless {
|
||||
logrus.Infof("allow other to access your share with the following command:\nzrok access private %v", resp.Payload.ShrToken)
|
||||
for {
|
||||
select {
|
||||
case req := <-requestsChan:
|
||||
logrus.Infof("%v -> %v %v", req.RemoteAddr, req.Method, req.Path)
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
shareDescription := fmt.Sprintf("access your share with: %v", tui.CodeStyle.Render(fmt.Sprintf("zrok access private %v", resp.Payload.ShrToken)))
|
||||
mdl := newShareModel(resp.Payload.ShrToken, []string{shareDescription}, "private", cmd.backendMode)
|
||||
logrus.SetOutput(mdl)
|
||||
prg := tea.NewProgram(mdl, tea.WithAltScreen())
|
||||
mdl.prg = prg
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case req := <-requestsChan:
|
||||
prg.Send(req)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
if _, err := prg.Run(); err != nil {
|
||||
tui.Error("An error occurred", err)
|
||||
}
|
||||
|
||||
close(requestsChan)
|
||||
cmd.destroy(zrd.Env.ZId, resp.Payload.ShrToken, zrok, auth)
|
||||
}
|
||||
}
|
||||
|
||||
func (cmd *sharePrivateCommand) proxyBackendMode(cfg *proxyBackend.Config) (endpoints.RequestHandler, error) {
|
||||
be, err := proxyBackend.NewBackend(cfg)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error creating http proxy backend")
|
||||
}
|
||||
|
||||
go func() {
|
||||
if err := be.Run(); err != nil {
|
||||
logrus.Errorf("error running http proxy backend: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
return be, nil
|
||||
}
|
||||
|
||||
func (cmd *sharePrivateCommand) webBackendMode(cfg *webBackend.Config) (endpoints.RequestHandler, error) {
|
||||
be, err := webBackend.NewBackend(cfg)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error creating http web backend")
|
||||
}
|
||||
|
||||
go func() {
|
||||
if err := be.Run(); err != nil {
|
||||
logrus.Errorf("error running http web backend: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
return be, nil
|
||||
}
|
||||
|
||||
func (cmd *sharePrivateCommand) destroy(id string, shrToken string, zrok *rest_client_zrok.Zrok, auth runtime.ClientAuthInfoWriter) {
|
||||
logrus.Debugf("shutting down '%v'", shrToken)
|
||||
req := share.NewUnshareParams()
|
||||
req.Body = &rest_model_zrok.UnshareRequest{
|
||||
EnvZID: id,
|
||||
ShrToken: shrToken,
|
||||
}
|
||||
if _, err := zrok.Share.Unshare(req, auth); err == nil {
|
||||
logrus.Debugf("shutdown complete")
|
||||
} else {
|
||||
logrus.Errorf("error shutting down: %v", err)
|
||||
}
|
||||
}
|
255
cmd/zrok/sharePublic.go
Normal file
255
cmd/zrok/sharePublic.go
Normal file
@ -0,0 +1,255 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/go-openapi/runtime"
|
||||
httptransport "github.com/go-openapi/runtime/client"
|
||||
"github.com/openziti-test-kitchen/zrok/endpoints"
|
||||
"github.com/openziti-test-kitchen/zrok/endpoints/proxyBackend"
|
||||
"github.com/openziti-test-kitchen/zrok/endpoints/webBackend"
|
||||
"github.com/openziti-test-kitchen/zrok/model"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_client_zrok"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_client_zrok/share"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_model_zrok"
|
||||
"github.com/openziti-test-kitchen/zrok/tui"
|
||||
"github.com/openziti-test-kitchen/zrok/zrokdir"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func init() {
|
||||
shareCmd.AddCommand(newSharePublicCommand().cmd)
|
||||
}
|
||||
|
||||
type sharePublicCommand struct {
|
||||
basicAuth []string
|
||||
frontendSelection []string
|
||||
backendMode string
|
||||
headless bool
|
||||
cmd *cobra.Command
|
||||
}
|
||||
|
||||
func newSharePublicCommand() *sharePublicCommand {
|
||||
cmd := &cobra.Command{
|
||||
Use: "public <target>",
|
||||
Short: "Share a target resource publicly",
|
||||
Args: cobra.ExactArgs(1),
|
||||
}
|
||||
command := &sharePublicCommand{cmd: cmd}
|
||||
cmd.Flags().StringArrayVar(&command.basicAuth, "basic-auth", []string{}, "Basic authentication users (<username:password>,...)")
|
||||
cmd.Flags().StringArrayVar(&command.frontendSelection, "frontends", []string{"public"}, "Selected frontends to use for the share")
|
||||
cmd.Flags().StringVar(&command.backendMode, "backend-mode", "proxy", "The backend mode {proxy, web}")
|
||||
cmd.Flags().BoolVar(&command.headless, "headless", false, "Disable TUI and run headless")
|
||||
cmd.Run = command.run
|
||||
return command
|
||||
}
|
||||
|
||||
func (cmd *sharePublicCommand) run(_ *cobra.Command, args []string) {
|
||||
var target string
|
||||
|
||||
switch cmd.backendMode {
|
||||
case "proxy":
|
||||
targetEndpoint, err := url.Parse(args[0])
|
||||
if err != nil {
|
||||
if !panicInstead {
|
||||
tui.Error("invalid target endpoint URL", err)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
if targetEndpoint.Scheme == "" {
|
||||
targetEndpoint.Scheme = "https"
|
||||
}
|
||||
target = targetEndpoint.String()
|
||||
|
||||
case "web":
|
||||
target = args[0]
|
||||
|
||||
default:
|
||||
tui.Error(fmt.Sprintf("invalid backend mode '%v'; expected {proxy, web}", cmd.backendMode), nil)
|
||||
}
|
||||
|
||||
zrd, err := zrokdir.Load()
|
||||
if err != nil {
|
||||
if !panicInstead {
|
||||
tui.Error("unable to load zrokdir", err)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if zrd.Env == nil {
|
||||
tui.Error("unable to load environment; did you 'zrok enable'?", nil)
|
||||
}
|
||||
|
||||
zif, err := zrokdir.ZitiIdentityFile("backend")
|
||||
if err != nil {
|
||||
if !panicInstead {
|
||||
tui.Error("unable to load ziti identity configuration", err)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
|
||||
zrok, err := zrd.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)
|
||||
req := share.NewShareParams()
|
||||
req.Body = &rest_model_zrok.ShareRequest{
|
||||
EnvZID: zrd.Env.ZId,
|
||||
ShareMode: "public",
|
||||
FrontendSelection: cmd.frontendSelection,
|
||||
BackendMode: cmd.backendMode,
|
||||
BackendProxyEndpoint: target,
|
||||
AuthScheme: string(model.None),
|
||||
}
|
||||
if len(cmd.basicAuth) > 0 {
|
||||
logrus.Infof("configuring basic auth")
|
||||
req.Body.AuthScheme = string(model.Basic)
|
||||
for _, pair := range cmd.basicAuth {
|
||||
tokens := strings.Split(pair, ":")
|
||||
if len(tokens) == 2 {
|
||||
req.Body.AuthUsers = append(req.Body.AuthUsers, &rest_model_zrok.AuthUser{Username: strings.TrimSpace(tokens[0]), Password: strings.TrimSpace(tokens[1])})
|
||||
} else {
|
||||
panic(errors.Errorf("invalid username:password pair '%v'", pair))
|
||||
}
|
||||
}
|
||||
}
|
||||
resp, err := zrok.Share.Share(req, auth)
|
||||
if err != nil {
|
||||
if !panicInstead {
|
||||
tui.Error("unable to create share", err)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c := make(chan os.Signal)
|
||||
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
|
||||
go func() {
|
||||
<-c
|
||||
cmd.destroy(zrd.Env.ZId, resp.Payload.ShrToken, zrok, auth)
|
||||
os.Exit(0)
|
||||
}()
|
||||
|
||||
requestsChan := make(chan *endpoints.Request, 1024)
|
||||
switch cmd.backendMode {
|
||||
case "proxy":
|
||||
cfg := &proxyBackend.Config{
|
||||
IdentityPath: zif,
|
||||
EndpointAddress: target,
|
||||
ShrToken: resp.Payload.ShrToken,
|
||||
RequestsChan: requestsChan,
|
||||
}
|
||||
_, err = cmd.proxyBackendMode(cfg)
|
||||
if err != nil {
|
||||
if !panicInstead {
|
||||
tui.Error("unable to create proxy backend handler", err)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
|
||||
case "web":
|
||||
cfg := &webBackend.Config{
|
||||
IdentityPath: zif,
|
||||
WebRoot: target,
|
||||
ShrToken: resp.Payload.ShrToken,
|
||||
RequestsChan: requestsChan,
|
||||
}
|
||||
_, err = cmd.webBackendMode(cfg)
|
||||
if err != nil {
|
||||
if !panicInstead {
|
||||
tui.Error("unable to create web backend handler", err)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
|
||||
default:
|
||||
tui.Error("invalid backend mode", nil)
|
||||
}
|
||||
|
||||
if cmd.headless {
|
||||
logrus.Infof("access your zrok share at the following endpoints:\n %v", strings.Join(resp.Payload.FrontendProxyEndpoints, "\n"))
|
||||
for {
|
||||
select {
|
||||
case req := <-requestsChan:
|
||||
logrus.Infof("%v -> %v %v", req.RemoteAddr, req.Method, req.Path)
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
mdl := newShareModel(resp.Payload.ShrToken, resp.Payload.FrontendProxyEndpoints, "public", cmd.backendMode)
|
||||
logrus.SetOutput(mdl)
|
||||
prg := tea.NewProgram(mdl, tea.WithAltScreen())
|
||||
mdl.prg = prg
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case req := <-requestsChan:
|
||||
prg.Send(req)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
if _, err := prg.Run(); err != nil {
|
||||
tui.Error("An error occurred", err)
|
||||
}
|
||||
|
||||
close(requestsChan)
|
||||
cmd.destroy(zrd.Env.ZId, resp.Payload.ShrToken, zrok, auth)
|
||||
}
|
||||
}
|
||||
|
||||
func (cmd *sharePublicCommand) proxyBackendMode(cfg *proxyBackend.Config) (endpoints.RequestHandler, error) {
|
||||
be, err := proxyBackend.NewBackend(cfg)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error creating http proxy backend")
|
||||
}
|
||||
|
||||
go func() {
|
||||
if err := be.Run(); err != nil {
|
||||
logrus.Errorf("error running http proxy backend: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
return be, nil
|
||||
}
|
||||
|
||||
func (cmd *sharePublicCommand) webBackendMode(cfg *webBackend.Config) (endpoints.RequestHandler, error) {
|
||||
be, err := webBackend.NewBackend(cfg)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error creating http web backend")
|
||||
}
|
||||
|
||||
go func() {
|
||||
if err := be.Run(); err != nil {
|
||||
logrus.Errorf("error running http web backend: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
return be, nil
|
||||
}
|
||||
|
||||
func (cmd *sharePublicCommand) destroy(id string, shrToken string, zrok *rest_client_zrok.Zrok, auth runtime.ClientAuthInfoWriter) {
|
||||
logrus.Debugf("shutting down '%v'", shrToken)
|
||||
req := share.NewUnshareParams()
|
||||
req.Body = &rest_model_zrok.UnshareRequest{
|
||||
EnvZID: id,
|
||||
ShrToken: shrToken,
|
||||
}
|
||||
if _, err := zrok.Share.Unshare(req, auth); err == nil {
|
||||
logrus.Debugf("shutdown complete")
|
||||
} else {
|
||||
logrus.Errorf("error shutting down: %v", err)
|
||||
}
|
||||
}
|
215
cmd/zrok/shareReserved.go
Normal file
215
cmd/zrok/shareReserved.go
Normal file
@ -0,0 +1,215 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
httptransport "github.com/go-openapi/runtime/client"
|
||||
"github.com/openziti-test-kitchen/zrok/endpoints"
|
||||
"github.com/openziti-test-kitchen/zrok/endpoints/proxyBackend"
|
||||
"github.com/openziti-test-kitchen/zrok/endpoints/webBackend"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_client_zrok/metadata"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_client_zrok/share"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_model_zrok"
|
||||
"github.com/openziti-test-kitchen/zrok/tui"
|
||||
"github.com/openziti-test-kitchen/zrok/zrokdir"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func init() {
|
||||
shareCmd.AddCommand(newShareReservedCommand().cmd)
|
||||
}
|
||||
|
||||
type shareReservedCommand struct {
|
||||
overrideEndpoint string
|
||||
headless bool
|
||||
cmd *cobra.Command
|
||||
}
|
||||
|
||||
func newShareReservedCommand() *shareReservedCommand {
|
||||
cmd := &cobra.Command{
|
||||
Use: "reserved <shareToken>",
|
||||
Short: "Start a backend for a reserved share",
|
||||
}
|
||||
command := &shareReservedCommand{cmd: cmd}
|
||||
cmd.Flags().StringVar(&command.overrideEndpoint, "override-endpoint", "", "Override the stored target endpoint with a replacement")
|
||||
cmd.Flags().BoolVar(&command.headless, "headless", false, "Disable TUI and run headless")
|
||||
cmd.Run = command.run
|
||||
return command
|
||||
}
|
||||
|
||||
func (cmd *shareReservedCommand) run(_ *cobra.Command, args []string) {
|
||||
shrToken := args[0]
|
||||
var target string
|
||||
|
||||
zrd, err := zrokdir.Load()
|
||||
if err != nil {
|
||||
if !panicInstead {
|
||||
tui.Error("error loading zrokdir", err)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if zrd.Env == nil {
|
||||
tui.Error("unable to load environment; did you 'zrok enable'?", nil)
|
||||
}
|
||||
|
||||
zrok, err := zrd.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)
|
||||
req := metadata.NewGetShareDetailParams()
|
||||
req.ShrToken = shrToken
|
||||
resp, err := zrok.Metadata.GetShareDetail(req, auth)
|
||||
if err != nil {
|
||||
if !panicInstead {
|
||||
tui.Error("unable to retrieve reserved share", err)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
if target == "" {
|
||||
target = resp.Payload.BackendProxyEndpoint
|
||||
}
|
||||
|
||||
zif, err := zrokdir.ZitiIdentityFile("backend")
|
||||
if err != nil {
|
||||
if !panicInstead {
|
||||
tui.Error("unable to load ziti identity configuration", err)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
|
||||
logrus.Infof("sharing target: '%v'", target)
|
||||
|
||||
if resp.Payload.BackendProxyEndpoint != target {
|
||||
upReq := share.NewUpdateShareParams()
|
||||
upReq.Body = &rest_model_zrok.UpdateShareRequest{
|
||||
ShrToken: shrToken,
|
||||
BackendProxyEndpoint: target,
|
||||
}
|
||||
if _, err := zrok.Share.UpdateShare(upReq, auth); err != nil {
|
||||
if !panicInstead {
|
||||
tui.Error("unable to update backend proxy endpoint", err)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
logrus.Infof("updated backend proxy endpoint to: %v", target)
|
||||
} else {
|
||||
logrus.Infof("using existing backend proxy endpoint: %v", target)
|
||||
}
|
||||
|
||||
requestsChan := make(chan *endpoints.Request, 1024)
|
||||
switch resp.Payload.BackendMode {
|
||||
case "proxy":
|
||||
cfg := &proxyBackend.Config{
|
||||
IdentityPath: zif,
|
||||
EndpointAddress: target,
|
||||
ShrToken: shrToken,
|
||||
RequestsChan: requestsChan,
|
||||
}
|
||||
_, err := cmd.proxyBackendMode(cfg)
|
||||
if err != nil {
|
||||
if !panicInstead {
|
||||
tui.Error("unable to create proxy backend handler", err)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
|
||||
case "web":
|
||||
cfg := &webBackend.Config{
|
||||
IdentityPath: zif,
|
||||
WebRoot: target,
|
||||
ShrToken: shrToken,
|
||||
RequestsChan: requestsChan,
|
||||
}
|
||||
_, err := cmd.webBackendMode(cfg)
|
||||
if err != nil {
|
||||
if !panicInstead {
|
||||
tui.Error("unable to create web backend handler", err)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
|
||||
default:
|
||||
tui.Error("invalid backend mode", nil)
|
||||
}
|
||||
|
||||
if cmd.headless {
|
||||
switch resp.Payload.ShareMode {
|
||||
case "public":
|
||||
logrus.Infof("access your zrok share: %v", resp.Payload.FrontendEndpoint)
|
||||
|
||||
case "private":
|
||||
logrus.Infof("use this command to access your zrok share: 'zrok access private %v'", shrToken)
|
||||
}
|
||||
for {
|
||||
select {
|
||||
case req := <-requestsChan:
|
||||
logrus.Infof("%v -> %v %v", req.RemoteAddr, req.Method, req.Path)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var shareDescription string
|
||||
switch resp.Payload.ShareMode {
|
||||
case "public":
|
||||
shareDescription = resp.Payload.FrontendEndpoint
|
||||
case "private":
|
||||
shareDescription = fmt.Sprintf("access your share with: %v", tui.CodeStyle.Render(fmt.Sprintf("zrok access private %v", shrToken)))
|
||||
}
|
||||
|
||||
mdl := newShareModel(shrToken, []string{shareDescription}, resp.Payload.ShareMode, resp.Payload.BackendMode)
|
||||
logrus.SetOutput(mdl)
|
||||
prg := tea.NewProgram(mdl, tea.WithAltScreen())
|
||||
mdl.prg = prg
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case req := <-requestsChan:
|
||||
prg.Send(req)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
if _, err := prg.Run(); err != nil {
|
||||
tui.Error("An error occurred", err)
|
||||
}
|
||||
|
||||
close(requestsChan)
|
||||
}
|
||||
}
|
||||
|
||||
func (cmd *shareReservedCommand) proxyBackendMode(cfg *proxyBackend.Config) (endpoints.RequestHandler, error) {
|
||||
be, err := proxyBackend.NewBackend(cfg)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error creating http proxy backend")
|
||||
}
|
||||
|
||||
go func() {
|
||||
if err := be.Run(); err != nil {
|
||||
logrus.Errorf("error running http proxy backend: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
return be, nil
|
||||
}
|
||||
|
||||
func (cmd *shareReservedCommand) webBackendMode(cfg *webBackend.Config) (endpoints.RequestHandler, error) {
|
||||
be, err := webBackend.NewBackend(cfg)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error creating http web backend")
|
||||
}
|
||||
|
||||
go func() {
|
||||
if err := be.Run(); err != nil {
|
||||
logrus.Errorf("error running http web backend: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
return be, nil
|
||||
}
|
240
cmd/zrok/shareTui.go
Normal file
240
cmd/zrok/shareTui.go
Normal file
@ -0,0 +1,240 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/muesli/reflow/wordwrap"
|
||||
"github.com/openziti-test-kitchen/zrok/endpoints"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const shareTuiBacklog = 256
|
||||
|
||||
type shareModel struct {
|
||||
shrToken string
|
||||
frontendDescriptions []string
|
||||
shareMode string
|
||||
backendMode string
|
||||
requests []*endpoints.Request
|
||||
log []string
|
||||
showLog bool
|
||||
width int
|
||||
height int
|
||||
headerHeight int
|
||||
prg *tea.Program
|
||||
}
|
||||
|
||||
type shareLogLine string
|
||||
|
||||
func newShareModel(shrToken string, frontendEndpoints []string, shareMode, backendMode string) *shareModel {
|
||||
return &shareModel{
|
||||
shrToken: shrToken,
|
||||
frontendDescriptions: frontendEndpoints,
|
||||
shareMode: shareMode,
|
||||
backendMode: backendMode,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *shareModel) Init() tea.Cmd { return nil }
|
||||
|
||||
func (m *shareModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
switch msg := msg.(type) {
|
||||
case *endpoints.Request:
|
||||
m.requests = append(m.requests, msg)
|
||||
if len(m.requests) > shareTuiBacklog {
|
||||
m.requests = m.requests[1:]
|
||||
}
|
||||
|
||||
case shareLogLine:
|
||||
m.showLog = true
|
||||
m.adjustPaneHeights()
|
||||
|
||||
m.log = append(m.log, string(msg))
|
||||
if len(m.log) > shareTuiBacklog {
|
||||
m.log = m.log[1:]
|
||||
}
|
||||
|
||||
case tea.WindowSizeMsg:
|
||||
m.width = msg.Width
|
||||
shareHeaderStyle.Width(m.width - 30)
|
||||
shareConfigStyle.Width(26)
|
||||
shareRequestsStyle.Width(m.width - 2)
|
||||
shareLogStyle.Width(m.width - 2)
|
||||
|
||||
m.height = msg.Height
|
||||
m.headerHeight = len(m.frontendDescriptions) + 4
|
||||
m.adjustPaneHeights()
|
||||
|
||||
case tea.KeyMsg:
|
||||
switch msg.String() {
|
||||
case "ctrl+c", "q":
|
||||
return m, tea.Quit
|
||||
case "ctrl+l":
|
||||
return m, tea.ClearScreen
|
||||
case "l":
|
||||
m.showLog = !m.showLog
|
||||
m.adjustPaneHeights()
|
||||
}
|
||||
}
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (m *shareModel) View() string {
|
||||
topRow := lipgloss.JoinHorizontal(lipgloss.Top,
|
||||
shareHeaderStyle.Render(strings.Join(m.frontendDescriptions, "\n")),
|
||||
shareConfigStyle.Render(m.renderConfig()),
|
||||
)
|
||||
var panes string
|
||||
if m.showLog {
|
||||
panes = lipgloss.JoinVertical(lipgloss.Left,
|
||||
shareRequestsStyle.Render(m.renderRequests()),
|
||||
shareLogStyle.Render(m.renderLog()),
|
||||
)
|
||||
} else {
|
||||
panes = shareRequestsStyle.Render(m.renderRequests())
|
||||
}
|
||||
return lipgloss.JoinVertical(lipgloss.Left, topRow, panes)
|
||||
}
|
||||
|
||||
func (m *shareModel) adjustPaneHeights() {
|
||||
if !m.showLog {
|
||||
shareRequestsStyle.Height(m.height - m.headerHeight)
|
||||
} else {
|
||||
splitHeight := m.height - m.headerHeight
|
||||
shareRequestsStyle.Height(splitHeight/2 - 1)
|
||||
shareLogStyle.Height(splitHeight/2 - 1)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *shareModel) renderConfig() string {
|
||||
out := "["
|
||||
if m.shareMode == "public" {
|
||||
out += shareModePublicStyle.Render(strings.ToUpper(m.shareMode))
|
||||
} else {
|
||||
out += shareModePrivateStyle.Render(strings.ToUpper(m.shareMode))
|
||||
}
|
||||
out += "] ["
|
||||
if m.backendMode == "proxy" {
|
||||
out += backendModeProxyStyle.Render(strings.ToUpper(m.backendMode))
|
||||
} else {
|
||||
out += backendModeWebStyle.Render(strings.ToUpper(m.backendMode))
|
||||
}
|
||||
out += "]"
|
||||
return out
|
||||
}
|
||||
|
||||
func (m *shareModel) renderRequests() string {
|
||||
var requestLines []string
|
||||
for _, req := range m.requests {
|
||||
reqLine := fmt.Sprintf("%v %v -> %v %v",
|
||||
timeStyle.Render(req.Stamp.Format(time.RFC850)),
|
||||
addressStyle.Render(req.RemoteAddr),
|
||||
m.renderMethod(req.Method),
|
||||
req.Path,
|
||||
)
|
||||
reqLineWrapped := wordwrap.String(reqLine, m.width-2)
|
||||
splitWrapped := strings.Split(reqLineWrapped, "\n")
|
||||
for _, splitLine := range splitWrapped {
|
||||
splitLine := strings.ReplaceAll(splitLine, "\n", "")
|
||||
if splitLine != "" {
|
||||
requestLines = append(requestLines, splitLine)
|
||||
}
|
||||
}
|
||||
}
|
||||
maxRows := shareRequestsStyle.GetHeight()
|
||||
startRow := 0
|
||||
if len(requestLines) > maxRows {
|
||||
startRow = len(requestLines) - maxRows
|
||||
}
|
||||
out := ""
|
||||
for i := startRow; i < len(requestLines); i++ {
|
||||
outLine := requestLines[i]
|
||||
if i < len(requestLines)-1 {
|
||||
outLine += "\n"
|
||||
}
|
||||
out += outLine
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (m *shareModel) renderMethod(method string) string {
|
||||
switch strings.ToLower(method) {
|
||||
case "get":
|
||||
return getStyle.Render(method)
|
||||
case "post":
|
||||
return postStyle.Render(method)
|
||||
default:
|
||||
return otherMethodStyle.Render(method)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *shareModel) renderLog() string {
|
||||
var splitLines []string
|
||||
for _, line := range m.log {
|
||||
wrapped := wordwrap.String(line, m.width-2)
|
||||
wrappedLines := strings.Split(wrapped, "\n")
|
||||
for _, wrappedLine := range wrappedLines {
|
||||
splitLine := strings.ReplaceAll(wrappedLine, "\n", "")
|
||||
if splitLine != "" {
|
||||
splitLines = append(splitLines, splitLine)
|
||||
}
|
||||
}
|
||||
}
|
||||
maxRows := shareLogStyle.GetHeight()
|
||||
startRow := 0
|
||||
if len(splitLines) > maxRows {
|
||||
startRow = len(splitLines) - maxRows
|
||||
}
|
||||
out := ""
|
||||
for i := startRow; i < len(splitLines); i++ {
|
||||
outLine := splitLines[i]
|
||||
if i < len(splitLines)-1 {
|
||||
outLine += "\n"
|
||||
}
|
||||
out += outLine
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (m *shareModel) Write(p []byte) (n int, err error) {
|
||||
in := string(p)
|
||||
lines := strings.Split(in, "\n")
|
||||
for _, line := range lines {
|
||||
cleanLine := strings.ReplaceAll(line, "\n", "")
|
||||
if cleanLine != "" {
|
||||
m.prg.Send(shareLogLine(cleanLine))
|
||||
}
|
||||
}
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
var shareHeaderStyle = lipgloss.NewStyle().
|
||||
BorderStyle(lipgloss.RoundedBorder()).
|
||||
BorderForeground(lipgloss.Color("63")).
|
||||
Align(lipgloss.Center)
|
||||
|
||||
var shareConfigStyle = lipgloss.NewStyle().
|
||||
BorderStyle(lipgloss.RoundedBorder()).
|
||||
BorderForeground(lipgloss.Color("63")).
|
||||
Align(lipgloss.Center)
|
||||
|
||||
var shareRequestsStyle = lipgloss.NewStyle().
|
||||
BorderStyle(lipgloss.RoundedBorder()).
|
||||
BorderForeground(lipgloss.Color("63"))
|
||||
|
||||
var shareLogStyle = lipgloss.NewStyle().
|
||||
BorderStyle(lipgloss.RoundedBorder()).
|
||||
BorderForeground(lipgloss.Color("63"))
|
||||
|
||||
var shareModePublicStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#0F0"))
|
||||
var shareModePrivateStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#F00"))
|
||||
var backendModeProxyStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#FFA500"))
|
||||
var backendModeWebStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#0CC"))
|
||||
var timeStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#444"))
|
||||
var addressStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#FFA500"))
|
||||
var getStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("98"))
|
||||
var postStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("101"))
|
||||
var otherMethodStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("166"))
|
65
cmd/zrok/status.go
Normal file
65
cmd/zrok/status.go
Normal file
@ -0,0 +1,65 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/openziti-test-kitchen/zrok/tui"
|
||||
"github.com/openziti-test-kitchen/zrok/zrokdir"
|
||||
"github.com/spf13/cobra"
|
||||
"os"
|
||||
)
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(newStatusCommand().cmd)
|
||||
}
|
||||
|
||||
type statusCommand struct {
|
||||
cmd *cobra.Command
|
||||
}
|
||||
|
||||
func newStatusCommand() *statusCommand {
|
||||
cmd := &cobra.Command{
|
||||
Use: "status",
|
||||
Short: "Show the current environment status",
|
||||
Aliases: []string{"st"},
|
||||
Args: cobra.ExactArgs(0),
|
||||
}
|
||||
command := &statusCommand{cmd: cmd}
|
||||
cmd.Run = command.run
|
||||
return command
|
||||
}
|
||||
|
||||
func (cmd *statusCommand) run(_ *cobra.Command, _ []string) {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "\n")
|
||||
|
||||
zrd, err := zrokdir.Load()
|
||||
if err != nil {
|
||||
tui.Error("unable to load zrokdir", err)
|
||||
}
|
||||
|
||||
_, _ = fmt.Fprintf(os.Stdout, tui.CodeStyle.Render("Config")+":\n\n")
|
||||
t := table.NewWriter()
|
||||
t.SetOutputMirror(os.Stdout)
|
||||
t.SetStyle(table.StyleColoredDark)
|
||||
t.AppendHeader(table.Row{"Config", "Value", "Source"})
|
||||
apiEndpoint, from := zrd.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")
|
||||
_, _ = fmt.Fprintf(os.Stderr, "To create a local environment use the %v command.\n", tui.CodeStyle.Render("zrok enable"))
|
||||
} else {
|
||||
_, _ = fmt.Fprintf(os.Stdout, tui.CodeStyle.Render("Environment")+":\n\n")
|
||||
|
||||
t := table.NewWriter()
|
||||
t.SetOutputMirror(os.Stdout)
|
||||
t.SetStyle(table.StyleColoredDark)
|
||||
t.AppendHeader(table.Row{"Property", "Value"})
|
||||
t.AppendRow(table.Row{"Secret Token", zrd.Env.Token})
|
||||
t.AppendRow(table.Row{"Ziti Identity", zrd.Env.ZId})
|
||||
t.Render()
|
||||
}
|
||||
_, _ = fmt.Fprintf(os.Stdout, "\n")
|
||||
}
|
@ -3,7 +3,8 @@ package main
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/opentracing/opentracing-go/log"
|
||||
"github.com/openziti-test-kitchen/zrok/cmd/zrok/endpoint_ui"
|
||||
"github.com/openziti-test-kitchen/zrok/cmd/zrok/endpointUi"
|
||||
"github.com/openziti-test-kitchen/zrok/tui"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"html/template"
|
||||
@ -14,12 +15,6 @@ import (
|
||||
|
||||
func init() {
|
||||
testCmd.AddCommand(newTestEndpointCommand().cmd)
|
||||
rootCmd.AddCommand(testCmd)
|
||||
}
|
||||
|
||||
var testCmd = &cobra.Command{
|
||||
Use: "test",
|
||||
Short: "Utilities used for testing zrok",
|
||||
}
|
||||
|
||||
type testEndpointCommand struct {
|
||||
@ -37,13 +32,13 @@ func newTestEndpointCommand() *testEndpointCommand {
|
||||
}
|
||||
command := &testEndpointCommand{cmd: cmd}
|
||||
var err error
|
||||
if command.t, err = template.ParseFS(endpoint_ui.FS, "index.gohtml"); err != nil {
|
||||
if command.t, err = template.ParseFS(endpointUi.FS, "index.gohtml"); err != nil {
|
||||
if !panicInstead {
|
||||
showError("unable to parse index template", err)
|
||||
tui.Error("unable to parse index template", err)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
cmd.Flags().StringVarP(&command.address, "address", "a", "0.0.0.0", "The address for the HTTP listener")
|
||||
cmd.Flags().StringVarP(&command.address, "address", "a", "127.0.0.1", "The address for the HTTP listener")
|
||||
cmd.Flags().Uint16VarP(&command.port, "port", "P", 9090, "The port for the HTTP listener")
|
||||
cmd.Run = command.run
|
||||
return command
|
||||
@ -53,20 +48,21 @@ func (cmd *testEndpointCommand) run(_ *cobra.Command, _ []string) {
|
||||
http.HandleFunc("/", cmd.serveIndex)
|
||||
if err := http.ListenAndServe(fmt.Sprintf("%v:%d", cmd.address, cmd.port), nil); err != nil {
|
||||
if !panicInstead {
|
||||
showError("unable to start http listener", err)
|
||||
tui.Error("unable to start http listener", err)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (cmd *testEndpointCommand) serveIndex(w http.ResponseWriter, r *http.Request) {
|
||||
logrus.Infof("%v {%v} -> /index.gohtml", r.RemoteAddr, r.Host)
|
||||
logrus.Infof("%v {%v} | %v -> /index.gohtml", r.RemoteAddr, r.Host, r.RequestURI)
|
||||
if err := cmd.t.Execute(w, newEndpointData(r)); err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
type endpointData struct {
|
||||
RequestedPath string
|
||||
Now time.Time
|
||||
RemoteAddr string
|
||||
Host string
|
||||
@ -78,6 +74,7 @@ type endpointData struct {
|
||||
|
||||
func newEndpointData(r *http.Request) *endpointData {
|
||||
ed := &endpointData{
|
||||
RequestedPath: r.RequestURI,
|
||||
Now: time.Now(),
|
||||
HostHeader: r.Host,
|
||||
Headers: r.Header,
|
15
cmd/zrok/util.go
Normal file
15
cmd/zrok/util.go
Normal file
@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/go-openapi/runtime"
|
||||
httptransport "github.com/go-openapi/runtime/client"
|
||||
"os"
|
||||
)
|
||||
|
||||
func mustGetAdminAuth() runtime.ClientAuthInfoWriter {
|
||||
adminToken := os.Getenv("ZROK_ADMIN_TOKEN")
|
||||
if adminToken == "" {
|
||||
panic("please set ZROK_ADMIN_TOKEN to a valid admin token for your zrok instance")
|
||||
}
|
||||
return httptransport.APIKeyAuth("X-TOKEN", "header", adminToken)
|
||||
}
|
@ -2,7 +2,9 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/openziti-test-kitchen/zrok/build"
|
||||
"github.com/openziti-test-kitchen/zrok/tui"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@ -25,5 +27,6 @@ func newVersionCommand() *versionCommand {
|
||||
}
|
||||
|
||||
func (cmd *versionCommand) run(_ *cobra.Command, _ []string) {
|
||||
fmt.Println(" _ \n _____ __ ___ | | __\n|_ / '__/ _ \\| |/ /\n / /| | | (_) | < \n/___|_| \\___/|_|\\_\\\n\n" + build.String() + "\n")
|
||||
zrokStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("#FF00EE"))
|
||||
fmt.Println(zrokStyle.Render(" _ \n _____ __ ___ | | __\n|_ / '__/ _ \\| |/ /\n / /| | | (_) | < \n/___|_| \\___/|_|\\_\\") + "\n\n" + tui.CodeStyle.Render(build.String()) + "\n")
|
||||
}
|
||||
|
90
controller/access.go
Normal file
90
controller/access.go
Normal file
@ -0,0 +1,90 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/openziti-test-kitchen/zrok/controller/store"
|
||||
"github.com/openziti-test-kitchen/zrok/controller/zrokEdgeSdk"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_model_zrok"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_server_zrok/operations/share"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type accessHandler struct{}
|
||||
|
||||
func newAccessHandler() *accessHandler {
|
||||
return &accessHandler{}
|
||||
}
|
||||
|
||||
func (h *accessHandler) Handle(params share.AccessParams, principal *rest_model_zrok.Principal) middleware.Responder {
|
||||
tx, err := str.Begin()
|
||||
if err != nil {
|
||||
logrus.Errorf("error starting transaction: %v", err)
|
||||
return share.NewAccessInternalServerError()
|
||||
}
|
||||
defer func() { _ = tx.Rollback() }()
|
||||
|
||||
envZId := params.Body.EnvZID
|
||||
envId := 0
|
||||
if envs, err := str.FindEnvironmentsForAccount(int(principal.ID), tx); err == nil {
|
||||
found := false
|
||||
for _, env := range envs {
|
||||
if env.ZId == envZId {
|
||||
logrus.Debugf("found identity '%v' for user '%v'", envZId, principal.Email)
|
||||
envId = env.Id
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
logrus.Errorf("environment '%v' not found for user '%v'", envZId, principal.Email)
|
||||
return share.NewAccessUnauthorized()
|
||||
}
|
||||
} else {
|
||||
logrus.Errorf("error finding environments for account '%v'", principal.Email)
|
||||
return share.NewAccessNotFound()
|
||||
}
|
||||
|
||||
shrToken := params.Body.ShrToken
|
||||
sshr, err := str.FindShareWithToken(shrToken, tx)
|
||||
if err != nil {
|
||||
logrus.Errorf("error finding share")
|
||||
return share.NewAccessNotFound()
|
||||
}
|
||||
if sshr == nil {
|
||||
logrus.Errorf("unable to find share '%v' for user '%v'", shrToken, principal.Email)
|
||||
return share.NewAccessNotFound()
|
||||
}
|
||||
|
||||
feToken, err := createToken()
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
return share.NewAccessInternalServerError()
|
||||
}
|
||||
|
||||
if _, err := str.CreateFrontend(envId, &store.Frontend{Token: feToken, ZId: envZId}, tx); err != nil {
|
||||
logrus.Errorf("error creating frontend record: %v", err)
|
||||
return share.NewAccessInternalServerError()
|
||||
}
|
||||
|
||||
edge, err := edgeClient()
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
return share.NewAccessInternalServerError()
|
||||
}
|
||||
addlTags := map[string]interface{}{
|
||||
"zrokEnvironmentZId": envZId,
|
||||
"zrokFrontendToken": feToken,
|
||||
"zrokShareToken": shrToken,
|
||||
}
|
||||
if err := zrokEdgeSdk.CreateServicePolicyDial(envZId+"-"+sshr.ZId+"-dial", sshr.ZId, []string{envZId}, addlTags, edge); err != nil {
|
||||
logrus.Errorf("unable to create dial policy: %v", err)
|
||||
return share.NewAccessInternalServerError()
|
||||
}
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
logrus.Errorf("error committing frontend record: %v", err)
|
||||
return share.NewAccessInternalServerError()
|
||||
}
|
||||
|
||||
return share.NewAccessCreated().WithPayload(&rest_model_zrok.AccessResponse{FrontendToken: feToken})
|
||||
}
|
@ -1,76 +0,0 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/openziti-test-kitchen/zrok/controller/store"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_server_zrok/operations/identity"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type createAccountHandler struct {
|
||||
}
|
||||
|
||||
func newCreateAccountHandler() *createAccountHandler {
|
||||
return &createAccountHandler{}
|
||||
}
|
||||
|
||||
func (self *createAccountHandler) Handle(params identity.CreateAccountParams) middleware.Responder {
|
||||
if params.Body == nil || params.Body.Email == "" {
|
||||
logrus.Errorf("missing email")
|
||||
return identity.NewCreateAccountBadRequest().WithPayload("missing email")
|
||||
}
|
||||
logrus.Infof("received account request for email '%v'", params.Body.Email)
|
||||
|
||||
token, err := createToken()
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
return identity.NewCreateAccountInternalServerError()
|
||||
}
|
||||
ar := &store.AccountRequest{
|
||||
Token: token,
|
||||
Email: params.Body.Email,
|
||||
SourceAddress: params.HTTPRequest.RemoteAddr,
|
||||
}
|
||||
|
||||
tx, err := str.Begin()
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
return identity.NewCreateAccountInternalServerError()
|
||||
}
|
||||
defer func() { _ = tx.Rollback() }()
|
||||
|
||||
if _, err := str.FindAccountWithEmail(params.Body.Email, tx); err == nil {
|
||||
logrus.Errorf("found account for '%v', cannot process account request", params.Body.Email)
|
||||
return identity.NewCreateAccountBadRequest()
|
||||
} else {
|
||||
logrus.Infof("no account found for '%v': %v", params.Body.Email, err)
|
||||
}
|
||||
|
||||
if oldAr, err := str.FindAccountRequestWithEmail(params.Body.Email, tx); err == nil {
|
||||
logrus.Warnf("found previous account request for '%v', removing", params.Body.Email)
|
||||
if err := str.DeleteAccountRequest(oldAr.Id, tx); err != nil {
|
||||
logrus.Errorf("error deleteing previous account request for '%v': %v", params.Body.Email, err)
|
||||
return identity.NewCreateAccountInternalServerError()
|
||||
}
|
||||
} else {
|
||||
logrus.Warnf("error finding previous account request for '%v': %v", params.Body.Email, err)
|
||||
}
|
||||
|
||||
if _, err := str.CreateAccountRequest(ar, tx); err != nil {
|
||||
logrus.Errorf("error creating account request for '%v': %v", params.Body.Email, err)
|
||||
return identity.NewCreateAccountInternalServerError()
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
logrus.Errorf("error committing account request for '%v': %v", params.Body.Email, err)
|
||||
return identity.NewCreateAccountInternalServerError()
|
||||
}
|
||||
|
||||
if err := sendVerificationEmail(params.Body.Email, token); err != nil {
|
||||
logrus.Errorf("error sending verification email for '%v': %v", params.Body.Email, err)
|
||||
return identity.NewCreateAccountInternalServerError()
|
||||
}
|
||||
|
||||
logrus.Infof("account request for '%v' has registration token '%v'", params.Body.Email, ar.Token)
|
||||
|
||||
return identity.NewCreateAccountCreated()
|
||||
}
|
347
controller/bootstrap.go
Normal file
347
controller/bootstrap.go
Normal file
@ -0,0 +1,347 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/openziti-test-kitchen/zrok/controller/store"
|
||||
"github.com/openziti-test-kitchen/zrok/controller/zrokEdgeSdk"
|
||||
"github.com/openziti-test-kitchen/zrok/model"
|
||||
"github.com/openziti-test-kitchen/zrok/zrokdir"
|
||||
"github.com/openziti/edge/rest_management_api_client"
|
||||
"github.com/openziti/edge/rest_management_api_client/config"
|
||||
"github.com/openziti/edge/rest_management_api_client/edge_router_policy"
|
||||
"github.com/openziti/edge/rest_management_api_client/identity"
|
||||
"github.com/openziti/edge/rest_management_api_client/service"
|
||||
"github.com/openziti/edge/rest_management_api_client/service_edge_router_policy"
|
||||
"github.com/openziti/edge/rest_management_api_client/service_policy"
|
||||
"github.com/openziti/edge/rest_model"
|
||||
rest_model_edge "github.com/openziti/edge/rest_model"
|
||||
"github.com/openziti/sdk-golang/ziti"
|
||||
config2 "github.com/openziti/sdk-golang/ziti/config"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Bootstrap(skipCtrl, skipFrontend bool, inCfg *Config) error {
|
||||
cfg = inCfg
|
||||
|
||||
if v, err := store.Open(cfg.Store); err == nil {
|
||||
str = v
|
||||
} else {
|
||||
return errors.Wrap(err, "error opening store")
|
||||
}
|
||||
|
||||
logrus.Info("connecting to the ziti edge management api")
|
||||
edge, err := edgeClient()
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
var frontendZId string
|
||||
if !skipFrontend {
|
||||
logrus.Info("creating identity for frontend ziti access")
|
||||
|
||||
if frontendZId, err = getIdentityId("frontend"); err == nil {
|
||||
logrus.Infof("frontend identity: %v", frontendZId)
|
||||
} else {
|
||||
frontendZId, err = bootstrapIdentity("frontend", edge)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
if err := assertIdentity(frontendZId, edge); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := assertErpForIdentity("frontend", frontendZId, edge); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
tx, err := str.Begin()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer func() { _ = tx.Rollback() }()
|
||||
publicFe, err := str.FindFrontendWithZId(frontendZId, tx)
|
||||
if err != nil {
|
||||
logrus.Warnf("missing public frontend for ziti id '%v'; please use 'zrok admin create frontend %v public https://{token}.your.dns.name' to create a frontend instance", frontendZId, frontendZId)
|
||||
} else {
|
||||
if publicFe.PublicName != nil && publicFe.UrlTemplate != nil {
|
||||
logrus.Infof("found public frontend entry '%v' (%v) for ziti identity '%v'", *publicFe.PublicName, publicFe.Token, frontendZId)
|
||||
} else {
|
||||
logrus.Warnf("found frontend entry for ziti identity '%v'; missing either public name or url template", frontendZId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := assertZrokProxyConfigType(edge); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var metricsSvcZId string
|
||||
if metricsSvcZId, err = assertMetricsService(cfg, edge); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := assertMetricsSerp(metricsSvcZId, cfg, edge); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !skipCtrl {
|
||||
if err := assertCtrlMetricsBind(ctrlZId, metricsSvcZId, edge); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if !skipFrontend {
|
||||
if err := assertFrontendMetricsDial(frontendZId, metricsSvcZId, edge); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func assertZrokProxyConfigType(edge *rest_management_api_client.ZitiEdgeManagement) error {
|
||||
filter := fmt.Sprintf("name=\"%v\"", model.ZrokProxyConfig)
|
||||
limit := int64(100)
|
||||
offset := int64(0)
|
||||
listReq := &config.ListConfigTypesParams{
|
||||
Filter: &filter,
|
||||
Limit: &limit,
|
||||
Offset: &offset,
|
||||
Context: context.Background(),
|
||||
}
|
||||
listReq.SetTimeout(30 * time.Second)
|
||||
listResp, err := edge.Config.ListConfigTypes(listReq, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(listResp.Payload.Data) < 1 {
|
||||
name := model.ZrokProxyConfig
|
||||
ct := &rest_model.ConfigTypeCreate{Name: &name}
|
||||
createReq := &config.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)
|
||||
} 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)
|
||||
} else {
|
||||
logrus.Infof("found '%v' config type with id '%v'", model.ZrokProxyConfig, *(listResp.Payload.Data[0].ID))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getIdentityId(identityName string) (string, error) {
|
||||
zif, err := zrokdir.ZitiIdentityFile(identityName)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "error opening identity '%v' from zrokdir", identityName)
|
||||
}
|
||||
zcfg, err := config2.NewFromFile(zif)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "error loading ziti config from file '%v'", zif)
|
||||
}
|
||||
zctx := ziti.NewContextWithConfig(zcfg)
|
||||
id, err := zctx.GetCurrentIdentity()
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "error getting current identity from '%v'", zif)
|
||||
}
|
||||
return id.Id, nil
|
||||
}
|
||||
|
||||
func assertIdentity(zId string, edge *rest_management_api_client.ZitiEdgeManagement) error {
|
||||
filter := fmt.Sprintf("id=\"%v\"", zId)
|
||||
limit := int64(0)
|
||||
offset := int64(0)
|
||||
listReq := &identity.ListIdentitiesParams{
|
||||
Filter: &filter,
|
||||
Limit: &limit,
|
||||
Offset: &offset,
|
||||
}
|
||||
listReq.SetTimeout(30 * time.Second)
|
||||
listResp, err := edge.Identity.ListIdentities(listReq, nil)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error listing identities for '%v'", zId)
|
||||
}
|
||||
if len(listResp.Payload.Data) != 1 {
|
||||
return errors.Wrapf(err, "found %d identities for '%v'", len(listResp.Payload.Data), zId)
|
||||
}
|
||||
logrus.Infof("asserted identity '%v'", zId)
|
||||
return nil
|
||||
}
|
||||
|
||||
func bootstrapIdentity(name string, edge *rest_management_api_client.ZitiEdgeManagement) (string, error) {
|
||||
idc, err := zrokEdgeSdk.CreateIdentity(name, rest_model_edge.IdentityTypeDevice, nil, edge)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "error creating '%v' identity", name)
|
||||
}
|
||||
|
||||
zId := idc.Payload.Data.ID
|
||||
cfg, err := zrokEdgeSdk.EnrollIdentity(zId, edge)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "error enrolling '%v' identity", name)
|
||||
}
|
||||
|
||||
var out bytes.Buffer
|
||||
enc := json.NewEncoder(&out)
|
||||
enc.SetEscapeHTML(false)
|
||||
err = enc.Encode(&cfg)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "error encoding identity config '%v'", name)
|
||||
}
|
||||
if err := zrokdir.SaveZitiIdentity(name, out.String()); err != nil {
|
||||
return "", errors.Wrapf(err, "error saving identity config '%v'", name)
|
||||
}
|
||||
return zId, nil
|
||||
}
|
||||
|
||||
func assertErpForIdentity(name, zId string, edge *rest_management_api_client.ZitiEdgeManagement) error {
|
||||
filter := fmt.Sprintf("name=\"%v\" and tags.zrok != null", name)
|
||||
limit := int64(0)
|
||||
offset := int64(0)
|
||||
listReq := &edge_router_policy.ListEdgeRouterPoliciesParams{
|
||||
Filter: &filter,
|
||||
Limit: &limit,
|
||||
Offset: &offset,
|
||||
}
|
||||
listReq.SetTimeout(30 * time.Second)
|
||||
listResp, err := edge.EdgeRouterPolicy.ListEdgeRouterPolicies(listReq, nil)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error listing edge router policies for '%v' (%v)", name, zId)
|
||||
}
|
||||
if len(listResp.Payload.Data) != 1 {
|
||||
logrus.Infof("creating erp for '%v' (%v)", name, zId)
|
||||
if err := zrokEdgeSdk.CreateEdgeRouterPolicy(name, zId, edge); err != nil {
|
||||
return errors.Wrapf(err, "error creating erp for '%v' (%v)", name, zId)
|
||||
}
|
||||
}
|
||||
logrus.Infof("asserted erps for '%v' (%v)", name, zId)
|
||||
return nil
|
||||
}
|
||||
|
||||
func assertMetricsService(cfg *Config, edge *rest_management_api_client.ZitiEdgeManagement) (string, error) {
|
||||
filter := fmt.Sprintf("name=\"%v\" and tags.zrok != null", cfg.Metrics.ServiceName)
|
||||
limit := int64(0)
|
||||
offset := int64(0)
|
||||
listReq := &service.ListServicesParams{
|
||||
Filter: &filter,
|
||||
Limit: &limit,
|
||||
Offset: &offset,
|
||||
}
|
||||
listReq.SetTimeout(30 * time.Second)
|
||||
listResp, err := edge.Service.ListServices(listReq, nil)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "error listing '%v' service", cfg.Metrics.ServiceName)
|
||||
}
|
||||
var svcZId string
|
||||
if len(listResp.Payload.Data) != 1 {
|
||||
logrus.Infof("creating '%v' service", cfg.Metrics.ServiceName)
|
||||
svcZId, err = zrokEdgeSdk.CreateService("metrics", nil, nil, edge)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "error creating '%v' service", cfg.Metrics.ServiceName)
|
||||
}
|
||||
} else {
|
||||
svcZId = *listResp.Payload.Data[0].ID
|
||||
}
|
||||
|
||||
logrus.Infof("asserted '%v' service (%v)", cfg.Metrics.ServiceName, svcZId)
|
||||
return svcZId, nil
|
||||
}
|
||||
|
||||
func assertMetricsSerp(metricsSvcZId string, cfg *Config, edge *rest_management_api_client.ZitiEdgeManagement) error {
|
||||
filter := fmt.Sprintf("allOf(serviceRoles) = \"@%v\" and allOf(edgeRouterRoles) = \"#all\" and tags.zrok != null", metricsSvcZId)
|
||||
limit := int64(0)
|
||||
offset := int64(0)
|
||||
listReq := &service_edge_router_policy.ListServiceEdgeRouterPoliciesParams{
|
||||
Filter: &filter,
|
||||
Limit: &limit,
|
||||
Offset: &offset,
|
||||
}
|
||||
listReq.SetTimeout(30 * time.Second)
|
||||
listResp, err := edge.ServiceEdgeRouterPolicy.ListServiceEdgeRouterPolicies(listReq, nil)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error listing '%v' serps", cfg.Metrics.ServiceName)
|
||||
}
|
||||
if len(listResp.Payload.Data) != 1 {
|
||||
logrus.Infof("creating '%v' serp", cfg.Metrics.ServiceName)
|
||||
_, err := zrokEdgeSdk.CreateServiceEdgeRouterPolicy(cfg.Metrics.ServiceName, metricsSvcZId, nil, edge)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error creating '%v' serp", cfg.Metrics.ServiceName)
|
||||
}
|
||||
}
|
||||
logrus.Infof("asserted '%v' serp", cfg.Metrics.ServiceName)
|
||||
return nil
|
||||
}
|
||||
|
||||
func assertCtrlMetricsBind(ctrlZId, metricsSvcZId string, edge *rest_management_api_client.ZitiEdgeManagement) error {
|
||||
filter := fmt.Sprintf("allOf(serviceRoles) = \"@%v\" and allOf(identityRoles) = \"@%v\" and type = 2 and tags.zrok != null", metricsSvcZId, ctrlZId)
|
||||
limit := int64(0)
|
||||
offset := int64(0)
|
||||
listReq := &service_policy.ListServicePoliciesParams{
|
||||
Filter: &filter,
|
||||
Limit: &limit,
|
||||
Offset: &offset,
|
||||
}
|
||||
listReq.SetTimeout(30 * time.Second)
|
||||
listResp, err := edge.ServicePolicy.ListServicePolicies(listReq, nil)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error listing 'ctrl-metrics-bind' service policy")
|
||||
}
|
||||
if len(listResp.Payload.Data) != 1 {
|
||||
logrus.Info("creating 'ctrl-metrics-bind' service policy")
|
||||
if err = zrokEdgeSdk.CreateServicePolicyBind("ctrl-metrics-bind", metricsSvcZId, ctrlZId, nil, edge); err != nil {
|
||||
return errors.Wrap(err, "error creating 'ctrl-metrics-bind' service policy")
|
||||
}
|
||||
}
|
||||
logrus.Infof("asserted 'ctrl-metrics-bind' service policy")
|
||||
return nil
|
||||
}
|
||||
|
||||
func assertFrontendMetricsDial(frontendZId, metricsSvcZId string, edge *rest_management_api_client.ZitiEdgeManagement) error {
|
||||
filter := fmt.Sprintf("allOf(serviceRoles) = \"@%v\" and allOf(identityRoles) = \"@%v\" and type = 1 and tags.zrok != null", metricsSvcZId, frontendZId)
|
||||
limit := int64(0)
|
||||
offset := int64(0)
|
||||
listReq := &service_policy.ListServicePoliciesParams{
|
||||
Filter: &filter,
|
||||
Limit: &limit,
|
||||
Offset: &offset,
|
||||
}
|
||||
listReq.SetTimeout(30 * time.Second)
|
||||
listResp, err := edge.ServicePolicy.ListServicePolicies(listReq, nil)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error listing 'frontend-metrics-dial' service policy")
|
||||
}
|
||||
if len(listResp.Payload.Data) != 1 {
|
||||
logrus.Info("creating 'frontend-metrics-dial' service policy")
|
||||
if err = zrokEdgeSdk.CreateServicePolicyDial("frontend-metrics-dial", metricsSvcZId, []string{frontendZId}, nil, edge); err != nil {
|
||||
return errors.Wrap(err, "error creating 'frontend-metrics-dial' service policy")
|
||||
}
|
||||
}
|
||||
logrus.Infof("asserted 'frontend-metrics-dial' service policy")
|
||||
return nil
|
||||
}
|
@ -4,17 +4,27 @@ import (
|
||||
"github.com/michaelquigley/cf"
|
||||
"github.com/openziti-test-kitchen/zrok/controller/store"
|
||||
"github.com/pkg/errors"
|
||||
"time"
|
||||
)
|
||||
|
||||
const ConfigVersion = 1
|
||||
|
||||
type Config struct {
|
||||
V int
|
||||
Admin *AdminConfig
|
||||
Endpoint *EndpointConfig
|
||||
Proxy *ProxyConfig
|
||||
Email *EmailConfig
|
||||
Influx *InfluxConfig
|
||||
Limits *LimitsConfig
|
||||
Maintenance *MaintenanceConfig
|
||||
Metrics *MetricsConfig
|
||||
Registration *RegistrationConfig
|
||||
Store *store.Config
|
||||
Ziti *ZitiConfig
|
||||
Metrics *MetricsConfig
|
||||
Influx *InfluxConfig
|
||||
}
|
||||
|
||||
type AdminConfig struct {
|
||||
Secrets []string `cf:"+secret"`
|
||||
}
|
||||
|
||||
type EndpointConfig struct {
|
||||
@ -22,11 +32,6 @@ type EndpointConfig struct {
|
||||
Port int
|
||||
}
|
||||
|
||||
type ProxyConfig struct {
|
||||
UrlTemplate string
|
||||
Identities []string
|
||||
}
|
||||
|
||||
type EmailConfig struct {
|
||||
Host string
|
||||
Port int
|
||||
@ -37,6 +42,7 @@ type EmailConfig struct {
|
||||
type RegistrationConfig struct {
|
||||
EmailFrom string
|
||||
RegistrationUrlTemplate string
|
||||
TokenStrategy string
|
||||
}
|
||||
|
||||
type ZitiConfig struct {
|
||||
@ -56,10 +62,49 @@ type InfluxConfig struct {
|
||||
Token string `cf:"+secret"`
|
||||
}
|
||||
|
||||
type MaintenanceConfig struct {
|
||||
Registration *RegistrationMaintenanceConfig
|
||||
}
|
||||
|
||||
type RegistrationMaintenanceConfig struct {
|
||||
ExpirationTimeout time.Duration
|
||||
CheckFrequency time.Duration
|
||||
BatchLimit int
|
||||
}
|
||||
|
||||
const Unlimited = -1
|
||||
|
||||
type LimitsConfig struct {
|
||||
Environments int
|
||||
Shares int
|
||||
}
|
||||
|
||||
func DefaultConfig() *Config {
|
||||
return &Config{
|
||||
Limits: &LimitsConfig{
|
||||
Environments: Unlimited,
|
||||
Shares: Unlimited,
|
||||
},
|
||||
Metrics: &MetricsConfig{
|
||||
ServiceName: "metrics",
|
||||
},
|
||||
Maintenance: &MaintenanceConfig{
|
||||
Registration: &RegistrationMaintenanceConfig{
|
||||
ExpirationTimeout: time.Hour * 24,
|
||||
CheckFrequency: time.Hour,
|
||||
BatchLimit: 500,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func LoadConfig(path string) (*Config, error) {
|
||||
cfg := &Config{}
|
||||
cfg := DefaultConfig()
|
||||
if err := cf.BindYaml(cfg, path, cf.DefaultOptions()); err != nil {
|
||||
return nil, errors.Wrapf(err, "error loading controller config '%v'", path)
|
||||
}
|
||||
if cfg.V != ConfigVersion {
|
||||
return nil, errors.Errorf("expecting configuration version '%v', your configuration is version '%v'; please see zrok.io for changelog and configuration documentation", ConfigVersion, cfg.V)
|
||||
}
|
||||
return cfg, nil
|
||||
}
|
||||
|
@ -1,12 +1,14 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/go-openapi/loads"
|
||||
influxdb2 "github.com/influxdata/influxdb-client-go/v2"
|
||||
"github.com/openziti-test-kitchen/zrok/controller/store"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_server_zrok"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_server_zrok/operations"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_server_zrok/operations/identity"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_server_zrok/operations/account"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_server_zrok/operations/metadata"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
@ -25,17 +27,28 @@ func Run(inCfg *Config) error {
|
||||
}
|
||||
|
||||
api := operations.NewZrokAPI(swaggerSpec)
|
||||
api.KeyAuth = ZrokAuthenticate
|
||||
api.IdentityCreateAccountHandler = newCreateAccountHandler()
|
||||
api.IdentityEnableHandler = newEnableHandler()
|
||||
api.IdentityDisableHandler = newDisableHandler()
|
||||
api.IdentityLoginHandler = identity.LoginHandlerFunc(loginHandler)
|
||||
api.IdentityRegisterHandler = newRegisterHandler()
|
||||
api.IdentityVerifyHandler = newVerifyHandler()
|
||||
api.KeyAuth = newZrokAuthenticator(cfg).authenticate
|
||||
api.AccountInviteHandler = newInviteHandler(cfg)
|
||||
api.AccountLoginHandler = account.LoginHandlerFunc(loginHandler)
|
||||
api.AccountRegisterHandler = newRegisterHandler()
|
||||
api.AccountVerifyHandler = newVerifyHandler()
|
||||
api.AdminCreateFrontendHandler = newCreateFrontendHandler()
|
||||
api.AdminCreateIdentityHandler = newCreateIdentityHandler()
|
||||
api.AdminDeleteFrontendHandler = newDeleteFrontendHandler()
|
||||
api.AdminInviteTokenGenerateHandler = newInviteTokenGenerateHandler()
|
||||
api.AdminListFrontendsHandler = newListFrontendsHandler()
|
||||
api.AdminUpdateFrontendHandler = newUpdateFrontendHandler()
|
||||
api.EnvironmentEnableHandler = newEnableHandler(cfg.Limits)
|
||||
api.EnvironmentDisableHandler = newDisableHandler()
|
||||
api.MetadataGetEnvironmentDetailHandler = newEnvironmentDetailHandler()
|
||||
api.MetadataGetShareDetailHandler = newShareDetailHandler()
|
||||
api.MetadataOverviewHandler = metadata.OverviewHandlerFunc(overviewHandler)
|
||||
api.MetadataVersionHandler = metadata.VersionHandlerFunc(versionHandler)
|
||||
api.TunnelTunnelHandler = newTunnelHandler()
|
||||
api.TunnelUntunnelHandler = newUntunnelHandler()
|
||||
api.ShareAccessHandler = newAccessHandler()
|
||||
api.ShareShareHandler = newShareHandler(cfg.Limits)
|
||||
api.ShareUnaccessHandler = newUnaccessHandler()
|
||||
api.ShareUnshareHandler = newUnshareHandler()
|
||||
api.ShareUpdateShareHandler = newUpdateShareHandler()
|
||||
|
||||
if err := controllerStartup(); err != nil {
|
||||
return err
|
||||
@ -60,6 +73,15 @@ func Run(inCfg *Config) error {
|
||||
}()
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer func() {
|
||||
cancel()
|
||||
}()
|
||||
|
||||
if cfg.Maintenance != nil && cfg.Maintenance.Registration != nil {
|
||||
go newMaintenanceAgent(ctx, cfg.Maintenance).run()
|
||||
}
|
||||
|
||||
server := rest_server_zrok.NewServer(api)
|
||||
defer func() { _ = server.Shutdown() }()
|
||||
server.Host = cfg.Endpoint.Host
|
||||
|
75
controller/createFrontend.go
Normal file
75
controller/createFrontend.go
Normal file
@ -0,0 +1,75 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/openziti-test-kitchen/zrok/controller/store"
|
||||
"github.com/openziti-test-kitchen/zrok/controller/zrokEdgeSdk"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_model_zrok"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_server_zrok/operations/admin"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type createFrontendHandler struct{}
|
||||
|
||||
func newCreateFrontendHandler() *createFrontendHandler {
|
||||
return &createFrontendHandler{}
|
||||
}
|
||||
|
||||
func (h *createFrontendHandler) Handle(params admin.CreateFrontendParams, principal *rest_model_zrok.Principal) middleware.Responder {
|
||||
if !principal.Admin {
|
||||
logrus.Errorf("invalid admin principal")
|
||||
return admin.NewCreateFrontendUnauthorized()
|
||||
}
|
||||
|
||||
client, err := edgeClient()
|
||||
if err != nil {
|
||||
logrus.Errorf("error getting edge client: %v", err)
|
||||
return admin.NewCreateFrontendInternalServerError()
|
||||
}
|
||||
|
||||
zId := params.Body.ZID
|
||||
detail, err := zrokEdgeSdk.GetIdentityByZId(zId, client)
|
||||
if err != nil {
|
||||
logrus.Errorf("error getting identity details for '%v': %v", zId, err)
|
||||
return admin.NewCreateFrontendInternalServerError()
|
||||
}
|
||||
if len(detail.Payload.Data) != 1 {
|
||||
logrus.Errorf("expected a single identity to be returned for '%v'", zId)
|
||||
return admin.NewCreateFrontendNotFound()
|
||||
}
|
||||
logrus.Infof("found frontend identity '%v'", *detail.Payload.Data[0].Name)
|
||||
|
||||
tx, err := str.Begin()
|
||||
if err != nil {
|
||||
logrus.Errorf("error starting transaction: %v", err)
|
||||
return admin.NewCreateFrontendInternalServerError()
|
||||
}
|
||||
defer func() { _ = tx.Rollback() }()
|
||||
|
||||
feToken, err := createToken()
|
||||
if err != nil {
|
||||
logrus.Errorf("error creating frontend token: %v", err)
|
||||
return admin.NewCreateFrontendInternalServerError()
|
||||
}
|
||||
|
||||
fe := &store.Frontend{
|
||||
Token: feToken,
|
||||
ZId: params.Body.ZID,
|
||||
PublicName: ¶ms.Body.PublicName,
|
||||
UrlTemplate: ¶ms.Body.URLTemplate,
|
||||
Reserved: true,
|
||||
}
|
||||
if _, err := str.CreateGlobalFrontend(fe, tx); err != nil {
|
||||
logrus.Errorf("error creating frontend record: %v", err)
|
||||
return admin.NewCreateFrontendInternalServerError()
|
||||
}
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
logrus.Errorf("error committing frontend record: %v", err)
|
||||
return admin.NewCreateFrontendInternalServerError()
|
||||
}
|
||||
|
||||
logrus.Infof("created global frontend '%v' with public name '%v'", fe.Token, *fe.PublicName)
|
||||
|
||||
return admin.NewCreateFrontendCreated().WithPayload(&rest_model_zrok.CreateFrontendResponse{Token: feToken})
|
||||
}
|
94
controller/createIdentity.go
Normal file
94
controller/createIdentity.go
Normal file
@ -0,0 +1,94 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/openziti-test-kitchen/zrok/controller/zrokEdgeSdk"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_model_zrok"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_server_zrok/operations/admin"
|
||||
"github.com/openziti/edge/rest_management_api_client/service"
|
||||
rest_model_edge "github.com/openziti/edge/rest_model"
|
||||
"github.com/sirupsen/logrus"
|
||||
"time"
|
||||
)
|
||||
|
||||
type createIdentityHandler struct{}
|
||||
|
||||
func newCreateIdentityHandler() *createIdentityHandler {
|
||||
return &createIdentityHandler{}
|
||||
}
|
||||
|
||||
func (h *createIdentityHandler) Handle(params admin.CreateIdentityParams, principal *rest_model_zrok.Principal) middleware.Responder {
|
||||
name := params.Body.Name
|
||||
|
||||
if !principal.Admin {
|
||||
logrus.Errorf("invalid admin principal")
|
||||
return admin.NewCreateIdentityUnauthorized()
|
||||
}
|
||||
|
||||
edge, err := edgeClient()
|
||||
if err != nil {
|
||||
logrus.Errorf("error getting edge client: %v", err)
|
||||
return admin.NewCreateIdentityInternalServerError()
|
||||
}
|
||||
|
||||
idc, err := zrokEdgeSdk.CreateIdentity(name, rest_model_edge.IdentityTypeService, nil, edge)
|
||||
if err != nil {
|
||||
logrus.Errorf("error creating identity: %v", err)
|
||||
return admin.NewCreateIdentityInternalServerError()
|
||||
}
|
||||
|
||||
zId := idc.Payload.Data.ID
|
||||
idCfg, err := zrokEdgeSdk.EnrollIdentity(zId, edge)
|
||||
if err != nil {
|
||||
logrus.Errorf("error enrolling identity: %v", err)
|
||||
return admin.NewCreateIdentityInternalServerError()
|
||||
}
|
||||
|
||||
if err := zrokEdgeSdk.CreateEdgeRouterPolicy(name, zId, edge); err != nil {
|
||||
logrus.Errorf("error creating edge router policy for identity: %v", err)
|
||||
return admin.NewCreateIdentityInternalServerError()
|
||||
}
|
||||
|
||||
filter := fmt.Sprintf("name=\"%v\" and tags.zrok != null", cfg.Metrics.ServiceName)
|
||||
limit := int64(0)
|
||||
offset := int64(0)
|
||||
listSvcReq := &service.ListServicesParams{
|
||||
Filter: &filter,
|
||||
Limit: &limit,
|
||||
Offset: &offset,
|
||||
}
|
||||
listSvcReq.SetTimeout(30 * time.Second)
|
||||
listSvcResp, err := edge.Service.ListServices(listSvcReq, nil)
|
||||
if err != nil {
|
||||
logrus.Errorf("error listing metrics service: %v", err)
|
||||
return admin.NewCreateIdentityInternalServerError()
|
||||
}
|
||||
if len(listSvcResp.Payload.Data) != 1 {
|
||||
logrus.Errorf("could not find metrics service")
|
||||
return admin.NewCreateIdentityInternalServerError()
|
||||
}
|
||||
svcZId := *listSvcResp.Payload.Data[0].ID
|
||||
|
||||
spName := fmt.Sprintf("%v-%v-dial", name, cfg.Metrics.ServiceName)
|
||||
if err := zrokEdgeSdk.CreateServicePolicyDial(spName, svcZId, []string{zId}, nil, edge); err != nil {
|
||||
logrus.Errorf("error creating named dial service policy '%v': %v", spName, err)
|
||||
return admin.NewCreateIdentityInternalServerError()
|
||||
}
|
||||
|
||||
var out bytes.Buffer
|
||||
enc := json.NewEncoder(&out)
|
||||
enc.SetEscapeHTML(false)
|
||||
err = enc.Encode(&idCfg)
|
||||
if err != nil {
|
||||
logrus.Errorf("error encoding identity config: %v", err)
|
||||
return admin.NewCreateFrontendInternalServerError()
|
||||
}
|
||||
|
||||
return admin.NewCreateIdentityCreated().WithPayload(&admin.CreateIdentityCreatedBody{
|
||||
Identity: zId,
|
||||
Cfg: out.String(),
|
||||
})
|
||||
}
|
49
controller/deleteFrontend.go
Normal file
49
controller/deleteFrontend.go
Normal file
@ -0,0 +1,49 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_model_zrok"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_server_zrok/operations/admin"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type deleteFrontendHandler struct{}
|
||||
|
||||
func newDeleteFrontendHandler() *deleteFrontendHandler {
|
||||
return &deleteFrontendHandler{}
|
||||
}
|
||||
|
||||
func (h *deleteFrontendHandler) Handle(params admin.DeleteFrontendParams, principal *rest_model_zrok.Principal) middleware.Responder {
|
||||
feToken := params.Body.FrontendToken
|
||||
|
||||
if !principal.Admin {
|
||||
logrus.Errorf("invalid admin principal")
|
||||
return admin.NewDeleteFrontendUnauthorized()
|
||||
}
|
||||
|
||||
tx, err := str.Begin()
|
||||
if err != nil {
|
||||
logrus.Errorf("error starting transaction: %v", err)
|
||||
return admin.NewDeleteFrontendInternalServerError()
|
||||
}
|
||||
defer func() { _ = tx.Rollback() }()
|
||||
|
||||
fe, err := str.FindFrontendWithToken(feToken, tx)
|
||||
if err != nil {
|
||||
logrus.Errorf("error finding frontend with token '%v': %v", feToken, err)
|
||||
return admin.NewDeleteFrontendNotFound()
|
||||
}
|
||||
|
||||
if err := str.DeleteFrontend(fe.Id, tx); err != nil {
|
||||
logrus.Errorf("error deleting frontend '%v': %v", feToken, err)
|
||||
return admin.NewDeleteFrontendInternalServerError()
|
||||
}
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
logrus.Errorf("error commiting frontend '%v' deletion: %v", feToken, err)
|
||||
return admin.NewDeleteFrontendInternalServerError()
|
||||
}
|
||||
|
||||
return admin.NewDeleteFrontendOK()
|
||||
|
||||
}
|
@ -3,8 +3,9 @@ package controller
|
||||
import (
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/openziti-test-kitchen/zrok/controller/zrokEdgeSdk"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_model_zrok"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_server_zrok/operations/identity"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_server_zrok/operations/environment"
|
||||
"github.com/openziti/edge/rest_management_api_client"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
@ -17,51 +18,51 @@ func newDisableHandler() *disableHandler {
|
||||
return &disableHandler{}
|
||||
}
|
||||
|
||||
func (self *disableHandler) Handle(params identity.DisableParams, principal *rest_model_zrok.Principal) middleware.Responder {
|
||||
func (h *disableHandler) Handle(params environment.DisableParams, principal *rest_model_zrok.Principal) middleware.Responder {
|
||||
tx, err := str.Begin()
|
||||
if err != nil {
|
||||
logrus.Errorf("error starting transaction: %v", err)
|
||||
return identity.NewDisableInternalServerError()
|
||||
return environment.NewDisableInternalServerError()
|
||||
}
|
||||
defer func() { _ = tx.Rollback() }()
|
||||
envId, err := self.checkZitiIdentity(params.Body.Identity, principal, tx)
|
||||
envId, err := h.checkZitiIdentity(params.Body.Identity, principal, tx)
|
||||
if err != nil {
|
||||
logrus.Errorf("identity check failed: %v", err)
|
||||
return identity.NewDisableUnauthorized()
|
||||
return environment.NewDisableUnauthorized()
|
||||
}
|
||||
env, err := str.GetEnvironment(envId, tx)
|
||||
if err != nil {
|
||||
logrus.Errorf("error getting environment: %v", err)
|
||||
return identity.NewDisableInternalServerError()
|
||||
return environment.NewDisableInternalServerError()
|
||||
}
|
||||
edge, err := edgeClient()
|
||||
if err != nil {
|
||||
logrus.Errorf("error getting edge client: %v", err)
|
||||
return identity.NewDisableInternalServerError()
|
||||
return environment.NewDisableInternalServerError()
|
||||
}
|
||||
if err := self.removeServicesForEnvironment(envId, tx, edge); err != nil {
|
||||
logrus.Errorf("error removing services for environment: %v", err)
|
||||
return identity.NewDisableInternalServerError()
|
||||
if err := h.removeSharesForEnvironment(envId, tx, edge); err != nil {
|
||||
logrus.Errorf("error removing shares for environment: %v", err)
|
||||
return environment.NewDisableInternalServerError()
|
||||
}
|
||||
if err := self.removeEnvironment(envId, tx); err != nil {
|
||||
if err := h.removeEnvironment(envId, tx); err != nil {
|
||||
logrus.Errorf("error removing environment: %v", err)
|
||||
return identity.NewDisableInternalServerError()
|
||||
return environment.NewDisableInternalServerError()
|
||||
}
|
||||
if err := deleteEdgeRouterPolicy(env.ZId, params.Body.Identity, edge); err != nil {
|
||||
if err := zrokEdgeSdk.DeleteEdgeRouterPolicy(env.ZId, edge); err != nil {
|
||||
logrus.Errorf("error deleting edge router policy: %v", err)
|
||||
return identity.NewDisableInternalServerError()
|
||||
return environment.NewDisableInternalServerError()
|
||||
}
|
||||
if err := deleteIdentity(params.Body.Identity, edge); err != nil {
|
||||
if err := zrokEdgeSdk.DeleteIdentity(params.Body.Identity, edge); err != nil {
|
||||
logrus.Errorf("error deleting identity: %v", err)
|
||||
return identity.NewDisableInternalServerError()
|
||||
return environment.NewDisableInternalServerError()
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
logrus.Errorf("error committing: %v", err)
|
||||
}
|
||||
return identity.NewDisableOK()
|
||||
return environment.NewDisableOK()
|
||||
}
|
||||
|
||||
func (self *disableHandler) checkZitiIdentity(id string, principal *rest_model_zrok.Principal, tx *sqlx.Tx) (int, error) {
|
||||
func (h *disableHandler) checkZitiIdentity(id string, principal *rest_model_zrok.Principal, tx *sqlx.Tx) (int, error) {
|
||||
envs, err := str.FindEnvironmentsForAccount(int(principal.ID), tx)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
@ -74,39 +75,48 @@ func (self *disableHandler) checkZitiIdentity(id string, principal *rest_model_z
|
||||
return -1, errors.Errorf("no such environment '%v'", id)
|
||||
}
|
||||
|
||||
func (self *disableHandler) removeServicesForEnvironment(envId int, tx *sqlx.Tx, edge *rest_management_api_client.ZitiEdgeManagement) error {
|
||||
func (h *disableHandler) removeSharesForEnvironment(envId int, tx *sqlx.Tx, edge *rest_management_api_client.ZitiEdgeManagement) error {
|
||||
env, err := str.GetEnvironment(envId, tx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
svcs, err := str.FindServicesForEnvironment(envId, tx)
|
||||
shrs, err := str.FindSharesForEnvironment(envId, tx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, svc := range svcs {
|
||||
svcName := svc.Name
|
||||
logrus.Infof("garbage collecting service '%v' for environment '%v'", svcName, env.ZId)
|
||||
if err := deleteServiceEdgeRouterPolicy(env.ZId, svcName, edge); err != nil {
|
||||
for _, shr := range shrs {
|
||||
shrToken := shr.Token
|
||||
logrus.Infof("garbage collecting share '%v' for environment '%v'", shrToken, env.ZId)
|
||||
if err := zrokEdgeSdk.DeleteServiceEdgeRouterPolicy(env.ZId, shrToken, edge); err != nil {
|
||||
logrus.Error(err)
|
||||
}
|
||||
if err := deleteServicePolicyDial(env.ZId, svcName, edge); err != nil {
|
||||
if err := zrokEdgeSdk.DeleteServicePolicyDial(env.ZId, shrToken, edge); err != nil {
|
||||
logrus.Error(err)
|
||||
}
|
||||
if err := deleteServicePolicyBind(env.ZId, svcName, edge); err != nil {
|
||||
if err := zrokEdgeSdk.DeleteServicePolicyBind(env.ZId, shrToken, edge); err != nil {
|
||||
logrus.Error(err)
|
||||
}
|
||||
if err := deleteConfig(env.ZId, svcName, edge); err != nil {
|
||||
if err := zrokEdgeSdk.DeleteConfig(env.ZId, shrToken, edge); err != nil {
|
||||
logrus.Error(err)
|
||||
}
|
||||
if err := deleteService(env.ZId, svc.ZId, edge); err != nil {
|
||||
if err := zrokEdgeSdk.DeleteService(env.ZId, shr.ZId, edge); err != nil {
|
||||
logrus.Error(err)
|
||||
}
|
||||
logrus.Infof("removed service '%v' for environment '%v'", svc.Name, env.ZId)
|
||||
logrus.Infof("removed share '%v' for environment '%v'", shr.Token, env.ZId)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *disableHandler) removeEnvironment(envId int, tx *sqlx.Tx) error {
|
||||
func (h *disableHandler) removeEnvironment(envId int, tx *sqlx.Tx) error {
|
||||
shrs, err := str.FindSharesForEnvironment(envId, tx)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error finding shares for environment '%d'", envId)
|
||||
}
|
||||
for _, shr := range shrs {
|
||||
if err := str.DeleteShare(shr.Id, tx); err != nil {
|
||||
return errors.Wrapf(err, "error deleting share '%d' for environment '%d'", shr.Id, envId)
|
||||
}
|
||||
}
|
||||
if err := str.DeleteEnvironment(envId, tx); err != nil {
|
||||
return errors.Wrapf(err, "error deleting environment '%d'", envId)
|
||||
}
|
||||
|
@ -1,178 +0,0 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/openziti/edge/rest_management_api_client"
|
||||
"github.com/openziti/edge/rest_management_api_client/config"
|
||||
"github.com/openziti/edge/rest_management_api_client/edge_router_policy"
|
||||
identity_edge "github.com/openziti/edge/rest_management_api_client/identity"
|
||||
"github.com/openziti/edge/rest_management_api_client/service"
|
||||
"github.com/openziti/edge/rest_management_api_client/service_edge_router_policy"
|
||||
"github.com/openziti/edge/rest_management_api_client/service_policy"
|
||||
"github.com/sirupsen/logrus"
|
||||
"time"
|
||||
)
|
||||
|
||||
func deleteServiceEdgeRouterPolicy(envZId, svcName string, edge *rest_management_api_client.ZitiEdgeManagement) error {
|
||||
filter := fmt.Sprintf("name=\"%v\"", svcName)
|
||||
limit := int64(1)
|
||||
offset := int64(0)
|
||||
listReq := &service_edge_router_policy.ListServiceEdgeRouterPoliciesParams{
|
||||
Filter: &filter,
|
||||
Limit: &limit,
|
||||
Offset: &offset,
|
||||
Context: context.Background(),
|
||||
}
|
||||
listReq.SetTimeout(30 * time.Second)
|
||||
listResp, err := edge.ServiceEdgeRouterPolicy.ListServiceEdgeRouterPolicies(listReq, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(listResp.Payload.Data) == 1 {
|
||||
serpId := *(listResp.Payload.Data[0].ID)
|
||||
req := &service_edge_router_policy.DeleteServiceEdgeRouterPolicyParams{
|
||||
ID: serpId,
|
||||
Context: context.Background(),
|
||||
}
|
||||
req.SetTimeout(30 * time.Second)
|
||||
_, err := edge.ServiceEdgeRouterPolicy.DeleteServiceEdgeRouterPolicy(req, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logrus.Infof("deleted service edge router policy '%v' for environment '%v'", serpId, envZId)
|
||||
} else {
|
||||
logrus.Infof("did not find a service edge router policy")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func deleteServicePolicyBind(envZId, svcName string, edge *rest_management_api_client.ZitiEdgeManagement) error {
|
||||
return deleteServicePolicy(envZId, fmt.Sprintf("name=\"%v-backend\"", svcName), edge)
|
||||
}
|
||||
|
||||
func deleteServicePolicyDial(envZId, svcName string, edge *rest_management_api_client.ZitiEdgeManagement) error {
|
||||
return deleteServicePolicy(envZId, fmt.Sprintf("name=\"%v-dial\"", svcName), edge)
|
||||
}
|
||||
|
||||
func deleteServicePolicy(envZId, filter string, edge *rest_management_api_client.ZitiEdgeManagement) error {
|
||||
limit := int64(1)
|
||||
offset := int64(0)
|
||||
listReq := &service_policy.ListServicePoliciesParams{
|
||||
Filter: &filter,
|
||||
Limit: &limit,
|
||||
Offset: &offset,
|
||||
Context: context.Background(),
|
||||
}
|
||||
listReq.SetTimeout(30 * time.Second)
|
||||
listResp, err := edge.ServicePolicy.ListServicePolicies(listReq, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(listResp.Payload.Data) == 1 {
|
||||
spId := *(listResp.Payload.Data[0].ID)
|
||||
req := &service_policy.DeleteServicePolicyParams{
|
||||
ID: spId,
|
||||
Context: context.Background(),
|
||||
}
|
||||
req.SetTimeout(30 * time.Second)
|
||||
_, err := edge.ServicePolicy.DeleteServicePolicy(req, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logrus.Infof("deleted service policy '%v' for environment '%v'", spId, envZId)
|
||||
} else {
|
||||
logrus.Infof("did not find a service policy")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func deleteConfig(envZId, svcName string, edge *rest_management_api_client.ZitiEdgeManagement) error {
|
||||
filter := fmt.Sprintf("name=\"%v\"", svcName)
|
||||
limit := int64(0)
|
||||
offset := int64(0)
|
||||
listReq := &config.ListConfigsParams{
|
||||
Filter: &filter,
|
||||
Limit: &limit,
|
||||
Offset: &offset,
|
||||
Context: context.Background(),
|
||||
}
|
||||
listReq.SetTimeout(30 * time.Second)
|
||||
listResp, err := edge.Config.ListConfigs(listReq, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, cfg := range listResp.Payload.Data {
|
||||
deleteReq := &config.DeleteConfigParams{
|
||||
ID: *cfg.ID,
|
||||
Context: context.Background(),
|
||||
}
|
||||
deleteReq.SetTimeout(30 * time.Second)
|
||||
_, err := edge.Config.DeleteConfig(deleteReq, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logrus.Infof("deleted config '%v' for '%v'", *cfg.ID, envZId)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func deleteService(envZId, svcId string, edge *rest_management_api_client.ZitiEdgeManagement) error {
|
||||
req := &service.DeleteServiceParams{
|
||||
ID: svcId,
|
||||
Context: context.Background(),
|
||||
}
|
||||
req.SetTimeout(30 * time.Second)
|
||||
_, err := edge.Service.DeleteService(req, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logrus.Infof("deleted service '%v' for environment '%v'", svcId, envZId)
|
||||
return nil
|
||||
}
|
||||
|
||||
func deleteEdgeRouterPolicy(envZId, id string, edge *rest_management_api_client.ZitiEdgeManagement) error {
|
||||
filter := fmt.Sprintf("name=\"%v\"", id)
|
||||
limit := int64(0)
|
||||
offset := int64(0)
|
||||
listReq := &edge_router_policy.ListEdgeRouterPoliciesParams{
|
||||
Filter: &filter,
|
||||
Limit: &limit,
|
||||
Offset: &offset,
|
||||
Context: context.Background(),
|
||||
}
|
||||
listReq.SetTimeout(30 * time.Second)
|
||||
listResp, err := edge.EdgeRouterPolicy.ListEdgeRouterPolicies(listReq, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(listResp.Payload.Data) == 1 {
|
||||
erpId := *(listResp.Payload.Data[0].ID)
|
||||
req := &edge_router_policy.DeleteEdgeRouterPolicyParams{
|
||||
ID: erpId,
|
||||
Context: context.Background(),
|
||||
}
|
||||
_, err := edge.EdgeRouterPolicy.DeleteEdgeRouterPolicy(req, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logrus.Infof("deleted edge router policy '%v' for environment '%v'", erpId, envZId)
|
||||
} else {
|
||||
logrus.Infof("found '%d' edge router policies, expected 1", len(listResp.Payload.Data))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func deleteIdentity(id string, edge *rest_management_api_client.ZitiEdgeManagement) error {
|
||||
req := &identity_edge.DeleteIdentityParams{
|
||||
ID: id,
|
||||
Context: context.Background(),
|
||||
}
|
||||
req.SetTimeout(30 * time.Second)
|
||||
_, err := edge.Identity.DeleteIdentity(req, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logrus.Infof("deleted environment identity '%v'", id)
|
||||
return nil
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package email_ui
|
||||
package emailUi
|
||||
|
||||
import "embed"
|
||||
|
@ -2,77 +2,89 @@ package controller
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/openziti-test-kitchen/zrok/build"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/openziti-test-kitchen/zrok/controller/store"
|
||||
"github.com/openziti-test-kitchen/zrok/controller/zrokEdgeSdk"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_model_zrok"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_server_zrok/operations/identity"
|
||||
"github.com/openziti/edge/rest_management_api_client"
|
||||
"github.com/openziti/edge/rest_management_api_client/edge_router_policy"
|
||||
identity_edge "github.com/openziti/edge/rest_management_api_client/identity"
|
||||
rest_model_edge "github.com/openziti/edge/rest_model"
|
||||
sdk_config "github.com/openziti/sdk-golang/ziti/config"
|
||||
"github.com/openziti/sdk-golang/ziti/enroll"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_server_zrok/operations/environment"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"time"
|
||||
)
|
||||
|
||||
type enableHandler struct {
|
||||
cfg *LimitsConfig
|
||||
}
|
||||
|
||||
func newEnableHandler() *enableHandler {
|
||||
return &enableHandler{}
|
||||
func newEnableHandler(cfg *LimitsConfig) *enableHandler {
|
||||
return &enableHandler{cfg: cfg}
|
||||
}
|
||||
|
||||
func (self *enableHandler) Handle(params identity.EnableParams, principal *rest_model_zrok.Principal) middleware.Responder {
|
||||
func (h *enableHandler) Handle(params environment.EnableParams, principal *rest_model_zrok.Principal) middleware.Responder {
|
||||
// start transaction early; if it fails, don't bother creating ziti resources
|
||||
tx, err := str.Begin()
|
||||
if err != nil {
|
||||
logrus.Errorf("error starting transaction: %v", err)
|
||||
return identity.NewEnableInternalServerError()
|
||||
return environment.NewEnableInternalServerError()
|
||||
}
|
||||
defer func() { _ = tx.Rollback() }()
|
||||
|
||||
if err := h.checkLimits(principal, tx); err != nil {
|
||||
logrus.Errorf("limits error: %v", err)
|
||||
return environment.NewEnableUnauthorized()
|
||||
}
|
||||
|
||||
client, err := edgeClient()
|
||||
if err != nil {
|
||||
logrus.Errorf("error getting edge client: %v", err)
|
||||
return identity.NewEnableInternalServerError()
|
||||
return environment.NewEnableInternalServerError()
|
||||
}
|
||||
ident, err := self.createIdentity(principal.Email, client)
|
||||
|
||||
uniqueToken, err := createShareToken()
|
||||
if err != nil {
|
||||
logrus.Errorf("error creating unique identity token: %v", err)
|
||||
return environment.NewEnableInternalServerError()
|
||||
}
|
||||
|
||||
ident, err := zrokEdgeSdk.CreateEnvironmentIdentity(uniqueToken, principal.Email, params.Body.Description, client)
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
return identity.NewEnableInternalServerError()
|
||||
return environment.NewEnableInternalServerError()
|
||||
}
|
||||
cfg, err := self.enrollIdentity(ident.Payload.Data.ID, client)
|
||||
|
||||
envZId := ident.Payload.Data.ID
|
||||
cfg, err := zrokEdgeSdk.EnrollIdentity(envZId, client)
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
return identity.NewEnableInternalServerError()
|
||||
return environment.NewEnableInternalServerError()
|
||||
}
|
||||
if err := self.createEdgeRouterPolicy(ident.Payload.Data.ID, client); err != nil {
|
||||
|
||||
if err := zrokEdgeSdk.CreateEdgeRouterPolicy(envZId, envZId, client); err != nil {
|
||||
logrus.Error(err)
|
||||
return identity.NewEnableInternalServerError()
|
||||
return environment.NewEnableInternalServerError()
|
||||
}
|
||||
|
||||
envId, err := str.CreateEnvironment(int(principal.ID), &store.Environment{
|
||||
Description: params.Body.Description,
|
||||
Host: params.Body.Host,
|
||||
Address: realRemoteAddress(params.HTTPRequest),
|
||||
ZId: ident.Payload.Data.ID,
|
||||
ZId: envZId,
|
||||
}, tx)
|
||||
if err != nil {
|
||||
logrus.Errorf("error storing created identity: %v", err)
|
||||
_ = tx.Rollback()
|
||||
return identity.NewCreateAccountInternalServerError()
|
||||
return environment.NewEnableInternalServerError()
|
||||
}
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
logrus.Errorf("error committing: %v", err)
|
||||
return identity.NewCreateAccountInternalServerError()
|
||||
return environment.NewEnableInternalServerError()
|
||||
}
|
||||
logrus.Infof("created environment for '%v', with ziti identity '%v', and database id '%v'", principal.Email, ident.Payload.Data.ID, envId)
|
||||
|
||||
resp := identity.NewEnableCreated().WithPayload(&rest_model_zrok.EnableResponse{
|
||||
Identity: ident.Payload.Data.ID,
|
||||
resp := environment.NewEnableCreated().WithPayload(&rest_model_zrok.EnableResponse{
|
||||
Identity: envZId,
|
||||
})
|
||||
|
||||
var out bytes.Buffer
|
||||
@ -87,86 +99,15 @@ func (self *enableHandler) Handle(params identity.EnableParams, principal *rest_
|
||||
return resp
|
||||
}
|
||||
|
||||
func (self *enableHandler) createIdentity(email string, client *rest_management_api_client.ZitiEdgeManagement) (*identity_edge.CreateIdentityCreated, error) {
|
||||
iIsAdmin := false
|
||||
name, err := createToken()
|
||||
func (h *enableHandler) checkLimits(principal *rest_model_zrok.Principal, tx *sqlx.Tx) error {
|
||||
if !principal.Limitless && h.cfg.Environments > Unlimited {
|
||||
envs, err := str.FindEnvironmentsForAccount(int(principal.ID), tx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return errors.Errorf("unable to find environments for account '%v': %v", principal.Email, err)
|
||||
}
|
||||
identityType := rest_model_edge.IdentityTypeUser
|
||||
tags := self.zrokTags()
|
||||
tags.SubTags["zrokEmail"] = email
|
||||
i := &rest_model_edge.IdentityCreate{
|
||||
Enrollment: &rest_model_edge.IdentityCreateEnrollment{Ott: true},
|
||||
IsAdmin: &iIsAdmin,
|
||||
Name: &name,
|
||||
RoleAttributes: nil,
|
||||
ServiceHostingCosts: nil,
|
||||
Tags: tags,
|
||||
Type: &identityType,
|
||||
if len(envs)+1 > h.cfg.Environments {
|
||||
return errors.Errorf("would exceed environments limit of %d for '%v'", h.cfg.Environments, principal.Email)
|
||||
}
|
||||
req := identity_edge.NewCreateIdentityParams()
|
||||
req.Identity = i
|
||||
resp, err := client.Identity.CreateIdentity(req, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (_ *enableHandler) enrollIdentity(id string, client *rest_management_api_client.ZitiEdgeManagement) (*sdk_config.Config, error) {
|
||||
p := &identity_edge.DetailIdentityParams{
|
||||
Context: context.Background(),
|
||||
ID: id,
|
||||
}
|
||||
p.SetTimeout(30 * time.Second)
|
||||
resp, err := client.Identity.DetailIdentity(p, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tkn, _, err := enroll.ParseToken(resp.GetPayload().Data.Enrollment.Ott.JWT)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
flags := enroll.EnrollmentFlags{
|
||||
Token: tkn,
|
||||
KeyAlg: "RSA",
|
||||
}
|
||||
conf, err := enroll.Enroll(flags)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return conf, nil
|
||||
}
|
||||
|
||||
func (self *enableHandler) createEdgeRouterPolicy(id string, edge *rest_management_api_client.ZitiEdgeManagement) error {
|
||||
edgeRouterRoles := []string{"#all"}
|
||||
identityRoles := []string{fmt.Sprintf("@%v", id)}
|
||||
semantic := rest_model_edge.SemanticAllOf
|
||||
erp := &rest_model_edge.EdgeRouterPolicyCreate{
|
||||
EdgeRouterRoles: edgeRouterRoles,
|
||||
IdentityRoles: identityRoles,
|
||||
Name: &id,
|
||||
Semantic: &semantic,
|
||||
Tags: self.zrokTags(),
|
||||
}
|
||||
req := &edge_router_policy.CreateEdgeRouterPolicyParams{
|
||||
Policy: erp,
|
||||
Context: context.Background(),
|
||||
}
|
||||
req.SetTimeout(30 * time.Second)
|
||||
resp, err := edge.EdgeRouterPolicy.CreateEdgeRouterPolicy(req, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logrus.Infof("created edge router policy '%v' for ziti identity '%v'", resp.Payload.Data.ID, id)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *enableHandler) zrokTags() *rest_model_edge.Tags {
|
||||
return &rest_model_edge.Tags{
|
||||
SubTags: map[string]interface{}{
|
||||
"zrok": build.String(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
78
controller/environmentDetail.go
Normal file
78
controller/environmentDetail.go
Normal file
@ -0,0 +1,78 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_model_zrok"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_server_zrok/operations/metadata"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type environmentDetailHandler struct{}
|
||||
|
||||
func newEnvironmentDetailHandler() *environmentDetailHandler {
|
||||
return &environmentDetailHandler{}
|
||||
}
|
||||
|
||||
func (h *environmentDetailHandler) Handle(params metadata.GetEnvironmentDetailParams, principal *rest_model_zrok.Principal) middleware.Responder {
|
||||
tx, err := str.Begin()
|
||||
if err != nil {
|
||||
logrus.Errorf("error starting transaction: %v", err)
|
||||
return metadata.NewGetEnvironmentDetailInternalServerError()
|
||||
}
|
||||
defer func() { _ = tx.Rollback() }()
|
||||
senv, err := str.FindEnvironmentForAccount(params.EnvZID, int(principal.ID), tx)
|
||||
if err != nil {
|
||||
logrus.Errorf("environment '%v' not found for account '%v': %v", params.EnvZID, principal.Email, err)
|
||||
return metadata.NewGetEnvironmentDetailNotFound()
|
||||
}
|
||||
es := &rest_model_zrok.EnvironmentShares{
|
||||
Environment: &rest_model_zrok.Environment{
|
||||
Address: senv.Address,
|
||||
CreatedAt: senv.CreatedAt.UnixMilli(),
|
||||
Description: senv.Description,
|
||||
Host: senv.Host,
|
||||
UpdatedAt: senv.UpdatedAt.UnixMilli(),
|
||||
ZID: senv.ZId,
|
||||
},
|
||||
}
|
||||
shrs, err := str.FindSharesForEnvironment(senv.Id, tx)
|
||||
if err != nil {
|
||||
logrus.Errorf("error finding shares for environment '%v': %v", senv.ZId, err)
|
||||
return metadata.NewGetEnvironmentDetailInternalServerError()
|
||||
}
|
||||
var sparkData map[string][]int64
|
||||
if cfg.Influx != nil {
|
||||
sparkData, err = sparkDataForShares(shrs)
|
||||
if err != nil {
|
||||
logrus.Errorf("error querying spark data for shares: %v", err)
|
||||
}
|
||||
}
|
||||
for _, shr := range shrs {
|
||||
feEndpoint := ""
|
||||
if shr.FrontendEndpoint != nil {
|
||||
feEndpoint = *shr.FrontendEndpoint
|
||||
}
|
||||
feSelection := ""
|
||||
if shr.FrontendSelection != nil {
|
||||
feSelection = *shr.FrontendSelection
|
||||
}
|
||||
beProxyEndpoint := ""
|
||||
if shr.BackendProxyEndpoint != nil {
|
||||
beProxyEndpoint = *shr.BackendProxyEndpoint
|
||||
}
|
||||
es.Shares = append(es.Shares, &rest_model_zrok.Share{
|
||||
Token: shr.Token,
|
||||
ZID: shr.ZId,
|
||||
ShareMode: shr.ShareMode,
|
||||
BackendMode: shr.BackendMode,
|
||||
FrontendSelection: feSelection,
|
||||
FrontendEndpoint: feEndpoint,
|
||||
BackendProxyEndpoint: beProxyEndpoint,
|
||||
Reserved: shr.Reserved,
|
||||
Metrics: sparkData[shr.Token],
|
||||
CreatedAt: shr.CreatedAt.UnixMilli(),
|
||||
UpdatedAt: shr.UpdatedAt.UnixMilli(),
|
||||
})
|
||||
}
|
||||
return metadata.NewGetEnvironmentDetailOK().WithPayload(es)
|
||||
}
|
@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/openziti-test-kitchen/zrok/controller/store"
|
||||
"github.com/openziti-test-kitchen/zrok/controller/zrokEdgeSdk"
|
||||
"github.com/openziti/edge/rest_management_api_client"
|
||||
"github.com/openziti/edge/rest_management_api_client/config"
|
||||
"github.com/openziti/edge/rest_management_api_client/service"
|
||||
@ -36,13 +37,13 @@ func GC(inCfg *Config) error {
|
||||
return err
|
||||
}
|
||||
defer func() { _ = tx.Rollback() }()
|
||||
dbSvcs, err := str.GetAllServices(tx)
|
||||
sshrs, err := str.GetAllShares(tx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
liveMap := make(map[string]struct{})
|
||||
for _, dbSvc := range dbSvcs {
|
||||
liveMap[dbSvc.Name] = struct{}{}
|
||||
for _, sshr := range sshrs {
|
||||
liveMap[sshr.Token] = struct{}{}
|
||||
}
|
||||
if err := gcServices(edge, liveMap); err != nil {
|
||||
return errors.Wrap(err, "error garbage collecting services")
|
||||
@ -71,19 +72,19 @@ func gcServices(edge *rest_management_api_client.ZitiEdgeManagement, liveMap map
|
||||
for _, svc := range listResp.Payload.Data {
|
||||
if _, found := liveMap[*svc.Name]; !found {
|
||||
logrus.Infof("garbage collecting, zitiSvcId='%v', zrokSvcId='%v'", *svc.ID, *svc.Name)
|
||||
if err := deleteServiceEdgeRouterPolicy("gc", *svc.Name, edge); err != nil {
|
||||
if err := zrokEdgeSdk.DeleteServiceEdgeRouterPolicy("gc", *svc.Name, edge); err != nil {
|
||||
logrus.Errorf("error garbage collecting service edge router policy: %v", err)
|
||||
}
|
||||
if err := deleteServicePolicyDial("gc", *svc.Name, edge); err != nil {
|
||||
if err := zrokEdgeSdk.DeleteServicePolicyDial("gc", *svc.Name, edge); err != nil {
|
||||
logrus.Errorf("error garbage collecting service dial policy: %v", err)
|
||||
}
|
||||
if err := deleteServicePolicyBind("gc", *svc.Name, edge); err != nil {
|
||||
if err := zrokEdgeSdk.DeleteServicePolicyBind("gc", *svc.Name, edge); err != nil {
|
||||
logrus.Errorf("error garbage collecting service bind policy: %v", err)
|
||||
}
|
||||
if err := deleteConfig("gc", *svc.Name, edge); err != nil {
|
||||
if err := zrokEdgeSdk.DeleteConfig("gc", *svc.Name, edge); err != nil {
|
||||
logrus.Errorf("error garbage collecting config: %v", err)
|
||||
}
|
||||
if err := deleteService("gc", *svc.ID, edge); err != nil {
|
||||
if err := zrokEdgeSdk.DeleteService("gc", *svc.ID, edge); err != nil {
|
||||
logrus.Errorf("error garbage collecting service: %v", err)
|
||||
}
|
||||
} else {
|
||||
@ -108,7 +109,7 @@ func gcServiceEdgeRouterPolicies(edge *rest_management_api_client.ZitiEdgeManage
|
||||
for _, serp := range listResp.Payload.Data {
|
||||
if _, found := liveMap[*serp.Name]; !found {
|
||||
logrus.Infof("garbage collecting, svcId='%v'", *serp.Name)
|
||||
if err := deleteServiceEdgeRouterPolicy("gc", *serp.Name, edge); err != nil {
|
||||
if err := zrokEdgeSdk.DeleteServiceEdgeRouterPolicy("gc", *serp.Name, edge); err != nil {
|
||||
logrus.Errorf("error garbage collecting service edge router policy: %v", err)
|
||||
}
|
||||
} else {
|
||||
@ -135,7 +136,7 @@ func gcServicePolicies(edge *rest_management_api_client.ZitiEdgeManagement, live
|
||||
if _, found := liveMap[spName]; !found {
|
||||
logrus.Infof("garbage collecting, svcId='%v'", spName)
|
||||
deleteFilter := fmt.Sprintf("id=\"%v\"", *sp.ID)
|
||||
if err := deleteServicePolicy("gc", deleteFilter, edge); err != nil {
|
||||
if err := zrokEdgeSdk.DeleteServicePolicy("gc", deleteFilter, edge); err != nil {
|
||||
logrus.Errorf("error garbage collecting service policy: %v", err)
|
||||
}
|
||||
} else {
|
||||
@ -159,7 +160,7 @@ func gcConfigs(edge *rest_management_api_client.ZitiEdgeManagement, liveMap map[
|
||||
if listResp, err := edge.Config.ListConfigs(listReq, nil); err == nil {
|
||||
for _, c := range listResp.Payload.Data {
|
||||
if _, found := liveMap[*c.Name]; !found {
|
||||
if err := deleteConfig("gc", *c.Name, edge); err != nil {
|
||||
if err := zrokEdgeSdk.DeleteConfig("gc", *c.Name, edge); err != nil {
|
||||
logrus.Errorf("error garbage collecting config: %v", err)
|
||||
}
|
||||
} else {
|
||||
|
102
controller/invite.go
Normal file
102
controller/invite.go
Normal file
@ -0,0 +1,102 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/openziti-test-kitchen/zrok/controller/store"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_server_zrok/operations/account"
|
||||
"github.com/openziti-test-kitchen/zrok/util"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type inviteHandler struct {
|
||||
cfg *Config
|
||||
}
|
||||
|
||||
func newInviteHandler(cfg *Config) *inviteHandler {
|
||||
return &inviteHandler{
|
||||
cfg: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
func (self *inviteHandler) Handle(params account.InviteParams) middleware.Responder {
|
||||
if params.Body == nil || params.Body.Email == "" {
|
||||
logrus.Errorf("missing email")
|
||||
return account.NewInviteBadRequest()
|
||||
}
|
||||
if !util.IsValidEmail(params.Body.Email) {
|
||||
logrus.Errorf("'%v' is not a valid email address", params.Body.Email)
|
||||
return account.NewInviteBadRequest()
|
||||
}
|
||||
logrus.Infof("received account request for email '%v'", params.Body.Email)
|
||||
var token string
|
||||
|
||||
tx, err := str.Begin()
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
return account.NewInviteInternalServerError()
|
||||
}
|
||||
defer func() { _ = tx.Rollback() }()
|
||||
|
||||
if self.cfg.Registration != nil && self.cfg.Registration.TokenStrategy == "store" {
|
||||
inviteToken, err := str.GetInviteTokenByToken(params.Body.Token, tx)
|
||||
if err != nil {
|
||||
logrus.Errorf("cannot get invite token '%v' for '%v': %v", params.Body.Token, params.Body.Email, err)
|
||||
return account.NewInviteBadRequest()
|
||||
}
|
||||
if err := str.DeleteInviteToken(inviteToken.Id, tx); err != nil {
|
||||
logrus.Error(err)
|
||||
return account.NewInviteInternalServerError()
|
||||
}
|
||||
logrus.Infof("using invite token '%v' to process invite request for '%v'", inviteToken.Token, params.Body.Email)
|
||||
}
|
||||
|
||||
token, err = createToken()
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
return account.NewInviteInternalServerError()
|
||||
}
|
||||
ar := &store.AccountRequest{
|
||||
Token: token,
|
||||
Email: params.Body.Email,
|
||||
SourceAddress: params.HTTPRequest.RemoteAddr,
|
||||
}
|
||||
|
||||
if _, err := str.FindAccountWithEmail(params.Body.Email, tx); err == nil {
|
||||
logrus.Errorf("found account for '%v', cannot process account request", params.Body.Email)
|
||||
return account.NewInviteBadRequest()
|
||||
} else {
|
||||
logrus.Infof("no account found for '%v': %v", params.Body.Email, err)
|
||||
}
|
||||
|
||||
if oldAr, err := str.FindAccountRequestWithEmail(params.Body.Email, tx); err == nil {
|
||||
logrus.Warnf("found previous account request for '%v', removing", params.Body.Email)
|
||||
if err := str.DeleteAccountRequest(oldAr.Id, tx); err != nil {
|
||||
logrus.Errorf("error deleteing previous account request for '%v': %v", params.Body.Email, err)
|
||||
return account.NewInviteInternalServerError()
|
||||
}
|
||||
} else {
|
||||
logrus.Warnf("error finding previous account request for '%v': %v", params.Body.Email, err)
|
||||
}
|
||||
|
||||
if _, err := str.CreateAccountRequest(ar, tx); err != nil {
|
||||
logrus.Errorf("error creating account request for '%v': %v", params.Body.Email, err)
|
||||
return account.NewInviteInternalServerError()
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
logrus.Errorf("error committing account request for '%v': %v", params.Body.Email, err)
|
||||
return account.NewInviteInternalServerError()
|
||||
}
|
||||
|
||||
if cfg.Email != nil && cfg.Registration != nil {
|
||||
if err := sendVerificationEmail(params.Body.Email, token); err != nil {
|
||||
logrus.Errorf("error sending verification email for '%v': %v", params.Body.Email, err)
|
||||
return account.NewInviteInternalServerError()
|
||||
}
|
||||
} else {
|
||||
logrus.Errorf("'email' and 'registration' configuration missing; skipping registration email")
|
||||
}
|
||||
|
||||
logrus.Infof("account request for '%v' has registration token '%v'", params.Body.Email, ar.Token)
|
||||
|
||||
return account.NewInviteCreated()
|
||||
}
|
55
controller/inviteTokenGenerate.go
Normal file
55
controller/inviteTokenGenerate.go
Normal file
@ -0,0 +1,55 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/openziti-test-kitchen/zrok/controller/store"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_model_zrok"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_server_zrok/operations/account"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_server_zrok/operations/admin"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type inviteTokenGenerateHandler struct {
|
||||
}
|
||||
|
||||
func newInviteTokenGenerateHandler() *inviteTokenGenerateHandler {
|
||||
return &inviteTokenGenerateHandler{}
|
||||
}
|
||||
|
||||
func (handler *inviteTokenGenerateHandler) Handle(params admin.InviteTokenGenerateParams, principal *rest_model_zrok.Principal) middleware.Responder {
|
||||
if !principal.Admin {
|
||||
logrus.Errorf("invalid admin principal")
|
||||
return admin.NewInviteTokenGenerateUnauthorized()
|
||||
}
|
||||
|
||||
if params.Body == nil || len(params.Body.Tokens) == 0 {
|
||||
logrus.Error("missing tokens")
|
||||
return admin.NewInviteTokenGenerateBadRequest()
|
||||
}
|
||||
logrus.Infof("received invite generate request with %d tokens", len(params.Body.Tokens))
|
||||
|
||||
invites := make([]*store.InviteToken, len(params.Body.Tokens))
|
||||
for i, token := range params.Body.Tokens {
|
||||
invites[i] = &store.InviteToken{
|
||||
Token: token,
|
||||
}
|
||||
}
|
||||
tx, err := str.Begin()
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
return admin.NewInviteTokenGenerateInternalServerError()
|
||||
}
|
||||
defer func() { _ = tx.Rollback() }()
|
||||
|
||||
if err := str.CreateInviteTokens(invites, tx); err != nil {
|
||||
logrus.Error(err)
|
||||
return admin.NewInviteTokenGenerateInternalServerError()
|
||||
}
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
logrus.Errorf("error committing inviteGenerate request: %v", err)
|
||||
return account.NewInviteInternalServerError()
|
||||
}
|
||||
|
||||
return admin.NewInviteTokenGenerateCreated()
|
||||
}
|
52
controller/listFrontends.go
Normal file
52
controller/listFrontends.go
Normal file
@ -0,0 +1,52 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_model_zrok"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_server_zrok/operations/admin"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type listFrontendsHandler struct{}
|
||||
|
||||
func newListFrontendsHandler() *listFrontendsHandler {
|
||||
return &listFrontendsHandler{}
|
||||
}
|
||||
|
||||
func (h *listFrontendsHandler) Handle(params admin.ListFrontendsParams, principal *rest_model_zrok.Principal) middleware.Responder {
|
||||
if !principal.Admin {
|
||||
logrus.Errorf("invalid admin principal")
|
||||
return admin.NewListFrontendsUnauthorized()
|
||||
}
|
||||
|
||||
tx, err := str.Begin()
|
||||
if err != nil {
|
||||
logrus.Errorf("error starting transaction: %v", err)
|
||||
return admin.NewListFrontendsInternalServerError()
|
||||
}
|
||||
defer func() { _ = tx.Rollback() }()
|
||||
|
||||
sfes, err := str.FindPublicFrontends(tx)
|
||||
if err != nil {
|
||||
logrus.Errorf("error finding public frontends: %v", err)
|
||||
return admin.NewListFrontendsInternalServerError()
|
||||
}
|
||||
|
||||
var frontends rest_model_zrok.PublicFrontendList
|
||||
for _, sfe := range sfes {
|
||||
frontend := &rest_model_zrok.PublicFrontend{
|
||||
Token: sfe.Token,
|
||||
ZID: sfe.ZId,
|
||||
CreatedAt: sfe.CreatedAt.UnixMilli(),
|
||||
UpdatedAt: sfe.UpdatedAt.UnixMilli(),
|
||||
}
|
||||
if sfe.UrlTemplate != nil {
|
||||
frontend.URLTemplate = *sfe.UrlTemplate
|
||||
}
|
||||
if sfe.PublicName != nil {
|
||||
frontend.PublicName = *sfe.PublicName
|
||||
}
|
||||
frontends = append(frontends, frontend)
|
||||
}
|
||||
return admin.NewListFrontendsOK().WithPayload(frontends)
|
||||
}
|
@ -3,14 +3,14 @@ package controller
|
||||
import (
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_model_zrok"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_server_zrok/operations/identity"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_server_zrok/operations/account"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func loginHandler(params identity.LoginParams) middleware.Responder {
|
||||
func loginHandler(params account.LoginParams) middleware.Responder {
|
||||
if params.Body == nil || params.Body.Email == "" || params.Body.Password == "" {
|
||||
logrus.Errorf("missing email or password")
|
||||
return identity.NewLoginUnauthorized()
|
||||
return account.NewLoginUnauthorized()
|
||||
}
|
||||
|
||||
logrus.Infof("received login request for email '%v'", params.Body.Email)
|
||||
@ -18,18 +18,18 @@ func loginHandler(params identity.LoginParams) middleware.Responder {
|
||||
tx, err := str.Begin()
|
||||
if err != nil {
|
||||
logrus.Errorf("error starting transaction: %v", err)
|
||||
return identity.NewLoginUnauthorized()
|
||||
return account.NewLoginUnauthorized()
|
||||
}
|
||||
defer func() { _ = tx.Rollback() }()
|
||||
a, err := str.FindAccountWithEmail(params.Body.Email, tx)
|
||||
if err != nil {
|
||||
logrus.Errorf("error finding account '%v': %v", params.Body.Email, err)
|
||||
return identity.NewLoginUnauthorized()
|
||||
return account.NewLoginUnauthorized()
|
||||
}
|
||||
if a.Password != hashPassword(params.Body.Password) {
|
||||
logrus.Errorf("password mismatch for account '%v'", params.Body.Email)
|
||||
return identity.NewLoginUnauthorized()
|
||||
return account.NewLoginUnauthorized()
|
||||
}
|
||||
|
||||
return identity.NewLoginOK().WithPayload(rest_model_zrok.LoginResponse(a.Token))
|
||||
return account.NewLoginOK().WithPayload(rest_model_zrok.LoginResponse(a.Token))
|
||||
}
|
||||
|
78
controller/maintenance.go
Normal file
78
controller/maintenance.go
Normal file
@ -0,0 +1,78 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type maintenanceAgent struct {
|
||||
*MaintenanceConfig
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
func newMaintenanceAgent(ctx context.Context, cfg *MaintenanceConfig) *maintenanceAgent {
|
||||
return &maintenanceAgent{
|
||||
MaintenanceConfig: cfg,
|
||||
ctx: ctx,
|
||||
}
|
||||
}
|
||||
|
||||
func (ma *maintenanceAgent) run() {
|
||||
logrus.Info("starting")
|
||||
defer logrus.Info("stopping")
|
||||
|
||||
ticker := time.NewTicker(ma.Registration.CheckFrequency)
|
||||
for {
|
||||
select {
|
||||
case <-ma.ctx.Done():
|
||||
{
|
||||
ticker.Stop()
|
||||
return
|
||||
}
|
||||
case <-ticker.C:
|
||||
{
|
||||
if err := ma.deleteExpiredAccountRequests(); err != nil {
|
||||
logrus.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (ma *maintenanceAgent) deleteExpiredAccountRequests() error {
|
||||
tx, err := str.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() { _ = tx.Rollback() }()
|
||||
|
||||
timeout := time.Now().UTC().Add(-ma.Registration.ExpirationTimeout)
|
||||
accountRequests, err := str.FindExpiredAccountRequests(timeout, ma.Registration.BatchLimit, tx)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error finding expire account requests before %v", timeout)
|
||||
}
|
||||
if len(accountRequests) > 0 {
|
||||
logrus.Infof("found %d expired account requests to remove", len(accountRequests))
|
||||
acctStrings := make([]string, len(accountRequests))
|
||||
ids := make([]int, len(accountRequests))
|
||||
for i, acct := range accountRequests {
|
||||
ids[i] = acct.Id
|
||||
acctStrings[i] = fmt.Sprintf("{%d:%s}", acct.Id, acct.Email)
|
||||
}
|
||||
|
||||
logrus.Infof("deleting expired account requests: %v", strings.Join(acctStrings, ","))
|
||||
if err := str.DeleteMultipleAccountRequests(ids, tx); err != nil {
|
||||
return errors.Wrapf(err, "error deleting expired account requests before %v", timeout)
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
return errors.Wrapf(err, "error committing expired acount requests deletion")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -119,7 +119,7 @@ func (ma *metricsAgent) processMetrics(m *model.Metrics) error {
|
||||
for k, v := range m.Sessions {
|
||||
if ma.writeApi != nil {
|
||||
pt := influxdb2.NewPoint("xfer",
|
||||
map[string]string{"namespace": m.Namespace, "service": k},
|
||||
map[string]string{"namespace": m.Namespace, "share": k},
|
||||
map[string]interface{}{"bytesRead": v.BytesRead, "bytesWritten": v.BytesWritten},
|
||||
time.UnixMilli(v.LastUpdate))
|
||||
pts = append(pts, pt)
|
||||
|
@ -1,10 +1,7 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/openziti-test-kitchen/zrok/controller/store"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_model_zrok"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_server_zrok/operations/metadata"
|
||||
"github.com/sirupsen/logrus"
|
||||
@ -22,14 +19,14 @@ func overviewHandler(_ metadata.OverviewParams, principal *rest_model_zrok.Princ
|
||||
logrus.Errorf("error finding environments for '%v': %v", principal.Email, err)
|
||||
return metadata.NewOverviewInternalServerError()
|
||||
}
|
||||
var out rest_model_zrok.EnvironmentServicesList
|
||||
var out rest_model_zrok.EnvironmentSharesList
|
||||
for _, env := range envs {
|
||||
svcs, err := str.FindServicesForEnvironment(env.Id, tx)
|
||||
shrs, err := str.FindSharesForEnvironment(env.Id, tx)
|
||||
if err != nil {
|
||||
logrus.Errorf("error finding services for environment '%v': %v", env.ZId, err)
|
||||
logrus.Errorf("error finding shares for environment '%v': %v", env.ZId, err)
|
||||
return metadata.NewOverviewInternalServerError()
|
||||
}
|
||||
es := &rest_model_zrok.EnvironmentServices{
|
||||
es := &rest_model_zrok.EnvironmentShares{
|
||||
Environment: &rest_model_zrok.Environment{
|
||||
Address: env.Address,
|
||||
CreatedAt: env.CreatedAt.UnixMilli(),
|
||||
@ -39,74 +36,34 @@ func overviewHandler(_ metadata.OverviewParams, principal *rest_model_zrok.Princ
|
||||
ZID: env.ZId,
|
||||
},
|
||||
}
|
||||
sparkData, err := sparkDataForServices(svcs)
|
||||
if err != nil {
|
||||
logrus.Errorf("error querying spark data for services: %v", err)
|
||||
return metadata.NewOverviewInternalServerError()
|
||||
|
||||
for _, shr := range shrs {
|
||||
feEndpoint := ""
|
||||
if shr.FrontendEndpoint != nil {
|
||||
feEndpoint = *shr.FrontendEndpoint
|
||||
}
|
||||
for _, svc := range svcs {
|
||||
es.Services = append(es.Services, &rest_model_zrok.Service{
|
||||
CreatedAt: svc.CreatedAt.UnixMilli(),
|
||||
Frontend: svc.Frontend,
|
||||
Backend: svc.Backend,
|
||||
UpdatedAt: svc.UpdatedAt.UnixMilli(),
|
||||
ZID: svc.ZId,
|
||||
Name: svc.Name,
|
||||
Metrics: sparkData[svc.Name],
|
||||
feSelection := ""
|
||||
if shr.FrontendSelection != nil {
|
||||
feSelection = *shr.FrontendSelection
|
||||
}
|
||||
beProxyEndpoint := ""
|
||||
if shr.BackendProxyEndpoint != nil {
|
||||
beProxyEndpoint = *shr.BackendProxyEndpoint
|
||||
}
|
||||
es.Shares = append(es.Shares, &rest_model_zrok.Share{
|
||||
Token: shr.Token,
|
||||
ZID: shr.ZId,
|
||||
ShareMode: shr.ShareMode,
|
||||
BackendMode: shr.BackendMode,
|
||||
FrontendSelection: feSelection,
|
||||
FrontendEndpoint: feEndpoint,
|
||||
BackendProxyEndpoint: beProxyEndpoint,
|
||||
Reserved: shr.Reserved,
|
||||
CreatedAt: shr.CreatedAt.UnixMilli(),
|
||||
UpdatedAt: shr.UpdatedAt.UnixMilli(),
|
||||
})
|
||||
}
|
||||
out = append(out, es)
|
||||
}
|
||||
return metadata.NewOverviewOK().WithPayload(out)
|
||||
}
|
||||
|
||||
func sparkDataForServices(svcs []*store.Service) (map[string][]int64, error) {
|
||||
out := make(map[string][]int64)
|
||||
|
||||
if len(svcs) > 0 {
|
||||
qapi := idb.QueryAPI(cfg.Influx.Org)
|
||||
|
||||
result, err := qapi.Query(context.Background(), sparkFluxQuery(svcs))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for result.Next() {
|
||||
combinedRate := int64(0)
|
||||
readRate := result.Record().ValueByKey("bytesRead")
|
||||
if readRate != nil {
|
||||
combinedRate += readRate.(int64)
|
||||
}
|
||||
writeRate := result.Record().ValueByKey("bytesWritten")
|
||||
if writeRate != nil {
|
||||
combinedRate += writeRate.(int64)
|
||||
}
|
||||
svcName := result.Record().ValueByKey("service").(string)
|
||||
svcMetrics := out[svcName]
|
||||
svcMetrics = append(svcMetrics, combinedRate)
|
||||
out[svcName] = svcMetrics
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func sparkFluxQuery(svcs []*store.Service) string {
|
||||
svcFilter := "|> filter(fn: (r) =>"
|
||||
for i, svc := range svcs {
|
||||
if i > 0 {
|
||||
svcFilter += " or"
|
||||
}
|
||||
svcFilter += fmt.Sprintf(" r[\"service\"] == \"%v\"", svc.Name)
|
||||
}
|
||||
svcFilter += ")"
|
||||
query := "read = from(bucket: \"zrok\")" +
|
||||
"|> range(start: -5m)" +
|
||||
"|> filter(fn: (r) => r[\"_measurement\"] == \"xfer\")" +
|
||||
"|> filter(fn: (r) => r[\"_field\"] == \"bytesRead\" or r[\"_field\"] == \"bytesWritten\")" +
|
||||
"|> filter(fn: (r) => r[\"namespace\"] == \"frontend\")" +
|
||||
svcFilter +
|
||||
"|> aggregateWindow(every: 5s, fn: sum, createEmpty: true)\n" +
|
||||
"|> pivot(rowKey:[\"_time\"], columnKey: [\"_field\"], valueColumn: \"_value\")" +
|
||||
"|> yield(name: \"last\")"
|
||||
return query
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ import (
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/openziti-test-kitchen/zrok/controller/store"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_model_zrok"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_server_zrok/operations/identity"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_server_zrok/operations/account"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
@ -13,30 +13,30 @@ type registerHandler struct{}
|
||||
func newRegisterHandler() *registerHandler {
|
||||
return ®isterHandler{}
|
||||
}
|
||||
func (self *registerHandler) Handle(params identity.RegisterParams) middleware.Responder {
|
||||
func (self *registerHandler) Handle(params account.RegisterParams) middleware.Responder {
|
||||
if params.Body == nil || params.Body.Token == "" || params.Body.Password == "" {
|
||||
logrus.Error("missing token or password")
|
||||
return identity.NewRegisterNotFound()
|
||||
return account.NewRegisterNotFound()
|
||||
}
|
||||
logrus.Infof("received register request for token '%v'", params.Body.Token)
|
||||
|
||||
tx, err := str.Begin()
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
return identity.NewRegisterInternalServerError()
|
||||
return account.NewRegisterInternalServerError()
|
||||
}
|
||||
defer func() { _ = tx.Rollback() }()
|
||||
|
||||
ar, err := str.FindAccountRequestWithToken(params.Body.Token, tx)
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
return identity.NewRegisterNotFound()
|
||||
return account.NewRegisterNotFound()
|
||||
}
|
||||
|
||||
token, err := createToken()
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
return identity.NewRegisterInternalServerError()
|
||||
return account.NewRegisterInternalServerError()
|
||||
}
|
||||
a := &store.Account{
|
||||
Email: ar.Email,
|
||||
@ -45,20 +45,20 @@ func (self *registerHandler) Handle(params identity.RegisterParams) middleware.R
|
||||
}
|
||||
if _, err := str.CreateAccount(a, tx); err != nil {
|
||||
logrus.Error(err)
|
||||
return identity.NewRegisterInternalServerError()
|
||||
return account.NewRegisterInternalServerError()
|
||||
}
|
||||
|
||||
if err := str.DeleteAccountRequest(ar.Id, tx); err != nil {
|
||||
logrus.Error(err)
|
||||
return identity.NewRegisterInternalServerError()
|
||||
return account.NewRegisterInternalServerError()
|
||||
}
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
logrus.Error(err)
|
||||
return identity.NewCreateAccountInternalServerError()
|
||||
return account.NewRegisterInternalServerError()
|
||||
}
|
||||
|
||||
logrus.Infof("created account '%v' with token '%v'", a.Email, a.Token)
|
||||
|
||||
return identity.NewRegisterOK().WithPayload(&rest_model_zrok.RegisterResponse{Token: a.Token})
|
||||
return account.NewRegisterOK().WithPayload(&rest_model_zrok.RegisterResponse{Token: a.Token})
|
||||
}
|
||||
|
160
controller/share.go
Normal file
160
controller/share.go
Normal file
@ -0,0 +1,160 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/openziti-test-kitchen/zrok/controller/store"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_model_zrok"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_server_zrok/operations/share"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type shareHandler struct {
|
||||
cfg *LimitsConfig
|
||||
}
|
||||
|
||||
func newShareHandler(cfg *LimitsConfig) *shareHandler {
|
||||
return &shareHandler{cfg: cfg}
|
||||
}
|
||||
|
||||
func (h *shareHandler) Handle(params share.ShareParams, principal *rest_model_zrok.Principal) middleware.Responder {
|
||||
logrus.Infof("handling")
|
||||
|
||||
tx, err := str.Begin()
|
||||
if err != nil {
|
||||
logrus.Errorf("error starting transaction: %v", err)
|
||||
return share.NewShareInternalServerError()
|
||||
}
|
||||
defer func() { _ = tx.Rollback() }()
|
||||
|
||||
envZId := params.Body.EnvZID
|
||||
envId := 0
|
||||
envs, err := str.FindEnvironmentsForAccount(int(principal.ID), tx)
|
||||
if err == nil {
|
||||
found := false
|
||||
for _, env := range envs {
|
||||
if env.ZId == envZId {
|
||||
logrus.Debugf("found identity '%v' for user '%v'", envZId, principal.Email)
|
||||
envId = env.Id
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
logrus.Errorf("environment '%v' not found for user '%v'", envZId, principal.Email)
|
||||
return share.NewShareUnauthorized()
|
||||
}
|
||||
} else {
|
||||
logrus.Errorf("error finding environments for account '%v'", principal.Email)
|
||||
return share.NewShareInternalServerError()
|
||||
}
|
||||
|
||||
if err := h.checkLimits(principal, envs, tx); err != nil {
|
||||
logrus.Errorf("limits error: %v", err)
|
||||
return share.NewShareUnauthorized()
|
||||
}
|
||||
|
||||
edge, err := edgeClient()
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
return share.NewShareInternalServerError()
|
||||
}
|
||||
shrToken, err := createShareToken()
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
return share.NewShareInternalServerError()
|
||||
}
|
||||
|
||||
var shrZId string
|
||||
var frontendEndpoints []string
|
||||
switch params.Body.ShareMode {
|
||||
case "public":
|
||||
if len(params.Body.FrontendSelection) < 1 {
|
||||
logrus.Info("no frontend selection provided")
|
||||
return share.NewShareNotFound()
|
||||
}
|
||||
|
||||
var frontendZIds []string
|
||||
var frontendTemplates []string
|
||||
for _, frontendSelection := range params.Body.FrontendSelection {
|
||||
sfe, err := str.FindFrontendPubliclyNamed(frontendSelection, tx)
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
return share.NewShareNotFound()
|
||||
}
|
||||
if sfe != nil && sfe.UrlTemplate != nil {
|
||||
frontendZIds = append(frontendZIds, sfe.ZId)
|
||||
frontendTemplates = append(frontendTemplates, *sfe.UrlTemplate)
|
||||
logrus.Infof("added frontend selection '%v' with ziti identity '%v' for share '%v'", frontendSelection, sfe.ZId, shrToken)
|
||||
}
|
||||
}
|
||||
shrZId, frontendEndpoints, err = newPublicResourceAllocator().allocate(envZId, shrToken, frontendZIds, frontendTemplates, params, edge)
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
return share.NewShareInternalServerError()
|
||||
}
|
||||
|
||||
case "private":
|
||||
shrZId, frontendEndpoints, err = newPrivateResourceAllocator().allocate(envZId, shrToken, params, edge)
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
return share.NewShareInternalServerError()
|
||||
}
|
||||
|
||||
default:
|
||||
logrus.Errorf("unknown share mode '%v", params.Body.ShareMode)
|
||||
return share.NewShareInternalServerError()
|
||||
}
|
||||
|
||||
logrus.Debugf("allocated share '%v'", shrToken)
|
||||
|
||||
reserved := params.Body.Reserved
|
||||
sshr := &store.Share{
|
||||
ZId: shrZId,
|
||||
Token: shrToken,
|
||||
ShareMode: params.Body.ShareMode,
|
||||
BackendMode: params.Body.BackendMode,
|
||||
BackendProxyEndpoint: ¶ms.Body.BackendProxyEndpoint,
|
||||
Reserved: reserved,
|
||||
}
|
||||
if len(frontendEndpoints) > 0 {
|
||||
sshr.FrontendEndpoint = &frontendEndpoints[0]
|
||||
} else if sshr.ShareMode == "private" {
|
||||
sshr.FrontendEndpoint = &sshr.ShareMode
|
||||
}
|
||||
|
||||
sid, err := str.CreateShare(envId, sshr, tx)
|
||||
if err != nil {
|
||||
logrus.Errorf("error creating share record: %v", err)
|
||||
return share.NewShareInternalServerError()
|
||||
}
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
logrus.Errorf("error committing share record: %v", err)
|
||||
return share.NewShareInternalServerError()
|
||||
}
|
||||
logrus.Infof("recorded share '%v' with id '%v' for '%v'", shrToken, sid, principal.Email)
|
||||
|
||||
return share.NewShareCreated().WithPayload(&rest_model_zrok.ShareResponse{
|
||||
FrontendProxyEndpoints: frontendEndpoints,
|
||||
ShrToken: shrToken,
|
||||
})
|
||||
}
|
||||
|
||||
func (h *shareHandler) checkLimits(principal *rest_model_zrok.Principal, envs []*store.Environment, tx *sqlx.Tx) error {
|
||||
if !principal.Limitless && h.cfg.Shares > Unlimited {
|
||||
total := 0
|
||||
for i := range envs {
|
||||
shrs, err := str.FindSharesForEnvironment(envs[i].Id, tx)
|
||||
if err != nil {
|
||||
return errors.Errorf("unable to find shares for environment '%v': %v", envs[i].ZId, err)
|
||||
}
|
||||
total += len(shrs)
|
||||
if total+1 > h.cfg.Shares {
|
||||
return errors.Errorf("would exceed shares limit of %d for '%v'", h.cfg.Shares, principal.Email)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
77
controller/shareDetail.go
Normal file
77
controller/shareDetail.go
Normal file
@ -0,0 +1,77 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/openziti-test-kitchen/zrok/controller/store"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_model_zrok"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_server_zrok/operations/metadata"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type shareDetailHandler struct{}
|
||||
|
||||
func newShareDetailHandler() *shareDetailHandler {
|
||||
return &shareDetailHandler{}
|
||||
}
|
||||
|
||||
func (h *shareDetailHandler) Handle(params metadata.GetShareDetailParams, principal *rest_model_zrok.Principal) middleware.Responder {
|
||||
tx, err := str.Begin()
|
||||
if err != nil {
|
||||
logrus.Errorf("error starting transaction: %v", err)
|
||||
return metadata.NewGetShareDetailInternalServerError()
|
||||
}
|
||||
defer func() { _ = tx.Rollback() }()
|
||||
shr, err := str.FindShareWithToken(params.ShrToken, tx)
|
||||
if err != nil {
|
||||
logrus.Errorf("error finding share '%v': %v", params.ShrToken, err)
|
||||
return metadata.NewGetShareDetailNotFound()
|
||||
}
|
||||
envs, err := str.FindEnvironmentsForAccount(int(principal.ID), tx)
|
||||
if err != nil {
|
||||
logrus.Errorf("error finding environments for account '%v': %v", principal.Email, err)
|
||||
return metadata.NewGetShareDetailInternalServerError()
|
||||
}
|
||||
found := false
|
||||
for _, env := range envs {
|
||||
if shr.EnvironmentId == env.Id {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
logrus.Errorf("environment not matched for share '%v' for account '%v'", params.ShrToken, principal.Email)
|
||||
return metadata.NewGetShareDetailNotFound()
|
||||
}
|
||||
var sparkData map[string][]int64
|
||||
if cfg.Influx != nil {
|
||||
sparkData, err = sparkDataForShares([]*store.Share{shr})
|
||||
if err != nil {
|
||||
logrus.Errorf("error querying spark data for share: %v", err)
|
||||
}
|
||||
}
|
||||
feEndpoint := ""
|
||||
if shr.FrontendEndpoint != nil {
|
||||
feEndpoint = *shr.FrontendEndpoint
|
||||
}
|
||||
feSelection := ""
|
||||
if shr.FrontendSelection != nil {
|
||||
feSelection = *shr.FrontendSelection
|
||||
}
|
||||
beProxyEndpoint := ""
|
||||
if shr.BackendProxyEndpoint != nil {
|
||||
beProxyEndpoint = *shr.BackendProxyEndpoint
|
||||
}
|
||||
return metadata.NewGetShareDetailOK().WithPayload(&rest_model_zrok.Share{
|
||||
Token: shr.Token,
|
||||
ZID: shr.ZId,
|
||||
ShareMode: shr.ShareMode,
|
||||
BackendMode: shr.BackendMode,
|
||||
FrontendSelection: feSelection,
|
||||
FrontendEndpoint: feEndpoint,
|
||||
BackendProxyEndpoint: beProxyEndpoint,
|
||||
Reserved: shr.Reserved,
|
||||
Metrics: sparkData[shr.Token],
|
||||
CreatedAt: shr.CreatedAt.UnixMilli(),
|
||||
UpdatedAt: shr.UpdatedAt.UnixMilli(),
|
||||
})
|
||||
}
|
40
controller/sharePrivate.go
Normal file
40
controller/sharePrivate.go
Normal file
@ -0,0 +1,40 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"github.com/openziti-test-kitchen/zrok/controller/zrokEdgeSdk"
|
||||
"github.com/openziti-test-kitchen/zrok/model"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_server_zrok/operations/share"
|
||||
"github.com/openziti/edge/rest_management_api_client"
|
||||
)
|
||||
|
||||
type privateResourceAllocator struct{}
|
||||
|
||||
func newPrivateResourceAllocator() *privateResourceAllocator {
|
||||
return &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
|
||||
for _, authUser := range params.Body.AuthUsers {
|
||||
authUsers = append(authUsers, &model.AuthUser{authUser.Username, authUser.Password})
|
||||
}
|
||||
cfgZId, err := zrokEdgeSdk.CreateConfig(zrokProxyConfigId, envZId, shrToken, params.Body.AuthScheme, authUsers, edge)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
shrZId, err = zrokEdgeSdk.CreateShareService(envZId, shrToken, cfgZId, edge)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
if err := zrokEdgeSdk.CreateServicePolicyBind(envZId+"-"+shrZId+"-bind", shrZId, envZId, zrokEdgeSdk.ZrokShareTags(shrToken).SubTags, edge); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
if err := zrokEdgeSdk.CreateShareServiceEdgeRouterPolicy(envZId, shrToken, shrZId, edge); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
return shrZId, nil, nil
|
||||
}
|
48
controller/sharePublic.go
Normal file
48
controller/sharePublic.go
Normal file
@ -0,0 +1,48 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"github.com/openziti-test-kitchen/zrok/controller/zrokEdgeSdk"
|
||||
"github.com/openziti-test-kitchen/zrok/model"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_server_zrok/operations/share"
|
||||
"github.com/openziti/edge/rest_management_api_client"
|
||||
)
|
||||
|
||||
type publicResourceAllocator struct{}
|
||||
|
||||
func newPublicResourceAllocator() *publicResourceAllocator {
|
||||
return &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
|
||||
for _, authUser := range params.Body.AuthUsers {
|
||||
authUsers = append(authUsers, &model.AuthUser{authUser.Username, authUser.Password})
|
||||
}
|
||||
cfgId, err := zrokEdgeSdk.CreateConfig(zrokProxyConfigId, envZId, shrToken, params.Body.AuthScheme, authUsers, edge)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
shrZId, err = zrokEdgeSdk.CreateShareService(envZId, shrToken, cfgId, edge)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
if err := zrokEdgeSdk.CreateServicePolicyBind(envZId+"-"+shrZId+"-bind", shrZId, envZId, zrokEdgeSdk.ZrokShareTags(shrToken).SubTags, edge); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
if err := zrokEdgeSdk.CreateServicePolicyDial(envZId+"-"+shrZId+"-dial", shrZId, frontendZIds, zrokEdgeSdk.ZrokShareTags(shrToken).SubTags, edge); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
if err := zrokEdgeSdk.CreateShareServiceEdgeRouterPolicy(envZId, shrToken, shrZId, edge); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
for _, frontendTemplate := range frontendTemplates {
|
||||
frontendEndpoints = append(frontendEndpoints, proxyUrl(shrToken, frontendTemplate))
|
||||
}
|
||||
|
||||
return shrZId, frontendEndpoints, nil
|
||||
}
|
58
controller/sparkData.go
Normal file
58
controller/sparkData.go
Normal file
@ -0,0 +1,58 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/openziti-test-kitchen/zrok/controller/store"
|
||||
)
|
||||
|
||||
func sparkDataForShares(shrs []*store.Share) (map[string][]int64, error) {
|
||||
out := make(map[string][]int64)
|
||||
|
||||
if len(shrs) > 0 {
|
||||
qapi := idb.QueryAPI(cfg.Influx.Org)
|
||||
|
||||
result, err := qapi.Query(context.Background(), sparkFluxQuery(shrs))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for result.Next() {
|
||||
combinedRate := int64(0)
|
||||
readRate := result.Record().ValueByKey("bytesRead")
|
||||
if readRate != nil {
|
||||
combinedRate += readRate.(int64)
|
||||
}
|
||||
writeRate := result.Record().ValueByKey("bytesWritten")
|
||||
if writeRate != nil {
|
||||
combinedRate += writeRate.(int64)
|
||||
}
|
||||
shrToken := result.Record().ValueByKey("share").(string)
|
||||
shrMetrics := out[shrToken]
|
||||
shrMetrics = append(shrMetrics, combinedRate)
|
||||
out[shrToken] = shrMetrics
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func sparkFluxQuery(shrs []*store.Share) string {
|
||||
shrFilter := "|> filter(fn: (r) =>"
|
||||
for i, shr := range shrs {
|
||||
if i > 0 {
|
||||
shrFilter += " or"
|
||||
}
|
||||
shrFilter += fmt.Sprintf(" r[\"share\"] == \"%v\"", shr.Token)
|
||||
}
|
||||
shrFilter += ")"
|
||||
query := "read = from(bucket: \"zrok\")" +
|
||||
"|> range(start: -5m)" +
|
||||
"|> filter(fn: (r) => r[\"_measurement\"] == \"xfer\")" +
|
||||
"|> filter(fn: (r) => r[\"_field\"] == \"bytesRead\" or r[\"_field\"] == \"bytesWritten\")" +
|
||||
"|> filter(fn: (r) => r[\"namespace\"] == \"frontend\")" +
|
||||
shrFilter +
|
||||
"|> aggregateWindow(every: 5s, fn: sum, createEmpty: true)\n" +
|
||||
"|> pivot(rowKey:[\"_time\"], columnKey: [\"_field\"], valueColumn: \"_value\")" +
|
||||
"|> yield(name: \"last\")"
|
||||
return query
|
||||
}
|
@ -6,7 +6,6 @@ import (
|
||||
"github.com/openziti-test-kitchen/zrok/model"
|
||||
"github.com/openziti/edge/rest_management_api_client"
|
||||
"github.com/openziti/edge/rest_management_api_client/config"
|
||||
"github.com/openziti/edge/rest_model"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"time"
|
||||
@ -28,14 +27,14 @@ func inspectZiti() error {
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error getting ziti edge client")
|
||||
}
|
||||
if err := ensureZrokProxyConfigType(edge); err != nil {
|
||||
if err := findZrokProxyConfigType(edge); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func ensureZrokProxyConfigType(edge *rest_management_api_client.ZitiEdgeManagement) error {
|
||||
func findZrokProxyConfigType(edge *rest_management_api_client.ZitiEdgeManagement) error {
|
||||
filter := fmt.Sprintf("name=\"%v\"", model.ZrokProxyConfig)
|
||||
limit := int64(100)
|
||||
offset := int64(0)
|
||||
@ -50,22 +49,11 @@ func ensureZrokProxyConfigType(edge *rest_management_api_client.ZitiEdgeManageme
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(listResp.Payload.Data) < 1 {
|
||||
name := model.ZrokProxyConfig
|
||||
ct := &rest_model.ConfigTypeCreate{Name: &name}
|
||||
createReq := &config.CreateConfigTypeParams{ConfigType: ct}
|
||||
createReq.SetTimeout(30 * time.Second)
|
||||
createResp, err := edge.Config.CreateConfigType(createReq, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
if len(listResp.Payload.Data) != 1 {
|
||||
return errors.Errorf("expected 1 zrok proxy config type, found %d", len(listResp.Payload.Data))
|
||||
}
|
||||
logrus.Infof("created '%v' config type with id '%v'", model.ZrokProxyConfig, createResp.Payload.Data.ID)
|
||||
zrokProxyConfigId = 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)
|
||||
} else {
|
||||
logrus.Infof("found '%v' config type with id '%v'", model.ZrokProxyConfig, *(listResp.Payload.Data[0].ID))
|
||||
zrokProxyConfigId = *(listResp.Payload.Data[0].ID)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -10,15 +10,16 @@ type Account struct {
|
||||
Email string
|
||||
Password string
|
||||
Token string
|
||||
Limitless bool
|
||||
}
|
||||
|
||||
func (self *Store) CreateAccount(a *Account, tx *sqlx.Tx) (int, error) {
|
||||
stmt, err := tx.Prepare("insert into accounts (email, password, token) values ($1, $2, $3) returning id")
|
||||
stmt, err := tx.Prepare("insert into accounts (email, password, token, limitless) values ($1, $2, $3, $4) returning id")
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "error preparing accounts insert statement")
|
||||
}
|
||||
var id int
|
||||
if err := stmt.QueryRow(a.Email, a.Password, a.Token).Scan(&id); err != nil {
|
||||
if err := stmt.QueryRow(a.Email, a.Password, a.Token, a.Limitless).Scan(&id); err != nil {
|
||||
return 0, errors.Wrap(err, "error executing accounts insert statement")
|
||||
}
|
||||
return id, nil
|
||||
|
@ -1,6 +1,10 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
@ -40,6 +44,34 @@ func (self *Store) FindAccountRequestWithToken(token string, tx *sqlx.Tx) (*Acco
|
||||
return ar, nil
|
||||
}
|
||||
|
||||
func (self *Store) FindExpiredAccountRequests(before time.Time, limit int, tx *sqlx.Tx) ([]*AccountRequest, error) {
|
||||
var sql string
|
||||
switch self.cfg.Type {
|
||||
case "postgres":
|
||||
sql = "select * from account_requests where created_at < $1 limit %d for update"
|
||||
|
||||
case "sqlite3":
|
||||
sql = "select * from account_requests where created_at < $1 limit %d"
|
||||
|
||||
default:
|
||||
return nil, errors.Errorf("unknown database type '%v'", self.cfg.Type)
|
||||
}
|
||||
|
||||
rows, err := tx.Queryx(fmt.Sprintf(sql, limit), before)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error selecting expired account_requests")
|
||||
}
|
||||
var ars []*AccountRequest
|
||||
for rows.Next() {
|
||||
ar := &AccountRequest{}
|
||||
if err := rows.StructScan(ar); err != nil {
|
||||
return nil, errors.Wrap(err, "error scanning account_request")
|
||||
}
|
||||
ars = append(ars, ar)
|
||||
}
|
||||
return ars, nil
|
||||
}
|
||||
|
||||
func (self *Store) FindAccountRequestWithEmail(email string, tx *sqlx.Tx) (*AccountRequest, error) {
|
||||
ar := &AccountRequest{}
|
||||
if err := tx.QueryRowx("select * from account_requests where email = $1", email).StructScan(ar); err != nil {
|
||||
@ -59,3 +91,27 @@ func (self *Store) DeleteAccountRequest(id int, tx *sqlx.Tx) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Store) DeleteMultipleAccountRequests(ids []int, tx *sqlx.Tx) error {
|
||||
if len(ids) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
anyIds := make([]any, len(ids))
|
||||
indexes := make([]string, len(ids))
|
||||
|
||||
for i, id := range ids {
|
||||
anyIds[i] = id
|
||||
indexes[i] = fmt.Sprintf("$%d", i+1)
|
||||
}
|
||||
|
||||
stmt, err := tx.Prepare(fmt.Sprintf("delete from account_requests where id in (%s)", strings.Join(indexes, ",")))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error preparing account_requests delete multiple statement")
|
||||
}
|
||||
_, err = stmt.Exec(anyIds...)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error executing account_requests delete multiple statement")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ import (
|
||||
|
||||
type Environment struct {
|
||||
Model
|
||||
AccountId int
|
||||
AccountId *int
|
||||
Description string
|
||||
Host string
|
||||
Address string
|
||||
@ -26,6 +26,18 @@ func (self *Store) CreateEnvironment(accountId int, i *Environment, tx *sqlx.Tx)
|
||||
return id, nil
|
||||
}
|
||||
|
||||
func (self *Store) CreateEphemeralEnvironment(i *Environment, tx *sqlx.Tx) (int, error) {
|
||||
stmt, err := tx.Prepare("insert into environments (description, host, address, z_id) values ($1, $2, $3, $4) returning id")
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "error preparing environments (ephemeral) insert statement")
|
||||
}
|
||||
var id int
|
||||
if err := stmt.QueryRow(i.Description, i.Host, i.Address, i.ZId).Scan(&id); err != nil {
|
||||
return 0, errors.Wrap(err, "error executing environments (ephemeral) insert statement")
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
|
||||
func (self *Store) GetEnvironment(id int, tx *sqlx.Tx) (*Environment, error) {
|
||||
i := &Environment{}
|
||||
if err := tx.QueryRowx("select * from environments where id = $1", id).StructScan(i); err != nil {
|
||||
@ -50,6 +62,14 @@ func (self *Store) FindEnvironmentsForAccount(accountId int, tx *sqlx.Tx) ([]*En
|
||||
return is, nil
|
||||
}
|
||||
|
||||
func (self *Store) FindEnvironmentForAccount(envZId string, accountId int, tx *sqlx.Tx) (*Environment, error) {
|
||||
env := &Environment{}
|
||||
if err := tx.QueryRowx("select environments.* from environments where z_id = $1 and account_id = $2", envZId, accountId).StructScan(env); err != nil {
|
||||
return nil, errors.Wrap(err, "error finding environment by z_id and account_id")
|
||||
}
|
||||
return env, nil
|
||||
}
|
||||
|
||||
func (self *Store) DeleteEnvironment(id int, tx *sqlx.Tx) error {
|
||||
stmt, err := tx.Prepare("delete from environments where id = $1")
|
||||
if err != nil {
|
||||
|
60
controller/store/environment_test.go
Normal file
60
controller/store/environment_test.go
Normal file
@ -0,0 +1,60 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestEphemeralEnvironment(t *testing.T) {
|
||||
str, err := Open(&Config{Path: ":memory:", Type: "sqlite3"})
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, str)
|
||||
|
||||
tx, err := str.Begin()
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, tx)
|
||||
|
||||
envId, err := str.CreateEphemeralEnvironment(&Environment{
|
||||
Description: "description",
|
||||
Host: "host",
|
||||
Address: "address",
|
||||
ZId: "zId0",
|
||||
}, tx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
env, err := str.GetEnvironment(envId, tx)
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, env)
|
||||
assert.Nil(t, env.AccountId)
|
||||
}
|
||||
|
||||
func TestEnvironment(t *testing.T) {
|
||||
str, err := Open(&Config{Path: ":memory:", Type: "sqlite3"})
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, str)
|
||||
|
||||
tx, err := str.Begin()
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, tx)
|
||||
|
||||
acctId, err := str.CreateAccount(&Account{
|
||||
Email: "test@test.com",
|
||||
Password: "password",
|
||||
Token: "token",
|
||||
}, tx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
envId, err := str.CreateEnvironment(acctId, &Environment{
|
||||
Description: "description",
|
||||
Host: "host",
|
||||
Address: "address",
|
||||
ZId: "zId0",
|
||||
}, tx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
env, err := str.GetEnvironment(envId, tx)
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, env)
|
||||
assert.NotNil(t, env.AccountId)
|
||||
assert.Equal(t, acctId, *env.AccountId)
|
||||
}
|
130
controller/store/frontend.go
Normal file
130
controller/store/frontend.go
Normal file
@ -0,0 +1,130 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type Frontend struct {
|
||||
Model
|
||||
EnvironmentId *int
|
||||
Token string
|
||||
ZId string
|
||||
PublicName *string
|
||||
UrlTemplate *string
|
||||
Reserved bool
|
||||
}
|
||||
|
||||
func (str *Store) CreateFrontend(envId int, f *Frontend, tx *sqlx.Tx) (int, error) {
|
||||
stmt, err := tx.Prepare("insert into frontends (environment_id, token, z_id, public_name, url_template, reserved) values ($1, $2, $3, $4, $5, $6) returning id")
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "error preparing frontends insert statement")
|
||||
}
|
||||
var id int
|
||||
if err := stmt.QueryRow(envId, f.Token, f.ZId, f.PublicName, f.UrlTemplate, f.Reserved).Scan(&id); err != nil {
|
||||
return 0, errors.Wrap(err, "error executing frontends insert statement")
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
|
||||
func (str *Store) CreateGlobalFrontend(f *Frontend, tx *sqlx.Tx) (int, error) {
|
||||
stmt, err := tx.Prepare("insert into frontends (token, z_id, public_name, url_template, reserved) values ($1, $2, $3, $4, $5) returning id")
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "error preparing global frontends insert statement")
|
||||
}
|
||||
var id int
|
||||
if err := stmt.QueryRow(f.Token, f.ZId, f.PublicName, f.UrlTemplate, f.Reserved).Scan(&id); err != nil {
|
||||
return 0, errors.Wrap(err, "error executing global frontends insert statement")
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
|
||||
func (str *Store) GetFrontend(id int, tx *sqlx.Tx) (*Frontend, error) {
|
||||
i := &Frontend{}
|
||||
if err := tx.QueryRowx("select * from frontends where id = $1", id).StructScan(i); err != nil {
|
||||
return nil, errors.Wrap(err, "error selecting frontend by id")
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func (str *Store) FindFrontendWithToken(token string, tx *sqlx.Tx) (*Frontend, error) {
|
||||
i := &Frontend{}
|
||||
if err := tx.QueryRowx("select frontends.* from frontends where token = $1", token).StructScan(i); err != nil {
|
||||
return nil, errors.Wrap(err, "error selecting frontend by name")
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func (str *Store) FindFrontendWithZId(zId string, tx *sqlx.Tx) (*Frontend, error) {
|
||||
i := &Frontend{}
|
||||
if err := tx.QueryRowx("select frontends.* from frontends where z_id = $1", zId).StructScan(i); err != nil {
|
||||
return nil, errors.Wrap(err, "error selecting frontend by ziti id")
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func (str *Store) FindFrontendPubliclyNamed(publicName string, tx *sqlx.Tx) (*Frontend, error) {
|
||||
i := &Frontend{}
|
||||
if err := tx.QueryRowx("select frontends.* from frontends where public_name = $1", publicName).StructScan(i); err != nil {
|
||||
return nil, errors.Wrap(err, "error selecting frontend by public_name")
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func (str *Store) FindFrontendsForEnvironment(envId int, tx *sqlx.Tx) ([]*Frontend, error) {
|
||||
rows, err := tx.Queryx("select frontends.* from frontends where environment_id = $1", envId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error selecting frontends by environment_id")
|
||||
}
|
||||
var is []*Frontend
|
||||
for rows.Next() {
|
||||
i := &Frontend{}
|
||||
if err := rows.StructScan(i); err != nil {
|
||||
return nil, errors.Wrap(err, "error scanning frontend")
|
||||
}
|
||||
is = append(is, i)
|
||||
}
|
||||
return is, nil
|
||||
}
|
||||
|
||||
func (str *Store) FindPublicFrontends(tx *sqlx.Tx) ([]*Frontend, error) {
|
||||
rows, err := tx.Queryx("select frontends.* from frontends where environment_id is null and reserved = true")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error selecting public frontends")
|
||||
}
|
||||
var frontends []*Frontend
|
||||
for rows.Next() {
|
||||
frontend := &Frontend{}
|
||||
if err := rows.StructScan(frontend); err != nil {
|
||||
return nil, errors.Wrap(err, "error scanning frontend")
|
||||
}
|
||||
frontends = append(frontends, frontend)
|
||||
}
|
||||
return frontends, nil
|
||||
}
|
||||
|
||||
func (str *Store) UpdateFrontend(fe *Frontend, tx *sqlx.Tx) error {
|
||||
sql := "update frontends set environment_id = $1, token = $2, z_id = $3, public_name = $4, url_template = $5, reserved = $6, updated_at = current_timestamp where id = $7"
|
||||
stmt, err := tx.Prepare(sql)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error preparing frontends update statement")
|
||||
}
|
||||
_, err = stmt.Exec(fe.EnvironmentId, fe.Token, fe.ZId, fe.PublicName, fe.UrlTemplate, fe.Reserved, fe.Id)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error executing frontends update statement")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (str *Store) DeleteFrontend(id int, tx *sqlx.Tx) error {
|
||||
|
||||
stmt, err := tx.Prepare("delete from frontends where id = $1")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error preparing frontends delete statement")
|
||||
}
|
||||
_, err = stmt.Exec(id)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error executing frontends delete statement")
|
||||
}
|
||||
return nil
|
||||
}
|
61
controller/store/frontend_test.go
Normal file
61
controller/store/frontend_test.go
Normal file
@ -0,0 +1,61 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPublicFrontend(t *testing.T) {
|
||||
str, err := Open(&Config{Path: ":memory:", Type: "sqlite3"})
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, str)
|
||||
|
||||
tx, err := str.Begin()
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, tx)
|
||||
|
||||
acctId, err := str.CreateAccount(&Account{
|
||||
Email: "test@test.com",
|
||||
Password: "password",
|
||||
Token: "token",
|
||||
}, tx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
envId, err := str.CreateEnvironment(acctId, &Environment{
|
||||
Description: "description",
|
||||
Host: "host",
|
||||
Address: "address",
|
||||
ZId: "zId0",
|
||||
}, tx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
feName := "public"
|
||||
feId, err := str.CreateFrontend(envId, &Frontend{
|
||||
Token: "token",
|
||||
ZId: "zId0",
|
||||
PublicName: &feName,
|
||||
}, tx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
fe, err := str.GetFrontend(feId, tx)
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, fe)
|
||||
assert.Equal(t, envId, *fe.EnvironmentId)
|
||||
assert.Equal(t, feName, *fe.PublicName)
|
||||
|
||||
fe0, err := str.FindFrontendPubliclyNamed(feName, tx)
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, fe0)
|
||||
assert.EqualValues(t, fe, fe0)
|
||||
|
||||
err = str.DeleteFrontend(fe.Id, tx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
fe0, err = str.FindFrontendWithToken(feName, tx)
|
||||
assert.NotNil(t, err)
|
||||
assert.Nil(t, fe0)
|
||||
|
||||
fe0, err = str.GetFrontend(fe.Id, tx)
|
||||
assert.NotNil(t, err)
|
||||
assert.Nil(t, fe0)
|
||||
}
|
52
controller/store/invite_tokens.go
Normal file
52
controller/store/invite_tokens.go
Normal file
@ -0,0 +1,52 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type InviteToken struct {
|
||||
Model
|
||||
Token string
|
||||
}
|
||||
|
||||
func (str *Store) CreateInviteTokens(inviteTokens []*InviteToken, tx *sqlx.Tx) error {
|
||||
sql := "insert into invite_tokens (token) values %s"
|
||||
invs := make([]any, len(inviteTokens))
|
||||
queries := make([]string, len(inviteTokens))
|
||||
for i, inv := range inviteTokens {
|
||||
invs[i] = inv.Token
|
||||
queries[i] = fmt.Sprintf("($%d)", i+1)
|
||||
}
|
||||
stmt, err := tx.Prepare(fmt.Sprintf(sql, strings.Join(queries, ",")))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error preparing invite_tokens insert statement")
|
||||
}
|
||||
if _, err := stmt.Exec(invs...); err != nil {
|
||||
return errors.Wrap(err, "error executing invites_tokens insert statement")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (str *Store) GetInviteTokenByToken(token string, tx *sqlx.Tx) (*InviteToken, error) {
|
||||
inviteToken := &InviteToken{}
|
||||
if err := tx.QueryRowx("select * from invite_tokens where token = $1", token).StructScan(inviteToken); err != nil {
|
||||
return nil, errors.Wrap(err, "error getting unused invite_token")
|
||||
}
|
||||
return inviteToken, nil
|
||||
}
|
||||
|
||||
func (str *Store) DeleteInviteToken(id int, tx *sqlx.Tx) error {
|
||||
stmt, err := tx.Prepare("delete from invite_tokens where id = $1")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error preparing invite_tokens delete statement")
|
||||
}
|
||||
_, err = stmt.Exec(id)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error executing invite_tokens delete statement")
|
||||
}
|
||||
return nil
|
||||
}
|
@ -1,92 +0,0 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
Model
|
||||
EnvironmentId int
|
||||
ZId string
|
||||
Name string
|
||||
Frontend string
|
||||
Backend string
|
||||
}
|
||||
|
||||
func (self *Store) CreateService(envId int, svc *Service, tx *sqlx.Tx) (int, error) {
|
||||
stmt, err := tx.Prepare("insert into services (environment_id, z_id, name, frontend, backend) values ($1, $2, $3, $4, $5) returning id")
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "error preparing services insert statement")
|
||||
}
|
||||
var id int
|
||||
if err := stmt.QueryRow(envId, svc.ZId, svc.Name, svc.Frontend, svc.Backend).Scan(&id); err != nil {
|
||||
return 0, errors.Wrap(err, "error executing services insert statement")
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
|
||||
func (self *Store) GetService(id int, tx *sqlx.Tx) (*Service, error) {
|
||||
svc := &Service{}
|
||||
if err := tx.QueryRowx("select * from services where id = $1", id).StructScan(svc); err != nil {
|
||||
return nil, errors.Wrap(err, "error selecting service by id")
|
||||
}
|
||||
return svc, nil
|
||||
}
|
||||
|
||||
func (self *Store) GetAllServices(tx *sqlx.Tx) ([]*Service, error) {
|
||||
rows, err := tx.Queryx("select * from services order by id")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error selecting all services")
|
||||
}
|
||||
var svcs []*Service
|
||||
for rows.Next() {
|
||||
svc := &Service{}
|
||||
if err := rows.StructScan(svc); err != nil {
|
||||
return nil, errors.Wrap(err, "error scanning service")
|
||||
}
|
||||
svcs = append(svcs, svc)
|
||||
}
|
||||
return svcs, nil
|
||||
}
|
||||
|
||||
func (self *Store) FindServicesForEnvironment(envId int, tx *sqlx.Tx) ([]*Service, error) {
|
||||
rows, err := tx.Queryx("select services.* from services where environment_id = $1", envId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error selecting services by environment id")
|
||||
}
|
||||
var svcs []*Service
|
||||
for rows.Next() {
|
||||
svc := &Service{}
|
||||
if err := rows.StructScan(svc); err != nil {
|
||||
return nil, errors.Wrap(err, "error scanning service")
|
||||
}
|
||||
svcs = append(svcs, svc)
|
||||
}
|
||||
return svcs, nil
|
||||
}
|
||||
|
||||
func (self *Store) UpdateService(svc *Service, tx *sqlx.Tx) error {
|
||||
sql := "update services set z_id = $1, name = $2, frontend = $3, backend = $4, updated_at = strftime('%Y-%m-%d %H:%M:%f', 'now') where id = $5"
|
||||
stmt, err := tx.Prepare(sql)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error preparing services update statement")
|
||||
}
|
||||
_, err = stmt.Exec(svc.ZId, svc.Name, svc.Frontend, svc.Backend, svc.Id)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error executing services update statement")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Store) DeleteService(id int, tx *sqlx.Tx) error {
|
||||
stmt, err := tx.Prepare("delete from services where id = $1")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error preparing services delete statement")
|
||||
}
|
||||
_, err = stmt.Exec(id)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error executing services delete statement")
|
||||
}
|
||||
return nil
|
||||
}
|
104
controller/store/share.go
Normal file
104
controller/store/share.go
Normal file
@ -0,0 +1,104 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type Share struct {
|
||||
Model
|
||||
EnvironmentId int
|
||||
ZId string
|
||||
Token string
|
||||
ShareMode string
|
||||
BackendMode string
|
||||
FrontendSelection *string
|
||||
FrontendEndpoint *string
|
||||
BackendProxyEndpoint *string
|
||||
Reserved bool
|
||||
}
|
||||
|
||||
func (self *Store) CreateShare(envId int, shr *Share, tx *sqlx.Tx) (int, error) {
|
||||
stmt, err := tx.Prepare("insert into shares (environment_id, z_id, token, share_mode, backend_mode, frontend_selection, frontend_endpoint, backend_proxy_endpoint, reserved) values ($1, $2, $3, $4, $5, $6, $7, $8, $9) returning id")
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "error preparing shares insert statement")
|
||||
}
|
||||
var id int
|
||||
if err := stmt.QueryRow(envId, shr.ZId, shr.Token, shr.ShareMode, shr.BackendMode, shr.FrontendSelection, shr.FrontendEndpoint, shr.BackendProxyEndpoint, shr.Reserved).Scan(&id); err != nil {
|
||||
return 0, errors.Wrap(err, "error executing shares insert statement")
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
|
||||
func (self *Store) GetShare(id int, tx *sqlx.Tx) (*Share, error) {
|
||||
shr := &Share{}
|
||||
if err := tx.QueryRowx("select * from shares where id = $1", id).StructScan(shr); err != nil {
|
||||
return nil, errors.Wrap(err, "error selecting share by id")
|
||||
}
|
||||
return shr, nil
|
||||
}
|
||||
|
||||
func (self *Store) GetAllShares(tx *sqlx.Tx) ([]*Share, error) {
|
||||
rows, err := tx.Queryx("select * from shares order by id")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error selecting all shares")
|
||||
}
|
||||
var shrs []*Share
|
||||
for rows.Next() {
|
||||
shr := &Share{}
|
||||
if err := rows.StructScan(shr); err != nil {
|
||||
return nil, errors.Wrap(err, "error scanning share")
|
||||
}
|
||||
shrs = append(shrs, shr)
|
||||
}
|
||||
return shrs, nil
|
||||
}
|
||||
|
||||
func (self *Store) FindShareWithToken(shrToken string, tx *sqlx.Tx) (*Share, error) {
|
||||
shr := &Share{}
|
||||
if err := tx.QueryRowx("select * from shares where token = $1", shrToken).StructScan(shr); err != nil {
|
||||
return nil, errors.Wrap(err, "error selecting share by token")
|
||||
}
|
||||
return shr, nil
|
||||
}
|
||||
|
||||
func (self *Store) FindSharesForEnvironment(envId int, tx *sqlx.Tx) ([]*Share, error) {
|
||||
rows, err := tx.Queryx("select shares.* from shares where environment_id = $1", envId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error selecting shares by environment id")
|
||||
}
|
||||
var shrs []*Share
|
||||
for rows.Next() {
|
||||
shr := &Share{}
|
||||
if err := rows.StructScan(shr); err != nil {
|
||||
return nil, errors.Wrap(err, "error scanning share")
|
||||
}
|
||||
shrs = append(shrs, shr)
|
||||
}
|
||||
return shrs, nil
|
||||
}
|
||||
|
||||
func (self *Store) UpdateShare(shr *Share, tx *sqlx.Tx) error {
|
||||
sql := "update shares set z_id = $1, token = $2, share_mode = $3, backend_mode = $4, frontend_selection = $5, frontend_endpoint = $6, backend_proxy_endpoint = $7, reserved = $8, updated_at = current_timestamp where id = $9"
|
||||
stmt, err := tx.Prepare(sql)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error preparing shares update statement")
|
||||
}
|
||||
_, err = stmt.Exec(shr.ZId, shr.Token, shr.ShareMode, shr.BackendMode, shr.FrontendSelection, shr.FrontendEndpoint, shr.BackendProxyEndpoint, shr.Reserved, shr.Id)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error executing shares update statement")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Store) DeleteShare(id int, tx *sqlx.Tx) error {
|
||||
stmt, err := tx.Prepare("delete from shares where id = $1")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error preparing shares delete statement")
|
||||
}
|
||||
_, err = stmt.Exec(id)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error executing shares delete statement")
|
||||
}
|
||||
return nil
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
-- +migrate Up
|
||||
|
||||
create table frontends (
|
||||
id serial primary key,
|
||||
environment_id integer references environments(id),
|
||||
token varchar(32) not null unique,
|
||||
z_id varchar(32) not null,
|
||||
url_template varchar(1024),
|
||||
public_name varchar(64) unique,
|
||||
reserved boolean not null default(false),
|
||||
created_at timestamptz not null default(current_timestamp),
|
||||
updated_at timestamptz not null default(current_timestamp)
|
||||
);
|
||||
|
||||
-- environments.account_id should allow NULL; environments with NULL account_id are "ephemeral"
|
||||
alter table environments drop constraint fk_accounts_identities;
|
||||
alter table environments add constraint fk_accounts_id foreign key (account_id) references accounts(id);
|
44
controller/store/sql/postgresql/003_v0_3_0_service_modes.sql
Normal file
44
controller/store/sql/postgresql/003_v0_3_0_service_modes.sql
Normal file
@ -0,0 +1,44 @@
|
||||
-- +migrate Up
|
||||
|
||||
create type share_mode as enum ('public', 'private');
|
||||
create type backend_mode as enum ('proxy', 'web', 'dav');
|
||||
|
||||
alter table services
|
||||
add column frontend_selection varchar(64),
|
||||
add column share_mode share_mode not null default 'public',
|
||||
add column backend_mode backend_mode not null default 'proxy',
|
||||
add column reserved boolean not null default false;
|
||||
|
||||
alter table services
|
||||
alter column share_mode drop default;
|
||||
alter table services
|
||||
alter column backend_mode drop default;
|
||||
|
||||
alter table services rename frontend to frontend_endpoint;
|
||||
alter table services rename backend to backend_proxy_endpoint;
|
||||
alter table services rename name to token;
|
||||
|
||||
alter table services rename to services_old;
|
||||
|
||||
create table services (
|
||||
id serial primary key,
|
||||
environment_id integer not null references environments(id),
|
||||
z_id varchar(32) not null unique,
|
||||
token varchar(32) not null unique,
|
||||
share_mode share_mode not null,
|
||||
backend_mode backend_mode not null,
|
||||
frontend_selection varchar(64),
|
||||
frontend_endpoint varchar(1024),
|
||||
backend_proxy_endpoint varchar(1024),
|
||||
reserved boolean not null default(false),
|
||||
created_at timestamptz not null default(current_timestamp),
|
||||
updated_at timestamptz not null default(current_timestamp),
|
||||
|
||||
constraint chk_z_id check (z_id <> ''),
|
||||
constraint chk_token check (token <> '')
|
||||
);
|
||||
|
||||
insert into services (id, environment_id, z_id, token, share_mode, backend_mode, frontend_selection, frontend_endpoint, backend_proxy_endpoint, created_at, updated_at)
|
||||
select id, environment_id, z_id, token, share_mode, backend_mode, frontend_selection, frontend_endpoint, backend_proxy_endpoint, created_at, updated_at from services_old;
|
||||
|
||||
drop table services_old;
|
@ -0,0 +1,8 @@
|
||||
-- +migrate Up
|
||||
|
||||
alter table services rename to shares;
|
||||
alter sequence services_id_seq1 rename to shares_id_seq1;
|
||||
alter index services_pkey1 rename to shares_pkey1;
|
||||
alter index services_token_key rename to shares_token_key;
|
||||
alter index services_z_id_key1 rename to shares_z_id_key1;
|
||||
alter table shares rename constraint services_environment_id_fkey to shares_environment_id_fkey;
|
14
controller/store/sql/postgresql/005_v0_3_0_invite_tokens.sql
Normal file
14
controller/store/sql/postgresql/005_v0_3_0_invite_tokens.sql
Normal file
@ -0,0 +1,14 @@
|
||||
-- +migrate Up
|
||||
|
||||
--
|
||||
-- invite_tokens
|
||||
---
|
||||
|
||||
create table invite_tokens (
|
||||
id serial primary key,
|
||||
token varchar(32) not null unique,
|
||||
created_at timestamptz not null default(current_timestamp),
|
||||
updated_at timestamptz not null default(current_timestamp),
|
||||
|
||||
constraint chk_token check(token <> '')
|
||||
);
|
@ -0,0 +1,25 @@
|
||||
-- +migrate Up
|
||||
|
||||
alter table accounts rename to accounts_old;
|
||||
|
||||
create table accounts (
|
||||
id serial primary key,
|
||||
email varchar(1024) not null unique,
|
||||
password char(128) not null,
|
||||
token varchar(32) not null unique,
|
||||
limitless boolean not null default(false),
|
||||
created_at timestamp not null default(current_timestamp),
|
||||
updated_at timestamp not null default(current_timestamp),
|
||||
|
||||
constraint chk_email check (email <> ''),
|
||||
constraint chk_password check (password <> ''),
|
||||
constraint chk_token check(token <> '')
|
||||
);
|
||||
|
||||
insert into accounts(id, email, password, token, created_at, updated_at)
|
||||
select id, email, password, token, created_at, updated_at from accounts_old;
|
||||
|
||||
alter table environments drop constraint fk_accounts_id;
|
||||
alter table environments add constraint fk_accounts_id foreign key (account_id) references accounts(id);
|
||||
|
||||
drop table accounts_old;
|
@ -0,0 +1,30 @@
|
||||
-- +migrate Up
|
||||
|
||||
-- environments.account_id should allow NULL; environments with NULL account_id are "ephemeral"
|
||||
alter table environments rename to environments_old;
|
||||
create table environments (
|
||||
id integer primary key,
|
||||
account_id integer references accounts(id) on delete cascade,
|
||||
description string,
|
||||
host string,
|
||||
address string,
|
||||
z_id string not null unique,
|
||||
created_at datetime not null default(strftime('%Y-%m-%d %H:%M:%f', 'now')),
|
||||
updated_at datetime not null default(strftime('%Y-%m-%d %H:%M:%f', 'now')),
|
||||
|
||||
constraint chk_z_id check (z_id <> '')
|
||||
);
|
||||
insert into environments select * from environments_old;
|
||||
drop table environments_old;
|
||||
|
||||
create table frontends (
|
||||
id integer primary key,
|
||||
environment_id integer references environments(id),
|
||||
token varchar(32) not null unique,
|
||||
z_id varchar(32) not null,
|
||||
public_name varchar(64) unique,
|
||||
url_template varchar(1024),
|
||||
reserved boolean not null default(false),
|
||||
created_at datetime not null default(strftime('%Y-%m-%d %H:%M:%f', 'now')),
|
||||
updated_at datetime not null default(strftime('%Y-%m-%d %H:%M:%f', 'now'))
|
||||
);
|
34
controller/store/sql/sqlite3/003_v0_3_0_service_modes.sql
Normal file
34
controller/store/sql/sqlite3/003_v0_3_0_service_modes.sql
Normal file
@ -0,0 +1,34 @@
|
||||
-- +migrate Up
|
||||
|
||||
alter table services add column frontend_selection string;
|
||||
alter table services add column share_mode string not null default 'public';
|
||||
alter table services add column backend_mode string not null default 'proxy';
|
||||
alter table services add column reserved boolean not null default false;
|
||||
alter table services rename column name to token;
|
||||
|
||||
alter table services rename to services_old;
|
||||
|
||||
create table services (
|
||||
id integer primary key,
|
||||
environment_id integer constraint fk_environments_services references environments on delete cascade,
|
||||
z_id string not null unique,
|
||||
token string not null unique,
|
||||
share_mode string not null,
|
||||
backend_mode string not null,
|
||||
frontend_selection string,
|
||||
frontend_endpoint string,
|
||||
backend_proxy_endpoint string,
|
||||
reserved boolean not null default(false),
|
||||
created_at datetime not null default(strftime('%Y-%m-%d %H:%M:%f', 'now')),
|
||||
updated_at datetime not null default(strftime('%Y-%m-%d %H:%M:%f', 'now')),
|
||||
|
||||
constraint chk_z_id check (z_id <> ''),
|
||||
constraint chk_token check (token <> ''),
|
||||
constraint chk_share_mode check (share_mode == 'public' or share_mode == 'private'),
|
||||
constraint chk_backend_mode check (backend_mode == 'proxy' or backend_mode == 'web' or backend_mode == 'dav')
|
||||
);
|
||||
|
||||
insert into services (id, environment_id, z_id, token, share_mode, backend_mode, frontend_selection, frontend_endpoint, backend_proxy_endpoint, created_at, updated_at)
|
||||
select id, environment_id, z_id, token, share_mode, backend_mode, frontend_selection, frontend, backend, created_at, updated_at from services_old;
|
||||
|
||||
drop table services_old;
|
@ -0,0 +1,25 @@
|
||||
-- +migrate Up
|
||||
|
||||
create table shares (
|
||||
id integer primary key,
|
||||
environment_id integer constraint fk_environments_shares references environments on delete cascade,
|
||||
z_id string not null unique,
|
||||
token string not null unique,
|
||||
share_mode string not null,
|
||||
backend_mode string not null,
|
||||
frontend_selection string,
|
||||
frontend_endpoint string,
|
||||
backend_proxy_endpoint string,
|
||||
reserved boolean not null default(false),
|
||||
created_at datetime not null default(strftime('%Y-%m-%d %H:%M:%f', 'now')),
|
||||
updated_at datetime not null default(strftime('%Y-%m-%d %H:%M:%f', 'now')),
|
||||
|
||||
constraint chk_z_id check (z_id <> ''),
|
||||
constraint chk_token check (token <> ''),
|
||||
constraint chk_share_mode check (share_mode == 'public' or share_mode == 'private'),
|
||||
constraint chk_backend_mode check (backend_mode == 'proxy' or backend_mode == 'web' or backend_mode == 'dav')
|
||||
);
|
||||
|
||||
insert into shares select * from services;
|
||||
|
||||
drop table services;
|
14
controller/store/sql/sqlite3/005_v0_3_0_invite_tokens.sql
Normal file
14
controller/store/sql/sqlite3/005_v0_3_0_invite_tokens.sql
Normal file
@ -0,0 +1,14 @@
|
||||
-- +migrate Up
|
||||
|
||||
--
|
||||
-- invite_tokens
|
||||
---
|
||||
|
||||
create table invite_tokens (
|
||||
id integer primary key,
|
||||
token string not null unique,
|
||||
created_at datetime not null default(strftime('%Y-%m-%d %H:%M:%f', 'now')),
|
||||
updated_at datetime not null default(strftime('%Y-%m-%d %H:%M:%f', 'now')),
|
||||
|
||||
constraint chk_token check(token <> '')
|
||||
);
|
@ -0,0 +1,3 @@
|
||||
-- +migrate Up
|
||||
|
||||
alter table accounts add column limitless boolean not null default(false);
|
@ -1,270 +0,0 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/openziti-test-kitchen/zrok/build"
|
||||
"github.com/openziti-test-kitchen/zrok/controller/store"
|
||||
"github.com/openziti-test-kitchen/zrok/model"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_model_zrok"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_server_zrok/operations/tunnel"
|
||||
"github.com/openziti/edge/rest_management_api_client"
|
||||
"github.com/openziti/edge/rest_management_api_client/config"
|
||||
"github.com/openziti/edge/rest_management_api_client/service"
|
||||
"github.com/openziti/edge/rest_management_api_client/service_edge_router_policy"
|
||||
"github.com/openziti/edge/rest_management_api_client/service_policy"
|
||||
"github.com/openziti/edge/rest_model"
|
||||
"github.com/sirupsen/logrus"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type tunnelHandler struct {
|
||||
}
|
||||
|
||||
func newTunnelHandler() *tunnelHandler {
|
||||
return &tunnelHandler{}
|
||||
}
|
||||
|
||||
func (h *tunnelHandler) Handle(params tunnel.TunnelParams, principal *rest_model_zrok.Principal) middleware.Responder {
|
||||
tx, err := str.Begin()
|
||||
if err != nil {
|
||||
logrus.Errorf("error starting transaction: %v", err)
|
||||
return tunnel.NewTunnelInternalServerError()
|
||||
}
|
||||
defer func() { _ = tx.Rollback() }()
|
||||
|
||||
envZId := params.Body.ZID
|
||||
envId := 0
|
||||
if envs, err := str.FindEnvironmentsForAccount(int(principal.ID), tx); err == nil {
|
||||
found := false
|
||||
for _, env := range envs {
|
||||
if env.ZId == envZId {
|
||||
logrus.Debugf("found identity '%v' for user '%v'", envZId, principal.Email)
|
||||
envId = env.Id
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
logrus.Errorf("environment '%v' not found for user '%v'", envZId, principal.Email)
|
||||
return tunnel.NewTunnelUnauthorized().WithPayload("bad environment identity")
|
||||
}
|
||||
} else {
|
||||
logrus.Errorf("error finding environments for account '%v'", principal.Email)
|
||||
return tunnel.NewTunnelInternalServerError()
|
||||
}
|
||||
|
||||
edge, err := edgeClient()
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
return tunnel.NewTunnelInternalServerError()
|
||||
}
|
||||
svcName, err := createServiceName()
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
return tunnel.NewTunnelInternalServerError()
|
||||
}
|
||||
cfgId, err := h.createConfig(envZId, svcName, params, edge)
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
return tunnel.NewTunnelInternalServerError()
|
||||
}
|
||||
svcZId, err := h.createService(envZId, svcName, cfgId, edge)
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
return tunnel.NewTunnelInternalServerError()
|
||||
}
|
||||
if err := h.createServicePolicyBind(envZId, svcName, svcZId, envZId, edge); err != nil {
|
||||
logrus.Error(err)
|
||||
return tunnel.NewTunnelInternalServerError()
|
||||
}
|
||||
if err := h.createServicePolicyDial(envZId, svcName, svcZId, edge); err != nil {
|
||||
logrus.Error(err)
|
||||
return tunnel.NewTunnelInternalServerError()
|
||||
}
|
||||
if err := h.createServiceEdgeRouterPolicy(envZId, svcName, svcZId, edge); err != nil {
|
||||
logrus.Error(err)
|
||||
return tunnel.NewTunnelInternalServerError()
|
||||
}
|
||||
|
||||
logrus.Debugf("allocated service '%v'", svcName)
|
||||
|
||||
frontendUrl := h.proxyUrl(svcName)
|
||||
sid, err := str.CreateService(envId, &store.Service{
|
||||
ZId: svcZId,
|
||||
Name: svcName,
|
||||
Frontend: frontendUrl,
|
||||
Backend: params.Body.Endpoint,
|
||||
}, tx)
|
||||
if err != nil {
|
||||
logrus.Errorf("error creating service record: %v", err)
|
||||
_ = tx.Rollback()
|
||||
return tunnel.NewUntunnelInternalServerError()
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
logrus.Errorf("error committing service record: %v", err)
|
||||
return tunnel.NewTunnelInternalServerError()
|
||||
}
|
||||
logrus.Infof("recorded service '%v' with id '%v' for '%v'", svcName, sid, principal.Email)
|
||||
|
||||
return tunnel.NewTunnelCreated().WithPayload(&rest_model_zrok.TunnelResponse{
|
||||
ProxyEndpoint: frontendUrl,
|
||||
SvcName: svcName,
|
||||
})
|
||||
}
|
||||
|
||||
func (h *tunnelHandler) createConfig(envZId, svcName string, params tunnel.TunnelParams, edge *rest_management_api_client.ZitiEdgeManagement) (cfgID string, err error) {
|
||||
authScheme, err := model.ParseAuthScheme(params.Body.AuthScheme)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
cfg := &model.ProxyConfig{
|
||||
AuthScheme: authScheme,
|
||||
}
|
||||
if cfg.AuthScheme == model.Basic {
|
||||
cfg.BasicAuth = &model.BasicAuth{}
|
||||
for _, authUser := range params.Body.AuthUsers {
|
||||
cfg.BasicAuth.Users = append(cfg.BasicAuth.Users, &model.AuthUser{Username: authUser.Username, Password: authUser.Password})
|
||||
}
|
||||
}
|
||||
cfgCrt := &rest_model.ConfigCreate{
|
||||
ConfigTypeID: &zrokProxyConfigId,
|
||||
Data: cfg,
|
||||
Name: &svcName,
|
||||
Tags: h.zrokTags(svcName),
|
||||
}
|
||||
cfgReq := &config.CreateConfigParams{
|
||||
Config: cfgCrt,
|
||||
Context: context.Background(),
|
||||
}
|
||||
cfgReq.SetTimeout(30 * time.Second)
|
||||
cfgResp, err := edge.Config.CreateConfig(cfgReq, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
logrus.Infof("created config '%v' for environment '%v'", cfgResp.Payload.Data.ID, envZId)
|
||||
return cfgResp.Payload.Data.ID, nil
|
||||
}
|
||||
|
||||
func (h *tunnelHandler) createService(envZId, svcName, cfgId string, edge *rest_management_api_client.ZitiEdgeManagement) (serviceId string, err error) {
|
||||
configs := []string{cfgId}
|
||||
encryptionRequired := true
|
||||
svc := &rest_model.ServiceCreate{
|
||||
Configs: configs,
|
||||
EncryptionRequired: &encryptionRequired,
|
||||
Name: &svcName,
|
||||
Tags: h.zrokTags(svcName),
|
||||
}
|
||||
req := &service.CreateServiceParams{
|
||||
Service: svc,
|
||||
Context: context.Background(),
|
||||
}
|
||||
req.SetTimeout(30 * time.Second)
|
||||
resp, err := edge.Service.CreateService(req, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
logrus.Infof("created zrok service named '%v' (with ziti id '%v') for environment '%v'", svcName, resp.Payload.Data.ID, envZId)
|
||||
return resp.Payload.Data.ID, nil
|
||||
}
|
||||
|
||||
func (h *tunnelHandler) createServicePolicyBind(envZId, svcName, svcZId, envId string, edge *rest_management_api_client.ZitiEdgeManagement) error {
|
||||
semantic := rest_model.SemanticAllOf
|
||||
identityRoles := []string{fmt.Sprintf("@%v", envId)}
|
||||
name := fmt.Sprintf("%v-backend", svcName)
|
||||
var postureCheckRoles []string
|
||||
serviceRoles := []string{fmt.Sprintf("@%v", svcZId)}
|
||||
dialBind := rest_model.DialBindBind
|
||||
svcp := &rest_model.ServicePolicyCreate{
|
||||
IdentityRoles: identityRoles,
|
||||
Name: &name,
|
||||
PostureCheckRoles: postureCheckRoles,
|
||||
Semantic: &semantic,
|
||||
ServiceRoles: serviceRoles,
|
||||
Type: &dialBind,
|
||||
Tags: h.zrokTags(svcName),
|
||||
}
|
||||
req := &service_policy.CreateServicePolicyParams{
|
||||
Policy: svcp,
|
||||
Context: context.Background(),
|
||||
}
|
||||
req.SetTimeout(30 * time.Second)
|
||||
resp, err := edge.ServicePolicy.CreateServicePolicy(req, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logrus.Infof("created bind service policy '%v' for service '%v' for environment '%v'", resp.Payload.Data.ID, svcZId, envZId)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *tunnelHandler) createServicePolicyDial(envZId, svcName, svcZId string, edge *rest_management_api_client.ZitiEdgeManagement) error {
|
||||
var identityRoles []string
|
||||
for _, proxyIdentity := range cfg.Proxy.Identities {
|
||||
identityRoles = append(identityRoles, "@"+proxyIdentity)
|
||||
logrus.Infof("added proxy identity role '%v'", proxyIdentity)
|
||||
}
|
||||
name := fmt.Sprintf("%v-dial", svcName)
|
||||
var postureCheckRoles []string
|
||||
semantic := rest_model.SemanticAllOf
|
||||
serviceRoles := []string{fmt.Sprintf("@%v", svcZId)}
|
||||
dialBind := rest_model.DialBindDial
|
||||
svcp := &rest_model.ServicePolicyCreate{
|
||||
IdentityRoles: identityRoles,
|
||||
Name: &name,
|
||||
PostureCheckRoles: postureCheckRoles,
|
||||
Semantic: &semantic,
|
||||
ServiceRoles: serviceRoles,
|
||||
Type: &dialBind,
|
||||
Tags: h.zrokTags(svcName),
|
||||
}
|
||||
req := &service_policy.CreateServicePolicyParams{
|
||||
Policy: svcp,
|
||||
Context: context.Background(),
|
||||
}
|
||||
req.SetTimeout(30 * time.Second)
|
||||
resp, err := edge.ServicePolicy.CreateServicePolicy(req, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logrus.Infof("created dial service policy '%v' for service '%v' for environment '%v'", resp.Payload.Data.ID, svcZId, envZId)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *tunnelHandler) createServiceEdgeRouterPolicy(envZId, svcName, svcZId string, edge *rest_management_api_client.ZitiEdgeManagement) error {
|
||||
edgeRouterRoles := []string{"#all"}
|
||||
semantic := rest_model.SemanticAllOf
|
||||
serviceRoles := []string{fmt.Sprintf("@%v", svcZId)}
|
||||
serp := &rest_model.ServiceEdgeRouterPolicyCreate{
|
||||
EdgeRouterRoles: edgeRouterRoles,
|
||||
Name: &svcName,
|
||||
Semantic: &semantic,
|
||||
ServiceRoles: serviceRoles,
|
||||
Tags: h.zrokTags(svcName),
|
||||
}
|
||||
serpParams := &service_edge_router_policy.CreateServiceEdgeRouterPolicyParams{
|
||||
Policy: serp,
|
||||
Context: context.Background(),
|
||||
}
|
||||
serpParams.SetTimeout(30 * time.Second)
|
||||
resp, err := edge.ServiceEdgeRouterPolicy.CreateServiceEdgeRouterPolicy(serpParams, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logrus.Infof("created service edge router policy '%v' for service '%v' for environment '%v'", resp.Payload.Data.ID, svcZId, envZId)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *tunnelHandler) proxyUrl(svcName string) string {
|
||||
return strings.Replace(cfg.Proxy.UrlTemplate, "{svcName}", svcName, -1)
|
||||
}
|
||||
|
||||
func (h *tunnelHandler) zrokTags(svcName string) *rest_model.Tags {
|
||||
return &rest_model.Tags{
|
||||
SubTags: map[string]interface{}{
|
||||
"zrok": build.String(),
|
||||
"zrok-service-name": svcName,
|
||||
},
|
||||
}
|
||||
}
|
84
controller/unaccess.go
Normal file
84
controller/unaccess.go
Normal file
@ -0,0 +1,84 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/openziti-test-kitchen/zrok/controller/store"
|
||||
"github.com/openziti-test-kitchen/zrok/controller/zrokEdgeSdk"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_model_zrok"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_server_zrok/operations/share"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type unaccessHandler struct{}
|
||||
|
||||
func newUnaccessHandler() *unaccessHandler {
|
||||
return &unaccessHandler{}
|
||||
}
|
||||
|
||||
func (h *unaccessHandler) Handle(params share.UnaccessParams, principal *rest_model_zrok.Principal) middleware.Responder {
|
||||
feToken := params.Body.FrontendToken
|
||||
shrToken := params.Body.ShrToken
|
||||
envZId := params.Body.EnvZID
|
||||
logrus.Infof("processing unaccess request for frontend '%v' (share '%v', environment '%v')", feToken, shrToken, envZId)
|
||||
|
||||
tx, err := str.Begin()
|
||||
if err != nil {
|
||||
logrus.Errorf("error starting transaction: %v", err)
|
||||
return share.NewUnaccessInternalServerError()
|
||||
}
|
||||
defer func() { _ = tx.Rollback() }()
|
||||
|
||||
edge, err := edgeClient()
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
return share.NewUnaccessInternalServerError()
|
||||
}
|
||||
|
||||
var senv *store.Environment
|
||||
if envs, err := str.FindEnvironmentsForAccount(int(principal.ID), tx); err == nil {
|
||||
for _, env := range envs {
|
||||
if env.ZId == envZId {
|
||||
senv = env
|
||||
break
|
||||
}
|
||||
}
|
||||
if senv == nil {
|
||||
err := errors.Errorf("environment with id '%v' not found for '%v", envZId, principal.Email)
|
||||
logrus.Error(err)
|
||||
return share.NewUnaccessUnauthorized()
|
||||
}
|
||||
} else {
|
||||
logrus.Errorf("error finding environments for account '%v': %v", principal.Email, err)
|
||||
return share.NewUnaccessUnauthorized()
|
||||
}
|
||||
|
||||
sfe, err := str.FindFrontendWithToken(feToken, tx)
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
return share.NewUnaccessInternalServerError()
|
||||
}
|
||||
|
||||
if sfe == nil || (sfe.EnvironmentId != nil && *sfe.EnvironmentId != senv.Id) {
|
||||
logrus.Errorf("frontend named '%v' not found", feToken)
|
||||
return share.NewUnaccessInternalServerError()
|
||||
}
|
||||
|
||||
if err := str.DeleteFrontend(sfe.Id, tx); err != nil {
|
||||
logrus.Errorf("error deleting frontend named '%v': %v", feToken, err)
|
||||
return share.NewUnaccessNotFound()
|
||||
}
|
||||
|
||||
if err := zrokEdgeSdk.DeleteServicePolicy(envZId, fmt.Sprintf("tags.zrokShareToken=\"%v\" and tags.zrokFrontendToken=\"%v\" and type=1", shrToken, feToken), edge); err != nil {
|
||||
logrus.Errorf("error removing access to '%v' for '%v': %v", shrToken, envZId, err)
|
||||
return share.NewUnaccessInternalServerError()
|
||||
}
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
logrus.Errorf("error committing frontend '%v' delete: %v", feToken, err)
|
||||
return share.NewUnaccessInternalServerError()
|
||||
}
|
||||
|
||||
return share.NewUnaccessOK()
|
||||
}
|
142
controller/unshare.go
Normal file
142
controller/unshare.go
Normal file
@ -0,0 +1,142 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/openziti-test-kitchen/zrok/controller/store"
|
||||
"github.com/openziti-test-kitchen/zrok/controller/zrokEdgeSdk"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_model_zrok"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_server_zrok/operations/share"
|
||||
"github.com/openziti/edge/rest_management_api_client"
|
||||
edge_service "github.com/openziti/edge/rest_management_api_client/service"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"time"
|
||||
)
|
||||
|
||||
type unshareHandler struct{}
|
||||
|
||||
func newUnshareHandler() *unshareHandler {
|
||||
return &unshareHandler{}
|
||||
}
|
||||
|
||||
func (h *unshareHandler) Handle(params share.UnshareParams, principal *rest_model_zrok.Principal) middleware.Responder {
|
||||
tx, err := str.Begin()
|
||||
if err != nil {
|
||||
logrus.Errorf("error starting transaction: %v", err)
|
||||
return share.NewUnshareInternalServerError()
|
||||
}
|
||||
defer func() { _ = tx.Rollback() }()
|
||||
|
||||
edge, err := edgeClient()
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
return share.NewUnshareInternalServerError()
|
||||
}
|
||||
shrToken := params.Body.ShrToken
|
||||
shrZId, err := h.findShareZId(shrToken, edge)
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
return share.NewUnshareNotFound()
|
||||
}
|
||||
var senv *store.Environment
|
||||
if envs, err := str.FindEnvironmentsForAccount(int(principal.ID), tx); err == nil {
|
||||
for _, env := range envs {
|
||||
if env.ZId == params.Body.EnvZID {
|
||||
senv = env
|
||||
break
|
||||
}
|
||||
}
|
||||
if senv == nil {
|
||||
err := errors.Errorf("environment with id '%v' not found for '%v", params.Body.EnvZID, principal.Email)
|
||||
logrus.Error(err)
|
||||
return share.NewUnshareNotFound()
|
||||
}
|
||||
} else {
|
||||
logrus.Errorf("error finding environments for account '%v': %v", principal.Email, err)
|
||||
return share.NewUnshareNotFound()
|
||||
}
|
||||
|
||||
var sshr *store.Share
|
||||
if shrs, err := str.FindSharesForEnvironment(senv.Id, tx); err == nil {
|
||||
for _, shr := range shrs {
|
||||
if shr.ZId == shrZId {
|
||||
sshr = shr
|
||||
break
|
||||
}
|
||||
}
|
||||
if sshr == nil {
|
||||
err := errors.Errorf("share with id '%v' not found for '%v'", shrZId, principal.Email)
|
||||
logrus.Error(err)
|
||||
return share.NewUnshareNotFound()
|
||||
}
|
||||
} else {
|
||||
logrus.Errorf("error finding shares for account '%v': %v", principal.Email, err)
|
||||
return share.NewUnshareInternalServerError()
|
||||
}
|
||||
|
||||
if sshr.Reserved == params.Body.Reserved {
|
||||
// single tag-based share deallocator; should work regardless of sharing mode
|
||||
if err := h.deallocateResources(senv, shrToken, shrZId, edge); err != nil {
|
||||
logrus.Errorf("error unsharing ziti resources for '%v': %v", sshr, err)
|
||||
return share.NewUnshareInternalServerError()
|
||||
}
|
||||
|
||||
logrus.Debugf("deallocated share '%v'", shrToken)
|
||||
|
||||
if err := str.DeleteShare(sshr.Id, tx); err != nil {
|
||||
logrus.Errorf("error deactivating share '%v': %v", shrZId, err)
|
||||
return share.NewUnshareInternalServerError()
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
logrus.Errorf("error committing transaction for '%v': %v", shrZId, err)
|
||||
return share.NewUnshareInternalServerError()
|
||||
}
|
||||
|
||||
} else {
|
||||
logrus.Infof("share '%v' is reserved, skipping deallocation", shrToken)
|
||||
}
|
||||
|
||||
return share.NewUnshareOK()
|
||||
}
|
||||
|
||||
func (h *unshareHandler) findShareZId(shrToken string, edge *rest_management_api_client.ZitiEdgeManagement) (string, error) {
|
||||
filter := fmt.Sprintf("name=\"%v\"", shrToken)
|
||||
limit := int64(1)
|
||||
offset := int64(0)
|
||||
listReq := &edge_service.ListServicesParams{
|
||||
Filter: &filter,
|
||||
Limit: &limit,
|
||||
Offset: &offset,
|
||||
Context: context.Background(),
|
||||
}
|
||||
listReq.SetTimeout(30 * time.Second)
|
||||
listResp, err := edge.Service.ListServices(listReq, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(listResp.Payload.Data) == 1 {
|
||||
return *(listResp.Payload.Data[0].ID), nil
|
||||
}
|
||||
return "", errors.Errorf("share '%v' not found", shrToken)
|
||||
}
|
||||
|
||||
func (h *unshareHandler) deallocateResources(senv *store.Environment, shrToken, shrZId string, edge *rest_management_api_client.ZitiEdgeManagement) error {
|
||||
if err := zrokEdgeSdk.DeleteServiceEdgeRouterPolicy(senv.ZId, shrToken, edge); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := zrokEdgeSdk.DeleteServicePolicyDial(senv.ZId, shrToken, edge); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := zrokEdgeSdk.DeleteServicePolicyBind(senv.ZId, shrToken, edge); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := zrokEdgeSdk.DeleteConfig(senv.ZId, shrToken, edge); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := zrokEdgeSdk.DeleteService(senv.ZId, shrZId, edge); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
@ -1,133 +0,0 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/openziti-test-kitchen/zrok/controller/store"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_model_zrok"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_server_zrok/operations/tunnel"
|
||||
"github.com/openziti/edge/rest_management_api_client"
|
||||
"github.com/openziti/edge/rest_management_api_client/service"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"time"
|
||||
)
|
||||
|
||||
type untunnelHandler struct {
|
||||
}
|
||||
|
||||
func newUntunnelHandler() *untunnelHandler {
|
||||
return &untunnelHandler{}
|
||||
}
|
||||
|
||||
func (h *untunnelHandler) Handle(params tunnel.UntunnelParams, principal *rest_model_zrok.Principal) middleware.Responder {
|
||||
tx, err := str.Begin()
|
||||
if err != nil {
|
||||
logrus.Errorf("error starting transaction: %v", err)
|
||||
return tunnel.NewUntunnelInternalServerError()
|
||||
}
|
||||
defer func() { _ = tx.Rollback() }()
|
||||
|
||||
edge, err := edgeClient()
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
return tunnel.NewUntunnelInternalServerError()
|
||||
}
|
||||
svcName := params.Body.SvcName
|
||||
svcZId, err := h.findServiceZId(svcName, edge)
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
return tunnel.NewUntunnelInternalServerError()
|
||||
}
|
||||
var senv *store.Environment
|
||||
if envs, err := str.FindEnvironmentsForAccount(int(principal.ID), tx); err == nil {
|
||||
for _, env := range envs {
|
||||
if env.ZId == params.Body.ZID {
|
||||
senv = env
|
||||
break
|
||||
}
|
||||
}
|
||||
if senv == nil {
|
||||
err := errors.Errorf("environment with id '%v' not found for '%v", params.Body.ZID, principal.Email)
|
||||
logrus.Error(err)
|
||||
return tunnel.NewUntunnelNotFound()
|
||||
}
|
||||
} else {
|
||||
logrus.Errorf("error finding environments for account '%v': %v", principal.Email, err)
|
||||
return tunnel.NewUntunnelInternalServerError()
|
||||
}
|
||||
|
||||
var ssvc *store.Service
|
||||
if svcs, err := str.FindServicesForEnvironment(senv.Id, tx); err == nil {
|
||||
for _, svc := range svcs {
|
||||
if svc.ZId == svcZId {
|
||||
ssvc = svc
|
||||
break
|
||||
}
|
||||
}
|
||||
if ssvc == nil {
|
||||
err := errors.Errorf("service with id '%v' not found for '%v'", svcZId, principal.Email)
|
||||
logrus.Error(err)
|
||||
return tunnel.NewUntunnelNotFound()
|
||||
}
|
||||
} else {
|
||||
logrus.Errorf("error finding services for account '%v': %v", principal.Email, err)
|
||||
return tunnel.NewUntunnelInternalServerError()
|
||||
}
|
||||
|
||||
if err := deleteServiceEdgeRouterPolicy(senv.ZId, svcName, edge); err != nil {
|
||||
logrus.Error(err)
|
||||
return tunnel.NewUntunnelInternalServerError()
|
||||
}
|
||||
if err := deleteServicePolicyDial(senv.ZId, svcName, edge); err != nil {
|
||||
logrus.Error(err)
|
||||
return tunnel.NewUntunnelInternalServerError()
|
||||
}
|
||||
if err := deleteServicePolicyBind(senv.ZId, svcName, edge); err != nil {
|
||||
logrus.Error(err)
|
||||
return tunnel.NewUntunnelInternalServerError()
|
||||
}
|
||||
if err := deleteConfig(senv.ZId, svcName, edge); err != nil {
|
||||
logrus.Error(err)
|
||||
return tunnel.NewTunnelInternalServerError()
|
||||
}
|
||||
if err := deleteService(senv.ZId, svcZId, edge); err != nil {
|
||||
logrus.Error(err)
|
||||
return tunnel.NewUntunnelInternalServerError()
|
||||
}
|
||||
|
||||
logrus.Debugf("deallocated service '%v'", svcName)
|
||||
|
||||
if err := str.DeleteService(ssvc.Id, tx); err != nil {
|
||||
logrus.Errorf("error deactivating service '%v': %v", svcZId, err)
|
||||
return tunnel.NewUntunnelInternalServerError()
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
logrus.Errorf("error committing: %v", err)
|
||||
return tunnel.NewUntunnelInternalServerError()
|
||||
}
|
||||
|
||||
return tunnel.NewUntunnelOK()
|
||||
}
|
||||
|
||||
func (h *untunnelHandler) findServiceZId(svcName string, edge *rest_management_api_client.ZitiEdgeManagement) (string, error) {
|
||||
filter := fmt.Sprintf("name=\"%v\"", svcName)
|
||||
limit := int64(1)
|
||||
offset := int64(0)
|
||||
listReq := &service.ListServicesParams{
|
||||
Filter: &filter,
|
||||
Limit: &limit,
|
||||
Offset: &offset,
|
||||
Context: context.Background(),
|
||||
}
|
||||
listReq.SetTimeout(30 * time.Second)
|
||||
listResp, err := edge.Service.ListServices(listReq, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(listResp.Payload.Data) == 1 {
|
||||
return *(listResp.Payload.Data[0].ID), nil
|
||||
}
|
||||
return "", errors.Errorf("service '%v' not found", svcName)
|
||||
}
|
66
controller/updateFrontend.go
Normal file
66
controller/updateFrontend.go
Normal file
@ -0,0 +1,66 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_model_zrok"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_server_zrok/operations/admin"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type updateFrontendHandler struct{}
|
||||
|
||||
func newUpdateFrontendHandler() *updateFrontendHandler {
|
||||
return &updateFrontendHandler{}
|
||||
}
|
||||
|
||||
func (h *updateFrontendHandler) Handle(params admin.UpdateFrontendParams, principal *rest_model_zrok.Principal) middleware.Responder {
|
||||
feToken := params.Body.FrontendToken
|
||||
publicName := params.Body.PublicName
|
||||
urlTemplate := params.Body.URLTemplate
|
||||
|
||||
if !principal.Admin {
|
||||
logrus.Errorf("invalid admin principal")
|
||||
return admin.NewUpdateFrontendUnauthorized()
|
||||
}
|
||||
|
||||
tx, err := str.Begin()
|
||||
if err != nil {
|
||||
logrus.Errorf("error starting transaction: %v", err)
|
||||
return admin.NewUpdateFrontendInternalServerError()
|
||||
}
|
||||
defer func() { _ = tx.Rollback() }()
|
||||
|
||||
fe, err := str.FindFrontendWithToken(feToken, tx)
|
||||
if err != nil {
|
||||
logrus.Errorf("error finding frontend with token '%v': %v", feToken, err)
|
||||
return admin.NewUpdateFrontendNotFound()
|
||||
}
|
||||
|
||||
doUpdate := false
|
||||
if publicName != "" {
|
||||
if fe.PublicName == nil || (fe.PublicName != nil && *fe.PublicName != publicName) {
|
||||
fe.PublicName = &publicName
|
||||
doUpdate = true
|
||||
}
|
||||
}
|
||||
if urlTemplate != "" {
|
||||
if fe.UrlTemplate == nil || (fe.UrlTemplate != nil && *fe.UrlTemplate != urlTemplate) {
|
||||
fe.UrlTemplate = &urlTemplate
|
||||
doUpdate = true
|
||||
}
|
||||
}
|
||||
|
||||
if doUpdate {
|
||||
if err := str.UpdateFrontend(fe, tx); err != nil {
|
||||
logrus.Errorf("error updating frontend: %v", err)
|
||||
return admin.NewUpdateFrontendInternalServerError()
|
||||
}
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
logrus.Errorf("error commiting frontend update: %v", err)
|
||||
return admin.NewUpdateFrontendInternalServerError()
|
||||
}
|
||||
}
|
||||
|
||||
return admin.NewUpdateFrontendOK()
|
||||
}
|
63
controller/updateShare.go
Normal file
63
controller/updateShare.go
Normal file
@ -0,0 +1,63 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_model_zrok"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_server_zrok/operations/share"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type updateShareHandler struct{}
|
||||
|
||||
func newUpdateShareHandler() *updateShareHandler {
|
||||
return &updateShareHandler{}
|
||||
}
|
||||
|
||||
func (h *updateShareHandler) Handle(params share.UpdateShareParams, principal *rest_model_zrok.Principal) middleware.Responder {
|
||||
shrToken := params.Body.ShrToken
|
||||
backendProxyEndpoint := params.Body.BackendProxyEndpoint
|
||||
|
||||
tx, err := str.Begin()
|
||||
if err != nil {
|
||||
logrus.Errorf("error starting transaction: %v", err)
|
||||
return share.NewUpdateShareInternalServerError()
|
||||
}
|
||||
defer func() { _ = tx.Rollback() }()
|
||||
|
||||
sshr, err := str.FindShareWithToken(shrToken, tx)
|
||||
if err != nil {
|
||||
logrus.Errorf("share '%v' not found: %v", shrToken, err)
|
||||
return share.NewUpdateShareNotFound()
|
||||
}
|
||||
|
||||
senvs, err := str.FindEnvironmentsForAccount(int(principal.ID), tx)
|
||||
if err != nil {
|
||||
logrus.Errorf("error finding environments for account '%v': %v", principal.Email, err)
|
||||
return share.NewUpdateShareInternalServerError()
|
||||
}
|
||||
|
||||
envFound := false
|
||||
for _, senv := range senvs {
|
||||
if senv.Id == sshr.Id {
|
||||
envFound = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !envFound {
|
||||
logrus.Errorf("environment not found for share '%v'", shrToken)
|
||||
return share.NewUpdateShareNotFound()
|
||||
}
|
||||
|
||||
sshr.BackendProxyEndpoint = &backendProxyEndpoint
|
||||
if err := str.UpdateShare(sshr, tx); err != nil {
|
||||
logrus.Errorf("error updating share '%v': %v", shrToken, err)
|
||||
return share.NewUpdateShareInternalServerError()
|
||||
}
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
logrus.Errorf("error committing transaction for share '%v' update: %v", shrToken, err)
|
||||
return share.NewUpdateShareInternalServerError()
|
||||
}
|
||||
|
||||
return share.NewUpdateShareOK()
|
||||
}
|
@ -13,20 +13,44 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
func ZrokAuthenticate(token string) (*rest_model_zrok.Principal, error) {
|
||||
type zrokAuthenticator struct {
|
||||
cfg *Config
|
||||
}
|
||||
|
||||
func newZrokAuthenticator(cfg *Config) *zrokAuthenticator {
|
||||
return &zrokAuthenticator{cfg}
|
||||
}
|
||||
|
||||
func (za *zrokAuthenticator) authenticate(token string) (*rest_model_zrok.Principal, error) {
|
||||
tx, err := str.Begin()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() { _ = tx.Rollback() }()
|
||||
|
||||
if a, err := str.FindAccountWithToken(token, tx); err == nil {
|
||||
principal := rest_model_zrok.Principal{
|
||||
principal := &rest_model_zrok.Principal{
|
||||
ID: int64(a.Id),
|
||||
Token: a.Token,
|
||||
Email: a.Email,
|
||||
Limitless: a.Limitless,
|
||||
}
|
||||
return &principal, nil
|
||||
return principal, nil
|
||||
} else {
|
||||
// check for admin secret
|
||||
if cfg.Admin != nil {
|
||||
for _, secret := range cfg.Admin.Secrets {
|
||||
if token == secret {
|
||||
principal := &rest_model_zrok.Principal{
|
||||
ID: int64(-1),
|
||||
Admin: true,
|
||||
}
|
||||
return principal, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// no match
|
||||
return nil, errors2.New(401, "invalid api key")
|
||||
}
|
||||
}
|
||||
@ -43,7 +67,7 @@ func edgeClient() (*rest_management_api_client.ZitiEdgeManagement, error) {
|
||||
return rest_util.NewEdgeManagementClientWithUpdb(cfg.Ziti.Username, cfg.Ziti.Password, cfg.Ziti.ApiEndpoint, caPool)
|
||||
}
|
||||
|
||||
func createServiceName() (string, error) {
|
||||
func createShareToken() (string, error) {
|
||||
gen, err := nanoid.CustomASCII("abcdefghijklmnopqrstuvwxyz0123456789", 12)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@ -78,3 +102,7 @@ func realRemoteAddress(req *http.Request) string {
|
||||
}
|
||||
return ip
|
||||
}
|
||||
|
||||
func proxyUrl(shrToken, template string) string {
|
||||
return strings.Replace(template, "{token}", shrToken, -1)
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ package controller
|
||||
import (
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_model_zrok"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_server_zrok/operations/identity"
|
||||
"github.com/openziti-test-kitchen/zrok/rest_server_zrok/operations/account"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
@ -14,25 +14,24 @@ func newVerifyHandler() *verifyHandler {
|
||||
return &verifyHandler{}
|
||||
}
|
||||
|
||||
func (self *verifyHandler) Handle(params identity.VerifyParams) middleware.Responder {
|
||||
func (self *verifyHandler) Handle(params account.VerifyParams) middleware.Responder {
|
||||
if params.Body != nil {
|
||||
logrus.Debugf("received verify request for token '%v'", params.Body.Token)
|
||||
tx, err := str.Begin()
|
||||
if err != nil {
|
||||
logrus.Errorf("error starting transaction: %v", err)
|
||||
return identity.NewVerifyInternalServerError()
|
||||
return account.NewVerifyInternalServerError()
|
||||
}
|
||||
defer func() { _ = tx.Rollback() }()
|
||||
|
||||
ar, err := str.FindAccountRequestWithToken(params.Body.Token, tx)
|
||||
if err != nil {
|
||||
logrus.Errorf("error finding account with token '%v': %v", params.Body.Token, err)
|
||||
return identity.NewVerifyNotFound()
|
||||
return account.NewVerifyNotFound()
|
||||
}
|
||||
|
||||
return identity.NewVerifyOK().WithPayload(&rest_model_zrok.VerifyResponse{Email: ar.Email})
|
||||
} else {
|
||||
return account.NewVerifyOK().WithPayload(&rest_model_zrok.VerifyResponse{Email: ar.Email})
|
||||
}
|
||||
logrus.Error("empty verification request")
|
||||
return identity.NewVerifyInternalServerError().WithPayload(rest_model_zrok.ErrorMessage("empty verification request"))
|
||||
}
|
||||
return account.NewVerifyInternalServerError()
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ package controller
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/openziti-test-kitchen/zrok/controller/email_ui"
|
||||
"github.com/openziti-test-kitchen/zrok/controller/emailUi"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/wneessen/go-mail"
|
||||
@ -63,7 +63,7 @@ func sendVerificationEmail(emailAddress, token string) error {
|
||||
}
|
||||
|
||||
func mergeTemplate(emailData *verificationEmail, filename string) (string, error) {
|
||||
t, err := template.ParseFS(email_ui.FS, filename)
|
||||
t, err := template.ParseFS(emailUi.FS, filename)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "error parsing verification email template '%v'", filename)
|
||||
}
|
75
controller/zrokEdgeSdk/config.go
Normal file
75
controller/zrokEdgeSdk/config.go
Normal file
@ -0,0 +1,75 @@
|
||||
package zrokEdgeSdk
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/openziti-test-kitchen/zrok/model"
|
||||
"github.com/openziti/edge/rest_management_api_client"
|
||||
"github.com/openziti/edge/rest_management_api_client/config"
|
||||
"github.com/openziti/edge/rest_model"
|
||||
"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)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
cfg := &model.ProxyConfig{
|
||||
AuthScheme: authScheme,
|
||||
}
|
||||
if cfg.AuthScheme == model.Basic {
|
||||
cfg.BasicAuth = &model.BasicAuth{}
|
||||
for _, authUser := range authUsers {
|
||||
cfg.BasicAuth.Users = append(cfg.BasicAuth.Users, &model.AuthUser{Username: authUser.Username, Password: authUser.Password})
|
||||
}
|
||||
}
|
||||
cfgCrt := &rest_model.ConfigCreate{
|
||||
ConfigTypeID: &cfgTypeZId,
|
||||
Data: cfg,
|
||||
Name: &shrToken,
|
||||
Tags: ZrokShareTags(shrToken),
|
||||
}
|
||||
cfgReq := &config.CreateConfigParams{
|
||||
Config: cfgCrt,
|
||||
Context: context.Background(),
|
||||
}
|
||||
cfgReq.SetTimeout(30 * time.Second)
|
||||
cfgResp, err := edge.Config.CreateConfig(cfgReq, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
logrus.Infof("created config '%v' for environment '%v'", cfgResp.Payload.Data.ID, envZId)
|
||||
return cfgResp.Payload.Data.ID, nil
|
||||
}
|
||||
|
||||
func DeleteConfig(envZId, shrToken string, edge *rest_management_api_client.ZitiEdgeManagement) error {
|
||||
filter := fmt.Sprintf("tags.zrokShareToken=\"%v\"", shrToken)
|
||||
limit := int64(0)
|
||||
offset := int64(0)
|
||||
listReq := &config.ListConfigsParams{
|
||||
Filter: &filter,
|
||||
Limit: &limit,
|
||||
Offset: &offset,
|
||||
Context: context.Background(),
|
||||
}
|
||||
listReq.SetTimeout(30 * time.Second)
|
||||
listResp, err := edge.Config.ListConfigs(listReq, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, cfg := range listResp.Payload.Data {
|
||||
deleteReq := &config.DeleteConfigParams{
|
||||
ID: *cfg.ID,
|
||||
Context: context.Background(),
|
||||
}
|
||||
deleteReq.SetTimeout(30 * time.Second)
|
||||
_, err := edge.Config.DeleteConfig(deleteReq, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logrus.Infof("deleted config '%v' for '%v'", *cfg.ID, envZId)
|
||||
}
|
||||
return nil
|
||||
}
|
67
controller/zrokEdgeSdk/erp.go
Normal file
67
controller/zrokEdgeSdk/erp.go
Normal file
@ -0,0 +1,67 @@
|
||||
package zrokEdgeSdk
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/openziti/edge/rest_management_api_client"
|
||||
"github.com/openziti/edge/rest_management_api_client/edge_router_policy"
|
||||
rest_model_edge "github.com/openziti/edge/rest_model"
|
||||
"github.com/sirupsen/logrus"
|
||||
"time"
|
||||
)
|
||||
|
||||
func CreateEdgeRouterPolicy(name, zId string, edge *rest_management_api_client.ZitiEdgeManagement) error {
|
||||
edgeRouterRoles := []string{"#all"}
|
||||
identityRoles := []string{fmt.Sprintf("@%v", zId)}
|
||||
semantic := rest_model_edge.SemanticAllOf
|
||||
erp := &rest_model_edge.EdgeRouterPolicyCreate{
|
||||
EdgeRouterRoles: edgeRouterRoles,
|
||||
IdentityRoles: identityRoles,
|
||||
Name: &name,
|
||||
Semantic: &semantic,
|
||||
Tags: ZrokTags(),
|
||||
}
|
||||
req := &edge_router_policy.CreateEdgeRouterPolicyParams{
|
||||
Policy: erp,
|
||||
Context: context.Background(),
|
||||
}
|
||||
req.SetTimeout(30 * time.Second)
|
||||
resp, err := edge.EdgeRouterPolicy.CreateEdgeRouterPolicy(req, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logrus.Infof("created edge router policy '%v' for ziti identity '%v'", resp.Payload.Data.ID, zId)
|
||||
return nil
|
||||
}
|
||||
|
||||
func DeleteEdgeRouterPolicy(envZId string, edge *rest_management_api_client.ZitiEdgeManagement) error {
|
||||
filter := fmt.Sprintf("name=\"%v\"", envZId)
|
||||
limit := int64(0)
|
||||
offset := int64(0)
|
||||
listReq := &edge_router_policy.ListEdgeRouterPoliciesParams{
|
||||
Filter: &filter,
|
||||
Limit: &limit,
|
||||
Offset: &offset,
|
||||
Context: context.Background(),
|
||||
}
|
||||
listReq.SetTimeout(30 * time.Second)
|
||||
listResp, err := edge.EdgeRouterPolicy.ListEdgeRouterPolicies(listReq, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(listResp.Payload.Data) == 1 {
|
||||
erpId := *(listResp.Payload.Data[0].ID)
|
||||
req := &edge_router_policy.DeleteEdgeRouterPolicyParams{
|
||||
ID: erpId,
|
||||
Context: context.Background(),
|
||||
}
|
||||
_, err := edge.EdgeRouterPolicy.DeleteEdgeRouterPolicy(req, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logrus.Infof("deleted edge router policy '%v' for environment '%v'", erpId, envZId)
|
||||
} else {
|
||||
logrus.Infof("found '%d' edge router policies, expected 1", len(listResp.Payload.Data))
|
||||
}
|
||||
return nil
|
||||
}
|
101
controller/zrokEdgeSdk/identity.go
Normal file
101
controller/zrokEdgeSdk/identity.go
Normal file
@ -0,0 +1,101 @@
|
||||
package zrokEdgeSdk
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/openziti/edge/rest_management_api_client"
|
||||
"github.com/openziti/edge/rest_management_api_client/identity"
|
||||
rest_model_edge "github.com/openziti/edge/rest_model"
|
||||
"github.com/openziti/sdk-golang/ziti/config"
|
||||
"github.com/openziti/sdk-golang/ziti/enroll"
|
||||
"github.com/sirupsen/logrus"
|
||||
"time"
|
||||
)
|
||||
|
||||
func CreateEnvironmentIdentity(uniqueToken, accountEmail, envDescription string, edge *rest_management_api_client.ZitiEdgeManagement) (*identity.CreateIdentityCreated, error) {
|
||||
identityType := rest_model_edge.IdentityTypeUser
|
||||
moreTags := map[string]interface{}{"zrokEmail": accountEmail}
|
||||
return CreateIdentity(accountEmail+"-"+uniqueToken+"-"+envDescription, identityType, moreTags, edge)
|
||||
}
|
||||
|
||||
func CreateIdentity(name string, identityType rest_model_edge.IdentityType, addlTags map[string]interface{}, edge *rest_management_api_client.ZitiEdgeManagement) (*identity.CreateIdentityCreated, error) {
|
||||
isAdmin := false
|
||||
tags := ZrokTags()
|
||||
for k, v := range addlTags {
|
||||
tags.SubTags[k] = v
|
||||
}
|
||||
req := identity.NewCreateIdentityParams()
|
||||
req.Identity = &rest_model_edge.IdentityCreate{
|
||||
Enrollment: &rest_model_edge.IdentityCreateEnrollment{Ott: true},
|
||||
IsAdmin: &isAdmin,
|
||||
Name: &name,
|
||||
RoleAttributes: nil,
|
||||
ServiceHostingCosts: nil,
|
||||
Tags: tags,
|
||||
Type: &identityType,
|
||||
}
|
||||
req.SetTimeout(30 * time.Second)
|
||||
resp, err := edge.Identity.CreateIdentity(req, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func GetIdentityByZId(zId string, edge *rest_management_api_client.ZitiEdgeManagement) (*identity.ListIdentitiesOK, error) {
|
||||
filter := fmt.Sprintf("id=\"%v\"", zId)
|
||||
limit := int64(0)
|
||||
offset := int64(0)
|
||||
req := &identity.ListIdentitiesParams{
|
||||
Filter: &filter,
|
||||
Limit: &limit,
|
||||
Offset: &offset,
|
||||
Context: context.Background(),
|
||||
}
|
||||
req.SetTimeout(30 * time.Second)
|
||||
resp, err := edge.Identity.ListIdentities(req, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func EnrollIdentity(zId string, edge *rest_management_api_client.ZitiEdgeManagement) (*config.Config, error) {
|
||||
p := &identity.DetailIdentityParams{
|
||||
Context: context.Background(),
|
||||
ID: zId,
|
||||
}
|
||||
p.SetTimeout(30 * time.Second)
|
||||
resp, err := edge.Identity.DetailIdentity(p, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tkn, _, err := enroll.ParseToken(resp.GetPayload().Data.Enrollment.Ott.JWT)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
flags := enroll.EnrollmentFlags{
|
||||
Token: tkn,
|
||||
KeyAlg: "RSA",
|
||||
}
|
||||
conf, err := enroll.Enroll(flags)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logrus.Infof("enrolled ziti identity '%v'", zId)
|
||||
return conf, nil
|
||||
}
|
||||
|
||||
func DeleteIdentity(zId string, edge *rest_management_api_client.ZitiEdgeManagement) error {
|
||||
req := &identity.DeleteIdentityParams{
|
||||
ID: zId,
|
||||
Context: context.Background(),
|
||||
}
|
||||
req.SetTimeout(30 * time.Second)
|
||||
_, err := edge.Identity.DeleteIdentity(req, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logrus.Infof("deleted ziti identity '%v'", zId)
|
||||
return nil
|
||||
}
|
81
controller/zrokEdgeSdk/serp.go
Normal file
81
controller/zrokEdgeSdk/serp.go
Normal file
@ -0,0 +1,81 @@
|
||||
package zrokEdgeSdk
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/openziti/edge/rest_management_api_client"
|
||||
"github.com/openziti/edge/rest_management_api_client/service_edge_router_policy"
|
||||
"github.com/openziti/edge/rest_model"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"time"
|
||||
)
|
||||
|
||||
func CreateShareServiceEdgeRouterPolicy(envZId, shrToken, shrZId string, edge *rest_management_api_client.ZitiEdgeManagement) error {
|
||||
serpZId, err := CreateServiceEdgeRouterPolicy(shrToken, shrZId, ZrokShareTags(shrToken).SubTags, edge)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logrus.Infof("created service edge router policy '%v' for service '%v' for environment '%v'", serpZId, shrZId, envZId)
|
||||
return nil
|
||||
}
|
||||
|
||||
func CreateServiceEdgeRouterPolicy(name, shrZId string, moreTags map[string]interface{}, edge *rest_management_api_client.ZitiEdgeManagement) (string, error) {
|
||||
edgeRouterRoles := []string{"#all"}
|
||||
semantic := rest_model.SemanticAllOf
|
||||
serviceRoles := []string{fmt.Sprintf("@%v", shrZId)}
|
||||
tags := ZrokTags()
|
||||
for k, v := range moreTags {
|
||||
tags.SubTags[k] = v
|
||||
}
|
||||
serp := &rest_model.ServiceEdgeRouterPolicyCreate{
|
||||
EdgeRouterRoles: edgeRouterRoles,
|
||||
Name: &name,
|
||||
Semantic: &semantic,
|
||||
ServiceRoles: serviceRoles,
|
||||
Tags: tags,
|
||||
}
|
||||
serpParams := &service_edge_router_policy.CreateServiceEdgeRouterPolicyParams{
|
||||
Policy: serp,
|
||||
Context: context.Background(),
|
||||
}
|
||||
serpParams.SetTimeout(30 * time.Second)
|
||||
resp, err := edge.ServiceEdgeRouterPolicy.CreateServiceEdgeRouterPolicy(serpParams, nil)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "error creating serp '%v' for service '%v'", name, shrZId)
|
||||
}
|
||||
return resp.Payload.Data.ID, nil
|
||||
}
|
||||
|
||||
func DeleteServiceEdgeRouterPolicy(envZId, shrToken string, edge *rest_management_api_client.ZitiEdgeManagement) error {
|
||||
filter := fmt.Sprintf("tags.zrokShareToken=\"%v\"", shrToken)
|
||||
limit := int64(1)
|
||||
offset := int64(0)
|
||||
listReq := &service_edge_router_policy.ListServiceEdgeRouterPoliciesParams{
|
||||
Filter: &filter,
|
||||
Limit: &limit,
|
||||
Offset: &offset,
|
||||
Context: context.Background(),
|
||||
}
|
||||
listReq.SetTimeout(30 * time.Second)
|
||||
listResp, err := edge.ServiceEdgeRouterPolicy.ListServiceEdgeRouterPolicies(listReq, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(listResp.Payload.Data) == 1 {
|
||||
serpId := *(listResp.Payload.Data[0].ID)
|
||||
req := &service_edge_router_policy.DeleteServiceEdgeRouterPolicyParams{
|
||||
ID: serpId,
|
||||
Context: context.Background(),
|
||||
}
|
||||
req.SetTimeout(30 * time.Second)
|
||||
_, err := edge.ServiceEdgeRouterPolicy.DeleteServiceEdgeRouterPolicy(req, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logrus.Infof("deleted service edge router policy '%v' for environment '%v'", serpId, envZId)
|
||||
} else {
|
||||
logrus.Infof("did not find a service edge router policy")
|
||||
}
|
||||
return nil
|
||||
}
|
56
controller/zrokEdgeSdk/service.go
Normal file
56
controller/zrokEdgeSdk/service.go
Normal file
@ -0,0 +1,56 @@
|
||||
package zrokEdgeSdk
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/openziti/edge/rest_management_api_client"
|
||||
edge_service "github.com/openziti/edge/rest_management_api_client/service"
|
||||
"github.com/openziti/edge/rest_model"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"time"
|
||||
)
|
||||
|
||||
func CreateShareService(envZId, shrToken, cfgZId string, edge *rest_management_api_client.ZitiEdgeManagement) (shrZId string, err error) {
|
||||
shrZId, err = CreateService(shrToken, []string{cfgZId}, map[string]interface{}{"zrokShareToken": shrToken}, edge)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "error creating share '%v'", shrToken)
|
||||
}
|
||||
logrus.Infof("created share '%v' (with ziti id '%v') for environment '%v'", shrToken, shrZId, envZId)
|
||||
return shrZId, nil
|
||||
}
|
||||
|
||||
func CreateService(name string, cfgZIds []string, addlTags map[string]interface{}, edge *rest_management_api_client.ZitiEdgeManagement) (shrZId string, err error) {
|
||||
encryptionRequired := true
|
||||
svc := &rest_model.ServiceCreate{
|
||||
EncryptionRequired: &encryptionRequired,
|
||||
Name: &name,
|
||||
Tags: MergeTags(ZrokTags(), addlTags),
|
||||
}
|
||||
if cfgZIds != nil {
|
||||
svc.Configs = cfgZIds
|
||||
}
|
||||
req := &edge_service.CreateServiceParams{
|
||||
Service: svc,
|
||||
Context: context.Background(),
|
||||
}
|
||||
req.SetTimeout(30 * time.Second)
|
||||
resp, err := edge.Service.CreateService(req, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return resp.Payload.Data.ID, nil
|
||||
}
|
||||
|
||||
func DeleteService(envZId, shrZId string, edge *rest_management_api_client.ZitiEdgeManagement) error {
|
||||
req := &edge_service.DeleteServiceParams{
|
||||
ID: shrZId,
|
||||
Context: context.Background(),
|
||||
}
|
||||
req.SetTimeout(30 * time.Second)
|
||||
_, err := edge.Service.DeleteService(req, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logrus.Infof("deleted service '%v' for environment '%v'", shrZId, envZId)
|
||||
return nil
|
||||
}
|
119
controller/zrokEdgeSdk/sp.go
Normal file
119
controller/zrokEdgeSdk/sp.go
Normal file
@ -0,0 +1,119 @@
|
||||
package zrokEdgeSdk
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/openziti/edge/rest_management_api_client"
|
||||
"github.com/openziti/edge/rest_management_api_client/service_policy"
|
||||
"github.com/openziti/edge/rest_model"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
servicePolicyDial = 1
|
||||
servicePolicyBind = 2
|
||||
)
|
||||
|
||||
func CreateServicePolicyBind(name, shrZId, bindZId string, addlTags map[string]interface{}, edge *rest_management_api_client.ZitiEdgeManagement) error {
|
||||
semantic := rest_model.SemanticAllOf
|
||||
identityRoles := []string{"@" + bindZId}
|
||||
serviceRoles := []string{"@" + shrZId}
|
||||
spZId, err := createServicePolicy(name, semantic, identityRoles, serviceRoles, addlTags, servicePolicyBind, edge)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error creating bind service policy for service '%v' for identity '%v'", shrZId, bindZId)
|
||||
}
|
||||
logrus.Infof("created bind service policy '%v' for service '%v' for identity '%v'", spZId, shrZId, bindZId)
|
||||
return nil
|
||||
}
|
||||
|
||||
func CreateServicePolicyDial(name, shrZId string, dialZIds []string, addlTags map[string]interface{}, edge *rest_management_api_client.ZitiEdgeManagement) error {
|
||||
semantic := rest_model.SemanticAllOf
|
||||
var identityRoles []string
|
||||
for _, zId := range dialZIds {
|
||||
identityRoles = append(identityRoles, "@"+zId)
|
||||
}
|
||||
serviceRoles := []string{"@" + shrZId}
|
||||
spZId, err := createServicePolicy(name, semantic, identityRoles, serviceRoles, addlTags, servicePolicyDial, edge)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error creating dial service policy for service '%v' for identities '%v'", shrZId, dialZIds)
|
||||
}
|
||||
logrus.Infof("created dial service policy '%v' for service '%v' for identities '%v'", spZId, shrZId, dialZIds)
|
||||
return nil
|
||||
}
|
||||
|
||||
func createServicePolicy(name string, semantic rest_model.Semantic, identityRoles, serviceRoles []string, addlTags map[string]interface{}, dialBind int, edge *rest_management_api_client.ZitiEdgeManagement) (spZId string, err error) {
|
||||
var dialBindType rest_model.DialBind
|
||||
switch dialBind {
|
||||
case servicePolicyBind:
|
||||
dialBindType = rest_model.DialBindBind
|
||||
case servicePolicyDial:
|
||||
dialBindType = rest_model.DialBindDial
|
||||
default:
|
||||
return "", errors.Errorf("invalid dial bind type")
|
||||
}
|
||||
|
||||
spc := &rest_model.ServicePolicyCreate{
|
||||
IdentityRoles: identityRoles,
|
||||
Name: &name,
|
||||
PostureCheckRoles: make([]string, 0),
|
||||
Semantic: &semantic,
|
||||
ServiceRoles: serviceRoles,
|
||||
Tags: MergeTags(ZrokTags(), addlTags),
|
||||
Type: &dialBindType,
|
||||
}
|
||||
|
||||
req := &service_policy.CreateServicePolicyParams{
|
||||
Policy: spc,
|
||||
Context: context.Background(),
|
||||
}
|
||||
req.SetTimeout(30 * time.Second)
|
||||
|
||||
resp, err := edge.ServicePolicy.CreateServicePolicy(req, nil)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "error creating service policy")
|
||||
}
|
||||
|
||||
return resp.Payload.Data.ID, nil
|
||||
}
|
||||
|
||||
func DeleteServicePolicyBind(envZId, shrToken string, edge *rest_management_api_client.ZitiEdgeManagement) error {
|
||||
return DeleteServicePolicy(envZId, fmt.Sprintf("tags.zrokShareToken=\"%v\" and type=%d", shrToken, servicePolicyBind), edge)
|
||||
}
|
||||
|
||||
func DeleteServicePolicyDial(envZId, shrToken string, edge *rest_management_api_client.ZitiEdgeManagement) error {
|
||||
return DeleteServicePolicy(envZId, fmt.Sprintf("tags.zrokShareToken=\"%v\" and type=%d", shrToken, servicePolicyDial), edge)
|
||||
}
|
||||
|
||||
func DeleteServicePolicy(envZId, filter string, edge *rest_management_api_client.ZitiEdgeManagement) error {
|
||||
limit := int64(1)
|
||||
offset := int64(0)
|
||||
listReq := &service_policy.ListServicePoliciesParams{
|
||||
Filter: &filter,
|
||||
Limit: &limit,
|
||||
Offset: &offset,
|
||||
Context: context.Background(),
|
||||
}
|
||||
listReq.SetTimeout(30 * time.Second)
|
||||
listResp, err := edge.ServicePolicy.ListServicePolicies(listReq, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(listResp.Payload.Data) == 1 {
|
||||
spId := *(listResp.Payload.Data[0].ID)
|
||||
req := &service_policy.DeleteServicePolicyParams{
|
||||
ID: spId,
|
||||
Context: context.Background(),
|
||||
}
|
||||
req.SetTimeout(30 * time.Second)
|
||||
_, err := edge.ServicePolicy.DeleteServicePolicy(req, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logrus.Infof("deleted service policy '%v' for environment '%v'", spId, envZId)
|
||||
} else {
|
||||
logrus.Infof("did not find a service policy")
|
||||
}
|
||||
return nil
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user