Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reverse port-forwarding #2781

Open
wants to merge 26 commits into
base: main
Choose a base branch
from
Open

Conversation

gememma
Copy link
Member

@gememma gememma commented Sep 26, 2024

Closes #2609

@gememma gememma force-pushed the reverse-portfwd branch 4 times, most recently from 5544be9 to 44c83fa Compare September 27, 2024 14:44
@t4lz t4lz self-assigned this Sep 30, 2024
@t4lz
Copy link
Member

t4lz commented Sep 30, 2024

Before I even start to review it: reverse port-forwarding should obviously be called port-backwarding.

@Razz4780
Copy link
Contributor

Before I even start to review it: reverse port-forwarding should obviously be called port-backwarding.

image

@gememma
Copy link
Member Author

gememma commented Sep 30, 2024

Before I even start to review it: reverse port-forwarding should obviously be called port-backwarding.

I only narrowly decided to call the struct ReversePortForwarder instead of PortBackwarder and all my notes say port-backwarding 😔 regrets

Copy link
Member

@t4lz t4lz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not done reviewing.

mirrord-analytics = { path = "../analytics" }
mirrord-intproxy = { path = "../intproxy" }
mirrord-vpn = { path = "../vpn" }
mirrord-layer = { path = "../layer" }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this used anywhere?


/// Enable reverse port forwarding
#[arg(short = 'r', long)]
pub reverse_port_forward: bool,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This argument is redundant, let's remove it and run in reverse if a reverse mapping was passed.

pub reverse_port_forward: bool,

/// Mappings for reverse port forwarding.
/// Expected format is: '-R \[remote_port:\]local_port'.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To include multiple values, do users pass -R multiple times? Maybe you could make it clear here how to pass multiple values.
And could you please also add a short explanation on port-backwarding that includes the remote and local ports? This will show up in the help message.

@@ -397,11 +397,20 @@ pub(super) struct PortForwardArgs {
/// Otherwise, the remote is assumed to be a hostname and lookup is performed in the cluster
/// after a connection is made to the target.
#[arg(short = 'L', long)]
pub port_mappings: Vec<PortMapping>,
pub port_mappings: Option<Vec<AddrPortMapping>>,
Copy link
Member

@t4lz t4lz Sep 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Making the two port mappings optional and checking explicitly in our code that one of them exists, is not a good solution, we should let clap verify correct usage for us.
Also, this is fixable, but that verification happens after mirrord already does a bunch of stuff like connecting to the operator, creating an agent, etc. But a problem in the CLI usage should lead to an immediate error. And the error that is displayed to the user tells them it's a bug, which it shouldn't.
In fact, these two issues exist in port-forwarding already:

image

I recommend taking one of the two following solutions:

  1. Instead of making this optional we have two separate commands for port-forwarding and for port-backwarding, all the common arguments can be extracted to a third struct and then included using clap's flatten, like TargetParams is included here. They each have a required port mapping arg with the appropriate type.
  2. Leave it as one command, but require exactly one of the two mappings is passed using required_unless_present and conflicts_with (or maybe there is a "xor" function. I couldn't find it in a quick search in the docs).

Either way, I also think:

  • This argument could be positional. I think that's a good interface for a single required value. Also, it would be similar to kubectl's port-forward, which many of our users are used to, wdyt?
  • I think it should be called port_mapping, singular, because it's a single mapping that maps multiple values.

Copy link
Member Author

@gememma gememma Oct 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The original reason that mapping args are not positional and use -L is to imitate the ssh port forward command as closely as possible, with -R then being chosen for the reverse. After some internal discussion, it has been decided that the user should be able to use -L and -R arguments in the same command. In this light, I can remove the -r flag (you're right, it isn't needed) and go from there - I'll play around with the clap required-adjacent stuff to see if it can enforce the presence of at least one of -L, -R

Comment on lines +443 to +444
/// associates resolved destination ports with local ports.
mappings: HashMap<RemotePort, LocalPort>,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does "resolved" mean here?

Comment on lines +468 to +475
for mapping in &parsed_mappings {
// check destinations are unique
if mappings.contains_key(&mapping.remote) {
// two mappings shared a key thus keys were not unique
return Err(PortForwardError::ReversePortMapSetupError(mapping.remote));
}
mappings.insert(mapping.remote, mapping.local);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about making the type of the port mapping command argument a tuple type that holds a HashMap, and do this parsing and verification in the FromStr implementation for that type?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not entirely sure this is possible within clap (see this discussion) - I think this check has to be done in ReversePortForwarder

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok so we can use a Vec, but the verification that there are no duplicates should happen early (before creating an agent etc.), and the error message should not say "This is a bug". We could accept a single delimited list (80:80;8080:8080) and parse it into a hashmap with FromStr, but apparently we want to copy the ssh interface, so we will have to live with verification after parsing.

Comment on lines +470 to +474
if mappings.contains_key(&mapping.remote) {
// two mappings shared a key thus keys were not unique
return Err(PortForwardError::ReversePortMapSetupError(mapping.remote));
}
mappings.insert(mapping.remote, mapping.local);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick

Suggested change
if mappings.contains_key(&mapping.remote) {
// two mappings shared a key thus keys were not unique
return Err(PortForwardError::ReversePortMapSetupError(mapping.remote));
}
mappings.insert(mapping.remote, mapping.local);
if mappings.insert(mapping.remote, mapping.local).is_some() {
// two mappings shared a key thus keys were not unique
return Err(PortForwardError::ReversePortMapSetupError(mapping.remote));
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support reverse port-forwarding
3 participants