Skip to content

Commit

Permalink
feat: support mapping expose ports via --map
Browse files Browse the repository at this point in the history
  • Loading branch information
jaredallard committed Sep 26, 2020
1 parent 0fee46b commit 2832756
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 57 deletions.
63 changes: 19 additions & 44 deletions cmd/localizer/localizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import (
"strings"
"syscall"

"github.com/davecgh/go-spew/spew"
"github.com/jaredallard/localizer/internal/expose"
"github.com/jaredallard/localizer/internal/kube"
"github.com/jaredallard/localizer/internal/proxier"
Expand Down Expand Up @@ -102,9 +101,7 @@ func main() { //nolint:funlen,gocyclo

// note: port 50 is chosen as least likely to collide with anything
// we may want to look into randomizing it in the future
port := 50
tls := false
return errors.Wrap(server.RunServer(ctx, &port, &tls, nil, nil), "server failed")
return errors.Wrap(server.RunServer(ctx, server.WithPort(50), server.WithLogger(log)), "server failed")
},
},
{
Expand All @@ -113,11 +110,8 @@ func main() { //nolint:funlen,gocyclo
Usage: "expose <service>",
Flags: []cli.Flag{
&cli.StringSliceFlag{
// Note: We should make this true override support
// right now this will only work for a targetPort -> another targetPort
// and not the noted localPort
Name: "map",
Usage: "Map a service's target port to another port, --map targetPortName:remotePort",
Usage: "Map a local port to a remote port, i.e --map 80:8080 will bind what is normally :8080 to :80 locally",
},
},
Action: func(c *cli.Context) error {
Expand Down Expand Up @@ -146,59 +140,40 @@ func main() { //nolint:funlen,gocyclo
return errors.Wrap(err, "failed to resolve service ports")
}

portOverrides := make(map[string]uint)
log.Debugf("map %v", c.StringSlice("map"))
for _, portOverride := range c.StringSlice("map") {
spl := strings.Split(portOverride, ":")
if len(spl) != 2 {
return fmt.Errorf("invalid port map '%s', expected 'local:remote'", portOverride)
}

local := spl[0]
rem := spl[1]

remote, err := strconv.ParseUint(rem, 10, 0)
local, err := strconv.ParseUint(spl[0], 10, 0)
if err != nil {
return errors.Wrapf(err, "failed to parse port map '%s'", portOverride)
}

portOverrides[local] = uint(remote)
}

// if we couldn't find endpoints, check if we mapped the port, if not
// then prompt the user
if !exists {
log.Info("Failed to resolve ports due to endpoints not exists, please use --map or awnser the below prompt(s)")
for _, sp := range servicePorts {
if sp.TargetPort.Type != intstr.String {
continue
}

v, err := getUserInput(fmt.Sprintf("Please enter a port to map '%s' to: ", sp.TargetPort.StrVal))
if err != nil {
return errors.Wrap(err, "failed to get user input")
}
rem, err := strconv.ParseUint(spl[1], 10, 0)
if err != nil {
return errors.Wrapf(err, "failed to parse port map '%s'", portOverride)
}

remote, err := strconv.ParseUint(v, 10, 0)
if err != nil {
return errors.Wrapf(err, "failed to parse port map '%s'", v)
// TODO: this is slow...
for i, sp := range servicePorts {
log.Debugf("checking if we need to map %v, using %d:%d", sp.TargetPort, rem, local)
if uint(servicePorts[i].TargetPort.IntVal) == uint(rem) {
servicePorts[i].MappedPort = uint(local)
}

portOverrides[strconv.Itoa(int(sp.Port))] = uint(remote)
}
}

mappedServicePorts := make([]kube.ResolvedServicePort, len(servicePorts))
for i, sp := range servicePorts {
mappedPort, ok := portOverrides[strconv.Itoa(int(sp.Port))]
if ok {
sp.Port = int32(mappedPort)
sp.TargetPort = intstr.FromInt(int(mappedPort))
// if we couldn't find endpoints, then we fall back to binding whatever the
// public port of the service is
if !exists {
for i, sp := range servicePorts {
servicePorts[i].TargetPort = intstr.FromInt(int(sp.Port))
}
mappedServicePorts[i] = sp
}

log.WithField("portoverride", "").Debug(spew.Sdump(portOverrides))

// if there's no endpoints
if !exists {
log.Debug("service has no endpoints")
Expand All @@ -209,7 +184,7 @@ func main() { //nolint:funlen,gocyclo
return err
}

p, err := e.Expose(ctx, mappedServicePorts, serviceSplit[0], serviceSplit[1])
p, err := e.Expose(ctx, servicePorts, serviceSplit[0], serviceSplit[1])
if err != nil {
return errors.Wrap(err, "failed to create reverse tunnel")
}
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,6 @@ require (
)

replace (
github.com/omrikiei/ktunnel => github.com/jaredallard/ktunnel v1.2.8-beta.0.20200926055947-16b943a26404
github.com/omrikiei/ktunnel => github.com/jaredallard/ktunnel v1.2.8-beta.0.20200926161618-3698a12ca694
k8s.io/client-go => github.com/jaredallard/client-go v0.0.0-20200919203213-e55c7f2b41ab
)
8 changes: 8 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,14 @@ github.com/jaredallard/client-go v0.0.0-20200919203213-e55c7f2b41ab h1:POcVSGYlT
github.com/jaredallard/client-go v0.0.0-20200919203213-e55c7f2b41ab/go.mod h1:SnLtmp6F+NMdYHYqnSKVWW81iA2wv5QfXXncW8aWuKc=
github.com/jaredallard/ktunnel v1.2.8-beta.0.20200926055947-16b943a26404 h1:COTI4mawAmM5OLPssFnVCIvwaN92mRubN7MJLMu/XDk=
github.com/jaredallard/ktunnel v1.2.8-beta.0.20200926055947-16b943a26404/go.mod h1:8tqZSClJ9vDe6gIx/XSrLPZ4PwZiW9X16Y2ujbm3NgA=
github.com/jaredallard/ktunnel v1.2.8-beta.0.20200926160510-2fbf336cf727 h1:/LSsu67oZN8a+D57pMalTi7mdfa4f1izzTZUSpt3fNw=
github.com/jaredallard/ktunnel v1.2.8-beta.0.20200926160510-2fbf336cf727/go.mod h1:8tqZSClJ9vDe6gIx/XSrLPZ4PwZiW9X16Y2ujbm3NgA=
github.com/jaredallard/ktunnel v1.2.8-beta.0.20200926161115-31da13c84f9f h1:P22nFDZslbjnSgE60eICyN7cfTO/QXlyeALTMmXOuuc=
github.com/jaredallard/ktunnel v1.2.8-beta.0.20200926161115-31da13c84f9f/go.mod h1:8tqZSClJ9vDe6gIx/XSrLPZ4PwZiW9X16Y2ujbm3NgA=
github.com/jaredallard/ktunnel v1.2.8-beta.0.20200926161423-12ee824c0786 h1:3MKb2EvPoviSW9jtyGH1UdpLAvb+g/M2KCrsH3qRYJc=
github.com/jaredallard/ktunnel v1.2.8-beta.0.20200926161423-12ee824c0786/go.mod h1:8tqZSClJ9vDe6gIx/XSrLPZ4PwZiW9X16Y2ujbm3NgA=
github.com/jaredallard/ktunnel v1.2.8-beta.0.20200926161618-3698a12ca694 h1:F48BX7xXpcF30lOaVRxpRE5bNvi8dT9NpLjOqj+ECto=
github.com/jaredallard/ktunnel v1.2.8-beta.0.20200926161618-3698a12ca694/go.mod h1:8tqZSClJ9vDe6gIx/XSrLPZ4PwZiW9X16Y2ujbm3NgA=
github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA=
github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
Expand Down
29 changes: 17 additions & 12 deletions internal/expose/port.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,13 @@ func (p *ServiceForward) createServerPodAndTransport(ctx context.Context) (clean
return func() {}, 0, errors.Wrap(err, "failed to create pod")
}

cleanupFn = func() {
p.c.log.Debug("cleaning up pod")
// cleanup the pod
//nolint:errcheck
p.c.k.CoreV1().Pods(p.Namespace).Delete(context.Background(), po.Name, metav1.DeleteOptions{})
}

p.c.log.Infof("created pod %s", po.ObjectMeta.Name)

p.c.log.Info("waiting for remote pod to be ready ...")
Expand All @@ -154,6 +161,7 @@ loop:
}
}
case <-ctx.Done():
cleanupFn()
return func() {}, 0, ctx.Err()
}
}
Expand All @@ -177,6 +185,7 @@ loop:
select {
case <-fw.Ready:
case <-ctx.Done():
cleanupFn()
return func() {}, 0, ctx.Err()
}

Expand All @@ -195,20 +204,15 @@ loop:
return func() {}, 0, fmt.Errorf("failed to determine the generated underlying transport port")
}

return func() {
p.c.log.Debug("cleaning up pod")
// cleanup the pod
//nolint:errcheck
p.c.k.CoreV1().Pods(p.Namespace).Delete(context.Background(), po.Name, metav1.DeleteOptions{})
}, port, nil
return cleanupFn, port, nil
}

// Start starts forwarding a service
func (p *ServiceForward) Start(ctx context.Context) error {
ports := make([]string, len(p.Ports))
for i, port := range p.Ports {
prt := int(port.TargetPort.IntVal)
ports[i] = fmt.Sprintf("%d:%d", prt, prt)
ports[i] = fmt.Sprintf("%d:%d", port.MappedPort, prt)
}

cleanupFn, localPort, err := p.createServerPodAndTransport(ctx)
Expand All @@ -217,10 +221,6 @@ func (p *ServiceForward) Start(ctx context.Context) error {
}
defer cleanupFn()

// TODO(jaredallard): We likely need reconnect logic here
host := "127.0.0.1"
tls := false

// scale down the other resources that powered this service
for _, o := range p.objects {
p.c.log.Infof("scaling %s from %d -> 0", o.GetKey(), o.Replicas)
Expand All @@ -239,7 +239,12 @@ func (p *ServiceForward) Start(ctx context.Context) error {
}()

p.c.log.Debug("creating ktunnel client")
err = client.RunClient(ctx, &host, &localPort, "tcp", &tls, nil, nil, ports)
err = client.RunClient(
ctx,
client.WithServer("127.0.0.1", localPort),
client.WithTunnels("tcp", ports...),
client.WithLogger(p.c.log),
)
if err != nil {
return errors.Wrap(err, "failed to create grpc transport")
}
Expand Down
6 changes: 6 additions & 0 deletions internal/kube/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ type ResolvedServicePort struct {
// OriginalTargetPort is set if the ServicePort
// was modified
OriginalTargetPort string

// MappedPort is the locally mapped port that this should have
// defaults to the targetPort
MappedPort uint
}

// ResolveServicePorts converts named ports into their true
Expand All @@ -105,6 +109,7 @@ func ResolveServicePorts(ctx context.Context, k kubernetes.Interface,
servicePorts[i] = ResolvedServicePort{
sp,
"",
uint(sp.Port),
}
}
return servicePorts, false, nil
Expand Down Expand Up @@ -136,6 +141,7 @@ func ResolveServicePorts(ctx context.Context, k kubernetes.Interface,
servicePorts[i] = ResolvedServicePort{
p,
original,
uint(p.TargetPort.IntVal),
}
}

Expand Down

0 comments on commit 2832756

Please sign in to comment.