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

feat: Add proxy support to CLI API client #12527

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions cmd/argo/commands/client/conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package client
import (
"context"
"fmt"
"net/http"
"net/url"
"os"

log "github.com/sirupsen/logrus"
Expand Down Expand Up @@ -55,6 +57,14 @@ func AddAPIClientFlagsToCmd(cmd *cobra.Command) {
}

func NewAPIClient(ctx context.Context) (context.Context, apiclient.Client, error) {
var proxy func(*http.Request) (*url.URL, error)
if overrides.ClusterInfo.ProxyURL != "" {
proxyURL, err := url.Parse(overrides.ClusterInfo.ProxyURL)
if err != nil {
return nil, nil, err
}
proxy = http.ProxyURL(proxyURL)
}
return apiclient.NewClientFromOpts(
apiclient.Opts{
ArgoServerOpts: ArgoServerOpts,
Expand All @@ -70,6 +80,7 @@ func NewAPIClient(ctx context.Context) (context.Context, apiclient.Client, error
Offline: Offline,
OfflineFiles: OfflineFiles,
Context: ctx,
Proxy: proxy,
})
}

Expand Down
5 changes: 4 additions & 1 deletion pkg/apiclient/apiclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package apiclient
import (
"context"
"fmt"
"net/http"
"net/url"

log "github.com/sirupsen/logrus"
"k8s.io/client-go/tools/clientcmd"
Expand Down Expand Up @@ -35,6 +37,7 @@ type Opts struct {
Offline bool
OfflineFiles []string
Context context.Context
Proxy func(*http.Request) (*url.URL, error)
}

func (o Opts) String() string {
Expand Down Expand Up @@ -73,7 +76,7 @@ func NewClientFromOpts(opts Opts) (context.Context, Client, error) {
if opts.AuthSupplier == nil {
return nil, nil, fmt.Errorf("AuthSupplier cannot be empty when connecting to Argo Server")
}
return newHTTP1Client(opts.ArgoServerOpts.GetURL(), opts.AuthSupplier(), opts.ArgoServerOpts.InsecureSkipVerify, opts.ArgoServerOpts.Headers, opts.ArgoServerOpts.HTTP1Client)
return newHTTP1Client(opts.ArgoServerOpts.GetURL(), opts.AuthSupplier(), opts.ArgoServerOpts.InsecureSkipVerify, opts.ArgoServerOpts.Headers, opts.ArgoServerOpts.HTTP1Client, opts.Proxy)
} else if opts.ArgoServerOpts.URL != "" {
if opts.AuthSupplier == nil {
return nil, nil, fmt.Errorf("AuthSupplier cannot be empty when connecting to Argo Server")
Expand Down
5 changes: 3 additions & 2 deletions pkg/apiclient/http1-client.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package apiclient
import (
"context"
"net/http"
"net/url"

"github.com/argoproj/argo-workflows/v3/pkg/apiclient/clusterworkflowtemplate"
cronworkflowpkg "github.com/argoproj/argo-workflows/v3/pkg/apiclient/cronworkflow"
Expand Down Expand Up @@ -41,6 +42,6 @@ func (h httpClient) NewInfoServiceClient() (infopkg.InfoServiceClient, error) {
return http1.InfoServiceClient(h), nil
}

func newHTTP1Client(baseUrl string, auth string, insecureSkipVerify bool, headers []string, customHttpClient *http.Client) (context.Context, Client, error) {
return context.Background(), httpClient(http1.NewFacade(baseUrl, auth, insecureSkipVerify, headers, customHttpClient)), nil
func newHTTP1Client(baseUrl string, auth string, insecureSkipVerify bool, headers []string, customHttpClient *http.Client, proxy func(*http.Request) (*url.URL, error)) (context.Context, Client, error) {
return context.Background(), httpClient(http1.NewFacade(baseUrl, auth, insecureSkipVerify, headers, customHttpClient, proxy)), nil
}
22 changes: 20 additions & 2 deletions pkg/apiclient/http1/facade.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,11 @@ type Facade struct {
insecureSkipVerify bool
headers []string
httpClient *http.Client
proxy func(*http.Request) (*url.URL, error)
}

func NewFacade(baseUrl, authorization string, insecureSkipVerify bool, headers []string, httpClient *http.Client) Facade {
return Facade{baseUrl, authorization, insecureSkipVerify, headers, httpClient}
func NewFacade(baseUrl, authorization string, insecureSkipVerify bool, headers []string, httpClient *http.Client, proxy func(*http.Request) (*url.URL, error)) Facade {
return Facade{baseUrl, authorization, insecureSkipVerify, headers, httpClient, proxy}
}

func (h Facade) Get(ctx context.Context, in, out interface{}, path string) error {
Expand Down Expand Up @@ -67,10 +68,15 @@ func (h Facade) EventStreamReader(ctx context.Context, in interface{}, path stri
req.Header.Set("Accept", "text/event-stream")
req.Header.Set("Authorization", h.authorization)
log.Debugf("curl -H 'Accept: text/event-stream' -H 'Authorization: ******' '%v'", u)
proxyURL, err := h.proxyFunc()(req)
if err != nil {
return nil, err
}
client := h.httpClient
if h.httpClient == nil {
client = &http.Client{
Transport: &http.Transport{
Proxy: http.ProxyURL(proxyURL),
TLSClientConfig: &tls.Config{
InsecureSkipVerify: h.insecureSkipVerify,
},
Expand Down Expand Up @@ -113,10 +119,15 @@ func (h Facade) do(ctx context.Context, in interface{}, out interface{}, method
req.Header = headers
req.Header.Set("Authorization", h.authorization)
log.Debugf("curl -X %s -H 'Authorization: ******' -d '%s' '%v'", method, string(data), u)
proxyURL, err := h.proxyFunc()(req)
if err != nil {
return err
}
client := h.httpClient
if h.httpClient == nil {
client = &http.Client{
Transport: &http.Transport{
Proxy: http.ProxyURL(proxyURL),
TLSClientConfig: &tls.Config{
InsecureSkipVerify: h.insecureSkipVerify,
},
Expand Down Expand Up @@ -180,3 +191,10 @@ func parseHeaders(headerStrings []string) (http.Header, error) {
}
return headers, nil
}

func (h *Facade) proxyFunc() func(*http.Request) (*url.URL, error) {
if h.proxy != nil {
return h.proxy
}
return http.ProxyFromEnvironment
}
34 changes: 34 additions & 0 deletions pkg/apiclient/http1/facade_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package http1

import (
"net/http"
"net/url"
"reflect"
"testing"

"github.com/stretchr/testify/assert"
Expand All @@ -14,3 +17,34 @@ func TestFacade_do(t *testing.T) {
require.NoError(t, err)
assert.Equal(t, "http://my-url/my-ns/?labels.foo=1", u.String())
}

func TestFacade_proxyFunc(t *testing.T) {
proxyFunc := func(_ *http.Request) (*url.URL, error) {
return nil, nil
}
tests := []struct {
name string
proxy func(*http.Request) (*url.URL, error)
want func(*http.Request) (*url.URL, error)
}{
{
name: "use proxy settings from environment variables",
proxy: nil,
want: http.ProxyFromEnvironment,
},
{
name: "use specific proxy",
proxy: proxyFunc,
want: proxyFunc,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
f := Facade{proxy: tt.want}
got := f.proxyFunc()
if reflect.ValueOf(got).Pointer() != reflect.ValueOf(tt.want).Pointer() {
t.Errorf("Facade.proxyURL() = %p, want %p", got, tt.want)
}
})
}
}
Loading