-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: rewrite tunnel interface, add list, reconnect underlying expose…
… tunnel (#30)
- Loading branch information
1 parent
405e55e
commit 03771f8
Showing
27 changed files
with
1,787 additions
and
1,063 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,3 +21,6 @@ replacements/ | |
|
||
# Build stuff | ||
dist/ | ||
|
||
# debugger | ||
__debug_bin |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
package main | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"io" | ||
"os" | ||
"strings" | ||
"time" | ||
|
||
apiv1 "github.com/jaredallard/localizer/api/v1" | ||
"github.com/jaredallard/localizer/internal/server" | ||
"github.com/pkg/errors" | ||
"github.com/sirupsen/logrus" | ||
"github.com/urfave/cli/v2" | ||
"google.golang.org/grpc" | ||
) | ||
|
||
func NewExposeCommand(log logrus.FieldLogger) *cli.Command { | ||
return &cli.Command{ | ||
Name: "expose", | ||
Description: "Expose ports for a given service to Kubernetes", | ||
Usage: "expose <namespace/service>", | ||
Flags: []cli.Flag{ | ||
&cli.StringSliceFlag{ | ||
Name: "map", | ||
Usage: "Map a local port to a remote port, i.e --map 80:8080 will bind what is normally :8080 to :80 locally", | ||
}, | ||
&cli.BoolFlag{ | ||
Name: "stop", | ||
Usage: "stop exposing a service", | ||
}, | ||
}, | ||
// TODO: multiple service support before this gets released | ||
Action: func(c *cli.Context) error { | ||
split := strings.Split(c.Args().First(), "/") | ||
if len(split) != 2 { | ||
return fmt.Errorf("invalid service, expected namespace/name") | ||
} | ||
|
||
serviceNamespace := split[0] | ||
serviceName := split[1] | ||
|
||
if _, err := os.Stat(server.SocketPath); os.IsNotExist(err) { | ||
return fmt.Errorf("localizer daemon not running (run localizer by itself?)") | ||
} | ||
|
||
ctx, cancel := context.WithTimeout(c.Context, 30*time.Second) | ||
defer cancel() | ||
|
||
log.Info("connecting to localizer daemon") | ||
conn, err := grpc.DialContext(ctx, "unix://"+server.SocketPath, | ||
grpc.WithBlock(), grpc.WithInsecure()) | ||
if err != nil { | ||
return errors.Wrap(err, "failed to talk to localizer daemon") | ||
} | ||
|
||
client := apiv1.NewLocalizerServiceClient(conn) | ||
|
||
var stream apiv1.LocalizerService_ExposeServiceClient | ||
if c.Bool("stop") { | ||
log.Info("sending stop expose request to daemon") | ||
stream, err = client.StopExpose(ctx, &apiv1.StopExposeRequest{ | ||
Namespace: serviceNamespace, | ||
Service: serviceName, | ||
}) | ||
} else { | ||
log.Info("sending expose request to daemon") | ||
stream, err = client.ExposeService(ctx, &apiv1.ExposeServiceRequest{ | ||
PortMap: c.StringSlice("map"), | ||
Namespace: serviceNamespace, | ||
Service: serviceName, | ||
}) | ||
} | ||
if err != nil { | ||
return err | ||
} | ||
|
||
for { | ||
res, err := stream.Recv() | ||
if err == io.EOF { | ||
return nil | ||
} else if err != nil { | ||
return err | ||
} | ||
|
||
logger := log.Info | ||
switch res.Level { | ||
case apiv1.ConsoleLevel_CONSOLE_LEVEL_INFO, apiv1.ConsoleLevel_CONSOLE_LEVEL_UNSPECIFIED: | ||
case apiv1.ConsoleLevel_CONSOLE_LEVEL_WARN: | ||
logger = log.Warn | ||
case apiv1.ConsoleLevel_CONSOLE_LEVEL_ERROR: | ||
logger = log.Error | ||
} | ||
|
||
logger(res.Message) | ||
} | ||
}, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
package main | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"os" | ||
"sort" | ||
"strings" | ||
"text/tabwriter" | ||
"time" | ||
|
||
apiv1 "github.com/jaredallard/localizer/api/v1" | ||
"github.com/jaredallard/localizer/internal/server" | ||
"github.com/pkg/errors" | ||
"github.com/sirupsen/logrus" | ||
"github.com/urfave/cli/v2" | ||
"google.golang.org/grpc" | ||
) | ||
|
||
func NewListCommand(_ logrus.FieldLogger) *cli.Command { | ||
return &cli.Command{ | ||
Name: "list", | ||
Description: "list all port-forwarded services and their status(es)", | ||
Usage: "list", | ||
Action: func(c *cli.Context) error { | ||
if _, err := os.Stat(server.SocketPath); os.IsNotExist(err) { | ||
return fmt.Errorf("localizer daemon not running (run localizer by itself?)") | ||
} | ||
|
||
ctx, cancel := context.WithTimeout(c.Context, 30*time.Second) | ||
defer cancel() | ||
|
||
conn, err := grpc.DialContext(ctx, "unix://"+server.SocketPath, | ||
grpc.WithBlock(), grpc.WithInsecure()) | ||
if err != nil { | ||
return errors.Wrap(err, "failed to talk to localizer daemon") | ||
} | ||
|
||
client := apiv1.NewLocalizerServiceClient(conn) | ||
resp, err := client.List(ctx, &apiv1.ListRequest{}) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
w := tabwriter.NewWriter(os.Stdout, 10, 0, 3, ' ', 0) | ||
defer w.Flush() | ||
|
||
fmt.Fprintf(w, "NAMESPACE\tNAME\tSTATUS\tREASON\tENDPOINT\tIP ADDRESS\tPORT(S)\t\n") | ||
|
||
// sort by namespace and then by name | ||
sort.Slice(resp.Services, func(i, j int) bool { | ||
return resp.Services[i].Namespace < resp.Services[j].Namespace | ||
}) | ||
sort.Slice(resp.Services, func(i, j int) bool { | ||
return resp.Services[i].Name < resp.Services[j].Name | ||
}) | ||
|
||
for _, s := range resp.Services { | ||
status := strings.ToUpper(s.Status[:1]) + s.Status[1:] | ||
ip := s.Ip | ||
if ip == "" { | ||
ip = "None" | ||
} | ||
|
||
fmt.Fprintf(w, | ||
"%s\t%s\t%s\t%s\t%s\t%s\t%s\n", | ||
s.Namespace, s.Name, status, s.StatusReason, s.Endpoint, ip, strings.Join(s.Ports, ","), | ||
) | ||
} | ||
|
||
return nil | ||
}, | ||
} | ||
} |
Oops, something went wrong.