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

Create proxy connection (or other KubeAction) #100

Open
MortenMeisler opened this issue Sep 21, 2019 · 21 comments
Open

Create proxy connection (or other KubeAction) #100

MortenMeisler opened this issue Sep 21, 2019 · 21 comments

Comments

@MortenMeisler
Copy link

MortenMeisler commented Sep 21, 2019

Sorry for keep asking these "nub" questions :) But I can't figure out how to use the KubeActions (the enum values).

Trying to accomplish something similar to this for an existing pod: kubectl port-forward jupyter-notebook-7cfb95db6b-z2frd 8888:8888 -n default

Not sure if it's working though ( #10 )

Any example is much appreciated, thank you.

@tintoy
Copy link
Owner

tintoy commented Sep 22, 2019

I don’t think we’ve actually implemented port-forward to be honest; the pieces are there but nobody’s needed it so far so there is no explicit support for it. If you look at how the Exec method is implemented in KubeClient.WebSockets you could probably create a similar extension method for port-forwarding.

I might have time to look at implementing this sometime next week but the weekend is just about over in my time zone :)

@tintoy
Copy link
Owner

tintoy commented Sep 22, 2019

The enum is just a way for us to categorise the URLs we extracted from the K8s API Swagger document (which lists the API paths and their associated models).

@MortenMeisler
Copy link
Author

Ah ok, thought I missed something. Makes sense, would be nice to have, think it could be useful to many who like me wants to "abstract" the kubectl to an internal webpage or similar.

I'll take a look at the exec method next week also.

Thanks for all the help, appreciate it :)

@MortenMeisler
Copy link
Author

MortenMeisler commented Sep 27, 2019

I've tried replicating the exec methods for websockets, but I get an 403 access denied when I try to use the portforward post command, not sure if the bearer token is passed on in the request header with this?

Here's the method (a bit hardcoded for now):

 public async static Task<WebSocket> PortForward(this IPodClientV1 podClient, string podName, string kubeNamespace = null, CancellationToken cancellation = default)
        {
            if (podClient == null)
                throw new ArgumentNullException(nameof(podClient));

            return await podClient.KubeClient.ConnectWebSocket(
                $"api/v1/namespaces/{kubeNamespace ?? podClient.KubeClient.DefaultNamespace}/pods/{podName}/portforward?ports=8888,8888",
                cancellation
            ).ConfigureAwait(false);
        }
//Initialize KubeApi client
            var Client = KubeApiClient.Create(new KubeClientOptions
            {
                ApiEndPoint = new Uri(myuri),
                AuthStrategy = KubeAuthStrategy.BearerToken,
                AccessToken = "mytoken",
                AllowInsecure = true // Don't validate server certificate
            });

            //throws: 403 forbidden
            var portforward = KubeOperations.PortForward(Client.PodsV1(), "jupyter-notebook-7cfb95db6b-788tm").GetAwaiter().GetResult();

I have no problems with other operations, like getting, listing, patching, creating.

@MortenMeisler
Copy link
Author

Also, is there any good tools to sniff the traffic when I try this? It works fine with kubectl portforward and I can see the POST cmd when i run it verbose (--v=8), but I'd like to see the request when I run this in VS. Tried wireshark, but didn't really give me anything useful.

@tintoy
Copy link
Owner

tintoy commented Sep 27, 2019 via email

@tintoy
Copy link
Owner

tintoy commented Sep 27, 2019

Does it work when you use Exec? Just trying to work out whether it’s a generic websockets issue or something specific to port-forward...

@MortenMeisler
Copy link
Author

MortenMeisler commented Sep 27, 2019

Does it work when you use Exec? Just trying to work out whether it’s a generic websockets issue or something specific to port-forward...

Yes I think so, this one went through and populated the multiplexer:

K8sMultiplexer multiplexer = Task.Run(async () => await Client.PodsV1().ExecAndConnect(
                        podName: "jupyter-notebook-7cfb95db6b-788tm",
                        kubeNamespace: "default",
                        command: "printenv",
                        stdin: true,
                        stdout: true,
                        stderr: true,
                        tty: true // Required for interactivity
                    )).GetAwaiter().GetResult();

            Console.WriteLine("Connected.");

I am targeting .NET Core 3.0 Preview 9. But seems like the other methods works fine from your library, so guessing I need something more/else for the POST call of portforward.

@MortenMeisler
Copy link
Author

I should upgrade to release ofc, I just found out now it was released some days ago. Don't think it will matter here though.

@tintoy
Copy link
Owner

tintoy commented Sep 27, 2019

Not sure if it will help, but you could try enabling logging of payloads in the KubeClientOptions... that will at least show you what KubeClient thinks it’s sending...

@tintoy
Copy link
Owner

tintoy commented Sep 27, 2019

Hmm. Looks like port-forward uses something like POST /api/v1/namespaces/{namespace}/pods/{name}/portforward, but AFAIK WebSocket requests are supposed to start with a GET. I might need to check whether port-forward over WebSockets is actually supported by the K8s API...?

@MortenMeisler
Copy link
Author

MortenMeisler commented Sep 27, 2019

Ok thanks,
I found this PR, seems like portforward is implemented over websockets: kubernetes/kubernetes#33684 but maybe implementation can reveal something..

I tried logging, the websocket method is not passing in loggerfactory, so don't think it gives much (or maybe I need to set something more up).

Code used:

