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: manage clusters via proxy #20374

Merged
merged 9 commits into from
Oct 15, 2024
Merged
Show file tree
Hide file tree
Changes from 8 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
4 changes: 4 additions & 0 deletions assets/swagger.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions cmd/argocd/commands/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package commands

import (
"fmt"
"net/http"
"os"
"regexp"
"strings"
Expand Down Expand Up @@ -106,6 +107,11 @@ func NewClusterAddCommand(clientOpts *argocdclient.ClientOptions, pathOpts *clie
contextName := args[0]
conf, err := getRestConfig(pathOpts, contextName)
errors.CheckError(err)
if clusterOpts.ProxyUrl != "" && conf.Proxy == nil {
u, err := argoappv1.ParseProxyUrl(clusterOpts.ProxyUrl)
errors.CheckError(err)
conf.Proxy = http.ProxyURL(u)
}
pasha-codefresh marked this conversation as resolved.
Show resolved Hide resolved
clientset, err := kubernetes.NewForConfig(conf)
errors.CheckError(err)
managerBearerToken := ""
Expand Down Expand Up @@ -191,6 +197,7 @@ func NewClusterAddCommand(clientOpts *argocdclient.ClientOptions, pathOpts *clie
command.Flags().BoolVarP(&skipConfirmation, "yes", "y", false, "Skip explicit confirmation")
command.Flags().StringArrayVar(&labels, "label", nil, "Set metadata labels (e.g. --label key=value)")
command.Flags().StringArrayVar(&annotations, "annotation", nil, "Set metadata annotations (e.g. --annotation key=value)")
command.Flags().StringVar(&clusterOpts.ProxyUrl, "proxy-url", "", "use proxy to connect cluster")
cmdutil.AddClusterFlags(command, &clusterOpts)
return command
}
Expand Down Expand Up @@ -374,6 +381,7 @@ func printClusterDetails(clusters []argoappv1.Cluster) {
fmt.Printf(" oAuth authentication: %v\n", cluster.Config.BearerToken != "")
fmt.Printf(" AWS authentication: %v\n", cluster.Config.AWSAuthConfig != nil)
fmt.Printf("\nDisable compression: %v\n", cluster.Config.DisableCompression)
fmt.Printf("\nUse proxy: %v\n", cluster.Config.ProxyUrl != "")
fmt.Println()
}
}
Expand Down
9 changes: 8 additions & 1 deletion cmd/util/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,13 @@ func NewCluster(name string, namespaces []string, clusterResources bool, conf *r
Labels: labels,
Annotations: annotations,
}

// it's a tradeoff to get proxy url from rest config
// more detail: https://github.com/kubernetes/kubernetes/pull/81443
if conf.Proxy != nil {
if url, err := conf.Proxy(nil); err == nil {
clst.Config.ProxyUrl = url.String()
}
}
// Bearer token will preferentially be used for auth if present,
// Even in presence of key/cert credentials
// So set bearer token only if the key/cert data is absent
Expand Down Expand Up @@ -160,6 +166,7 @@ type ClusterOptions struct {
ExecProviderInstallHint string
ClusterEndpoint string
DisableCompression bool
ProxyUrl string
}

// InClusterEndpoint returns true if ArgoCD should reference the in-cluster
Expand Down
2 changes: 2 additions & 0 deletions docs/operator-manual/declarative-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,8 @@ execProviderConfig:
}
apiVersion: string
installHint: string
# Proxy URL for the kubernetes client to use when connecting to the cluster api server
proxyUrl: string
# Transport layer security configuration settings
tlsClientConfig:
# Base64 encoded PEM-encoded bytes (typically read from a client certificate file).
Expand Down
1 change: 1 addition & 0 deletions docs/user-guide/commands/argocd_cluster_add.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1,475 changes: 758 additions & 717 deletions pkg/apis/application/v1alpha1/generated.pb.go

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions pkg/apis/application/v1alpha1/generated.proto

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 28 additions & 0 deletions pkg/apis/application/v1alpha1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"math"
"net"
"net/http"
"net/url"
"os"
"path/filepath"
"reflect"
Expand Down Expand Up @@ -2042,6 +2043,9 @@ type ClusterConfig struct {

// DisableCompression bypasses automatic GZip compression requests to the server.
DisableCompression bool `json:"disableCompression,omitempty" protobuf:"bytes,7,opt,name=disableCompression"`

// ProxyURL is the URL to the proxy to be used for all requests send to the server
ProxyUrl string `json:"proxyUrl,omitempty" protobuf:"bytes,8,opt,name=proxyUrl"`
}

// TLSClientConfig contains settings to enable transport layer security
Expand Down Expand Up @@ -3105,6 +3109,9 @@ func SetK8SConfigDefaults(config *rest.Config) error {
DisableCompression: config.DisableCompression,
IdleConnTimeout: K8sTCPIdleConnTimeout,
})
if config.Proxy != nil {
transport.Proxy = config.Proxy
}
tr, err := rest.HTTPWrappersForConfig(config, transport)
if err != nil {
return err
Expand All @@ -3128,6 +3135,20 @@ func SetK8SConfigDefaults(config *rest.Config) error {
return nil
}

// ParseProxyUrl returns a parsed url and verifies that schema is correct
func ParseProxyUrl(proxyUrl string) (*url.URL, error) {
u, err := url.Parse(proxyUrl)
if err != nil {
return nil, err
}
switch u.Scheme {
case "http", "https", "socks5":
default:
return nil, fmt.Errorf("Failed to parse proxy url, unsupported scheme %q, must be http, https, or socks5", u.Scheme)
}
return u, nil
}

// RawRestConfig returns a go-client REST config from cluster that might be serialized into the file using kube.WriteKubeConfig method.
func (c *Cluster) RawRestConfig() (*rest.Config, error) {
var config *rest.Config
Expand Down Expand Up @@ -3215,6 +3236,13 @@ func (c *Cluster) RawRestConfig() (*rest.Config, error) {
if err != nil {
return nil, fmt.Errorf("Unable to create K8s REST config: %w", err)
}
if c.Config.ProxyUrl != "" {
u, err := ParseProxyUrl(c.Config.ProxyUrl)
if err != nil {
return nil, fmt.Errorf("Unable to create K8s REST config, can`t parse proxy url: %w", err)
}
config.Proxy = http.ProxyURL(u)
}
config.DisableCompression = c.Config.DisableCompression
config.Timeout = K8sServerSideTimeout
config.QPS = K8sClientConfigQPS
Expand Down
32 changes: 32 additions & 0 deletions pkg/apis/application/v1alpha1/types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4265,3 +4265,35 @@ func TestAppProject_ValidateDestinationServiceAccount(t *testing.T) {
}
}
}

func TestCluster_ParseProxyUrl(t *testing.T) {
testData := []struct {
url string
expectedErrMsg string
}{
{
url: "https://192.168.99.100:8443",
expectedErrMsg: "",
},
{
url: "test://!abc",
expectedErrMsg: "Failed to parse proxy url, unsupported scheme \"test\", must be http, https, or socks5",
},
{
url: "http://192.168.99.100:8443",
expectedErrMsg: "",
},
{
url: "socks5://192.168.99.100:8443",
expectedErrMsg: "",
},
}
for _, data := range testData {
_, err := ParseProxyUrl(data.url)
if data.expectedErrMsg == "" {
require.NoError(t, err)
} else {
require.ErrorContains(t, err, data.expectedErrMsg)
}
}
}