feat(internal/cmd): Add 'instance tunnel' command#248
feat(internal/cmd): Add 'instance tunnel' command#248craciunoiuc wants to merge 2 commits intostagingfrom
Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds a new unikraft instance tunnel subcommand intended to forward a local port to a non-public instance via an intermediate TLS tunnel service, enabling testing scenarios that require internal VM traffic.
Changes:
- Adds
InstancesTunnelCmdto the instances command group. - Implements target parsing, instance/IP resolution, tunnel-proxy instance creation, and local-to-remote relaying over TLS.
- Adds control-channel heartbeats to keep the tunnel service connection alive.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
49d2539 to
8ae136d
Compare
| // exposedProxyPorts holds the port exposed by the tunnel service per target. | ||
| exposedProxyPorts []uint16 | ||
| // portIterator tracks the next port offset when a single proxy port is given. | ||
| portIterator uint16 |
There was a problem hiding this comment.
👀 why are these all on this struct?
The only things that should be here are the args that can be read by kong.
There was a problem hiding this comment.
equivalent of passing them by arg everywhere
If I move to a separate package I can keep them there.
There was a problem hiding this comment.
IMO, these kind of act like global variables. It's unclear where they get created / passed through. Passing them through as args would be better if we can.
If they grow to be very long, we can group them into structs.
There was a problem hiding this comment.
sure, if i'm moving this to a separate package it will need them as arguments anyway
There was a problem hiding this comment.
Ok, I simplified it as much as I could, let's see what Copilot says.
I think the exposed interface of the library is quite simple now:
New <- you create the instance
RunProxy <- start the proxying instance
TerminateProxy <- stop the proxying instance
StartRelay <- block and send messages
8ae136d to
5e8f3e5
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated 8 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
I need to fix these + do one more pass so no rush |
5e8f3e5 to
b8e8437
Compare
|
Should be as decent as it gets, definitely an upgrade compared to kraftkit |
b8e8437 to
a5b1451
Compare
|
Have to rebase the code and to generate tests on Monday. |
dcaf1a6 to
c7622e6
Compare
jedevc
left a comment
There was a problem hiding this comment.
Needs a rebase, and to get the tests passing 🎉
| cleanupCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second) | ||
| defer cancel() | ||
| env.unikraftIgnoreErr(cleanupCtx, "instance", "delete", instanceName) | ||
| }) |
There was a problem hiding this comment.
We should use the same test style as in the rest of the tests, so we get a resulting golden file!
For managing a tunnel in the background - we should probably add some sort of way to run it in the background using the golden setup.
There was a problem hiding this comment.
Hmm, I don't have much context on that but I can try
| } | ||
|
|
||
| type InstancesTunnelCmd struct { | ||
| Targets []string `arg:"" name:"target" min:"1" help:"Forwarding target(s) in the form [LOCAL_PORT:]<INSTANCE>:DEST_PORT[/TYPE]."` |
There was a problem hiding this comment.
Use placeholder to show the form. See other examples of how this is done.
|
|
||
| func (cmd *InstancesTunnelCmd) Run(ctx context.Context, stdio config.Stdio) error { | ||
| if len(cmd.TunnelProxyPorts) == 0 { | ||
| cmd.TunnelProxyPorts = []string{"4444"} |
There was a problem hiding this comment.
What are you referring to? you mean for the images? you can just set a different image
There was a problem hiding this comment.
I mean in the kong command struct.
There was a problem hiding this comment.
Ah sorry, probably some old kraftkit copy paste
| for i, r := range resolved { | ||
| inst := r.(Instance) | ||
| if metro != "" && inst.key.Metro != "" && inst.key.Metro != metro { | ||
| return nil, "", fmt.Errorf("forwarding targets must be in the same metro: found %q and %q", metro, inst.key.Metro) |
There was a problem hiding this comment.
🤔 we could support instances across multiple metros. We would just need to create multiple tunnels. This is supported for the logs cmd, so makes sense to support it here.
Instead of resolving all the metros, we'd do a call more like CollectRefs, and use that to resolve all the ips, and metros, and then just do multiple tunnel creations!
There was a problem hiding this comment.
Sounds crazy, but doable, the command has support for multiple tunnel connections
| // NOTE(craciunoiuc): We do this here to have access to the Instances API | ||
| func (cmd *InstancesTunnelCmd) tunnelLookupInstances(ctx context.Context, t *tunnel.Tunnel) ([]string, string, error) { |
There was a problem hiding this comment.
This function could very easily be added to the tunnel package actually.
Yes, we'd have to do the GetInstances request directly there, but then it's a reusable package, for anyone to use. Also, we already are doing CreateInstance requests in there, so.
There was a problem hiding this comment.
Sure, was conflicted about that
| func (r *tunnelRelay) listenLocal(ctx context.Context) (net.Listener, error) { | ||
| var lc net.ListenConfig | ||
| return lc.Listen(ctx, r.ctype+"4", r.lAddr) | ||
| } |
| defer rc.Close() | ||
| go func() { <-ctx.Done(); rc.Close() }() |
There was a problem hiding this comment.
😭
I though I caught all the places where this happened
| parts := strings.SplitN(portsArg, "/", 3) | ||
| if len(parts) == 3 { | ||
| rest = parts[0] + "/" + parts[1] | ||
| ctype = parts[2] | ||
| } else if len(parts) == 2 { |
There was a problem hiding this comment.
👀 what? What is this case for? What if there are 4 /s here?
We could simplify this by just splitting at the last /.
There was a problem hiding this comment.
I believe the format is now possibly metro/instance/connectionType
Comment is outdated needs an update
| if parseErr != nil { | ||
| return "", 0, 0, "", fmt.Errorf("%q is not a valid port number", segments[1]) | ||
| } | ||
| return segments[0], uint16(rport64), uint16(rport64), ctype, nil |
There was a problem hiding this comment.
If no local port - shouldn't we select it to a random available port? (by setting it to 0)
There was a problem hiding this comment.
I think it sets it to the default value?
There was a problem hiding this comment.
It seems to be just using rport64 for both?
|
I'll rebase and fix afterwards |
Signed-off-by: Cezar Craciunoiu <cezar@unikraft.io>
Signed-off-by: Cezar Craciunoiu <cezar@unikraft.io>
c7622e6 to
41b74aa
Compare
Nodes need this option
--net-internal-traffic=vmin order to test it.The Tunlr also needs updating to remove the check for inter-vm comms.Done.
TODO: Update tests when platform is stable.
Blocked on: https://github.com/unikraft-cloud/platform/pull/705
Closes: TOOL-714