var loggerFactory = LoggerFactory.Create(builder =>
            {
                builder
                .SetMinimumLevel(LogLevel.Trace)
                    .AddConsole();
            });
            ILogger logger = loggerFactory.CreateLogger<ConsoleTester_App>();
            logger.LogInformation("Example log message");
            logger.LogDebug("Example debug message");


            //Initialize KubeApi client

            var Client = KubeApiClient.Create(new KubeClientOptions
            {
                ApiEndPoint = new Uri(baseUrl),
                AuthStrategy = KubeAuthStrategy.BearerToken,
                AccessToken = "mytoken",
                AllowInsecure = true, // Don't validate server certificate
                LogPayloads = true,
                LoggerFactory = loggerFactory
            }); ;

            K8sMultiplexer multiplexer = Task.Run(async () => await Client.PodsV1().ExecAndConnect(
                        podName: "jupyter-notebook-7cfb95db6b-788tm",
                        kubeNamespace: "default",
                        command: "printenv",
                        stdin: true,
                        stdout: true,
                        stderr: true,
                        tty: true
                        // Required for interactivity
                    )).GetAwaiter().GetResult();

            logger.LogInformation("Connected.");


            //403 forbidden
            var portforward = Task.Run(async () => await KubeOperations.PortForward(Client.PodsV1(), "jupyter-notebook-7cfb95db6b-788tm"));
            var portresult = portforward.GetAwaiter().GetResult();

Log:

info: ConsoleTester.ConsoleTester_App[0]
      Example log message
dbug: ConsoleTester.ConsoleTester_App[0]
      Example debug message
trce: KubeClient.Extensions.WebSockets.K8sMultiplexer[0]
      K8sMultiplexer created with 2 input streams (indexes: [1, 2]) and 1 output streams (indexes: [0]).
trce: KubeClient.Extensions.WebSockets.K8sMultiplexer[0]
      Message-send pump started.
trce: KubeClient.Extensions.WebSockets.K8sMultiplexer[0]
      Message-receive pump started.
info: ConsoleTester.ConsoleTester_App[0]
      Connected.
trce: KubeClient.Extensions.WebSockets.K8sMultiplexer[0]
      Received 1023 bytes for stream 1 (EndOfMessage = False).
trce: KubeClient.Extensions.WebSockets.K8sMultiplexer[0]
      Received 1024 additional bytes for stream 1 (EndOfMessage = False).
trce: KubeClient.Extensions.WebSockets.K8sMultiplexer[0]
      Received 457 additional bytes for stream 1 (EndOfMessage = True).
dbug: KubeClient.Extensions.WebSockets.K8sMultiplexer[0]
      Received first half of connection-close handshake (Status = NormalClosure, Description = ).
dbug: KubeClient.Extensions.WebSockets.K8sMultiplexer[0]
      Sending second half of connection-close handshake...
dbug: KubeClient.Extensions.WebSockets.K8sMultiplexer[0]
      Sent second half of connection-close handshake.
trce: KubeClient.Extensions.WebSockets.K8sMultiplexer[0]
      Message-receive pump terminated.
Unhandled exception. System.Net.WebSockets.WebSocketException (0x80004005): WebSocket connection failure.
 ---> System.Net.WebSockets.WebSocketException (0x80004005): Connection failure (status line = 'HTTP/1.1 403 Forbidden').
   at KubeClient.Extensions.WebSockets.K8sWebSocket.ParseAndValidateConnectResponseAsync(Stream stream, K8sWebSocketOptions options, String expectedSecWebSocketAccept, CancellationToken cancellationToken)
   at KubeClient.Extensions.WebSockets.K8sWebSocket.ConnectAsync(Uri uri, K8sWebSocketOptions options, CancellationToken cancellationToken)
   at KubeClient.Extensions.WebSockets.K8sWebSocket.ConnectAsync(Uri uri, K8sWebSocketOptions options, CancellationToken cancellationToken)
   at MyApp.Application.KubeOperations.PortForward(IPodClientV1 podClient, String podName, String kubeNamespace, CancellationToken cancellation) in 
--- End of stack trace from previous location where exception was thrown ---

@tintoy
Copy link
Owner

tintoy commented Sep 27, 2019

Hmm, ok, looks like I need to add some logging there :)

(sorry, don’t have a working cluster at the moment; I’ll see if I can get Docker for Desktop installed and have a go with that)

@MortenMeisler
Copy link
Author

Np, thanks for the effort in any case :)

@tintoy
Copy link
Owner

tintoy commented Sep 28, 2019

Sure thing, sorry for the delay - I should be able to try this out tomorrow

@tintoy
Copy link
Owner

tintoy commented Sep 30, 2019

I’m still looking into this, will let you know what I find :)

@tintoy
Copy link
Owner

tintoy commented Sep 30, 2019

If you run kubectl with logging enabled and do an exec does it do a POST as well, or does it start off with a GET?

@tintoy
Copy link
Owner

tintoy commented Sep 30, 2019

And what value do you see in K8sWebSocketOptions.RequestedSubProtocols? Is it channel.k8s.io?

@tintoy
Copy link
Owner

tintoy commented Sep 30, 2019

Also, I take it if you don't do the exec call before the portforward call you still get the same result?

@tintoy
Copy link
Owner

tintoy commented Sep 30, 2019

You can also try KubeClientOptions.FromKubeConfig to ensure you're grabbing the same settings that kubectl uses.

@MortenMeisler
Copy link
Author

Alright, I will try tonight when the kids are sleeping ;)

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

No branches or pull requests

2 participants