Skip to content

Commit

Permalink
feat: Use kubeconfig format for external clusters (#28)
Browse files Browse the repository at this point in the history
Change-Id: I8c8cb432e026b47e0099fc1b990982035efccf3d
  • Loading branch information
zmotso authored and SergK committed Mar 7, 2024
1 parent bf1c09b commit 68e4a15
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 181 deletions.
131 changes: 0 additions & 131 deletions pkg/multiclusterclient/cluster.go

This file was deleted.

60 changes: 24 additions & 36 deletions pkg/multiclusterclient/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,16 @@ package multiclusterclient

import (
"context"
"encoding/json"
"fmt"
"strings"

corev1 "k8s.io/api/core/v1"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"sigs.k8s.io/controller-runtime/pkg/client"

cdPipeApi "github.com/epam/edp-cd-pipeline-operator/v2/api/v1"
)

const (
// nolint:gosec // it is not hardcoded credentials.
argocdClusterSecretLabel = "argocd.argoproj.io/secret-type"
argocdClusterSecretLabelVal = "cluster"
)

type ClientProvider struct {
internalClusterClient client.Client
}
Expand All @@ -37,13 +31,11 @@ func (c *ClientProvider) GetClusterClient(ctx context.Context, secretNamespace,
return nil, err
}

cluster, err := secretToCluster(secret)
restConfig, err := secretToRestConfig(secret)
if err != nil {
return nil, err
}

restConfig := cluster.RestConfig()

if options.Scheme == nil {
options.Scheme = c.internalClusterClient.Scheme()
}
Expand All @@ -57,39 +49,35 @@ func (c *ClientProvider) GetClusterClient(ctx context.Context, secretNamespace,
}

func (c *ClientProvider) getClusterSecret(ctx context.Context, clusterName, secretNamespace string) (*corev1.Secret, error) {
secretList := &corev1.SecretList{}
if err := c.internalClusterClient.List(
secret := &corev1.Secret{}
if err := c.internalClusterClient.Get(
ctx,
secretList,
client.InNamespace(secretNamespace),
client.MatchingLabels(map[string]string{argocdClusterSecretLabel: argocdClusterSecretLabelVal}),
client.ObjectKey{
Namespace: secretNamespace,
Name: clusterName,
},
secret,
); err != nil {
return nil, fmt.Errorf("failed to get cluster secret %s: %w", clusterName, err)
}

for i := 0; i < len(secretList.Items); i++ {
if string(secretList.Items[i].Data["name"]) == clusterName {
return &secretList.Items[i], nil
}
return nil, fmt.Errorf("failed to get cluster secret: %w", err)
}

return nil, fmt.Errorf("secret for %s cluster not found", clusterName)
return secret, nil
}

func secretToCluster(s *corev1.Secret) (*Cluster, error) {
var config ClusterConfig
if len(s.Data["config"]) > 0 {
err := json.Unmarshal(s.Data["config"], &config)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal cluster config: %w", err)
}
const k8sClientConfigQPS = 50

func secretToRestConfig(s *corev1.Secret) (*rest.Config, error) {
if _, ok := s.Data["config"]; !ok {
return nil, fmt.Errorf("no config data in the secret %s", s.Name)
}

cluster := Cluster{
Server: strings.TrimRight(string(s.Data["server"]), "/"),
Name: string(s.Data["name"]),
Config: config,
config, err := clientcmd.RESTConfigFromKubeConfig(s.Data["config"])
if err != nil {
return nil, fmt.Errorf("failed to create rest config from cluster secret: %w", err)
}

return &cluster, nil
config.QPS = k8sClientConfigQPS
config.Burst = int(config.QPS * 2)

return config, nil
}
77 changes: 63 additions & 14 deletions pkg/multiclusterclient/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,26 +27,54 @@ func TestClientProvider_GetClusterClient(t *testing.T) {
wantErr require.ErrorAssertionFunc
}{
{
name: "should return internal cluster client",
name: "should return external cluster client",
clusterName: "external-cluster",
internalClusterClient: func(t *testing.T) client.Client {
s := runtime.NewScheme()
require.NoError(t, cdPipeApi.AddToScheme(s))
require.NoError(t, corev1.AddToScheme(s))

return fake.NewClientBuilder().
WithScheme(s).
WithObjects(
&corev1.Secret{
ObjectMeta: metaV1.ObjectMeta{
Name: "secret",
Name: "external-cluster",
Namespace: "default",
Labels: map[string]string{argocdClusterSecretLabel: argocdClusterSecretLabelVal},
},
Data: map[string][]byte{
"config": []byte(`{"bearerToken": "token"}`),
"name": []byte("external-cluster"),
"server": []byte("https://external-cluster"),
"config": []byte(`{
"apiVersion": "v1",
"kind": "Config",
"current-context": "default-context",
"preferences": {},
"clusters": [
{
"cluster": {
"server": "https://test-cluster",
"certificate-authority-data": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNVVENDQWZ1Z0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRUUZBREJYTVFzd0NRWURWUVFHRXdKRFRqRUwKTUFrR0ExVUVDQk1DVUU0eEN6QUpCZ05WQkFjVEFrTk9NUXN3Q1FZRFZRUUtFd0pQVGpFTE1Ba0dBMVVFQ3hNQwpWVTR4RkRBU0JnTlZCQU1UQzBobGNtOXVaeUJaWVc1bk1CNFhEVEExTURjeE5USXhNVGswTjFvWERUQTFNRGd4Ck5ESXhNVGswTjFvd1Z6RUxNQWtHQTFVRUJoTUNRMDR4Q3pBSkJnTlZCQWdUQWxCT01Rc3dDUVlEVlFRSEV3SkQKVGpFTE1Ba0dBMVVFQ2hNQ1QwNHhDekFKQmdOVkJBc1RBbFZPTVJRd0VnWURWUVFERXd0SVpYSnZibWNnV1dGdQpaekJjTUEwR0NTcUdTSWIzRFFFQkFRVUFBMHNBTUVnQ1FRQ3A1aG5HN29nQmh0bHlucE9TMjFjQmV3S0UvQjdqClYxNHFleXNsbnIyNnhaVXNTVmtvMzZabmhpYU8vemJNT29SY0tLOXZFY2dNdGNMRnVRVFdEbDNSQWdNQkFBR2oKZ2JFd2dhNHdIUVlEVlIwT0JCWUVGRlhJNzBrclhlUUR4WmdiYUNRb1I0alVEbmNFTUg4R0ExVWRJd1I0TUhhQQpGRlhJNzBrclhlUUR4WmdiYUNRb1I0alVEbmNFb1Z1a1dUQlhNUXN3Q1FZRFZRUUdFd0pEVGpFTE1Ba0dBMVVFCkNCTUNVRTR4Q3pBSkJnTlZCQWNUQWtOT01Rc3dDUVlEVlFRS0V3SlBUakVMTUFrR0ExVUVDeE1DVlU0eEZEQVMKQmdOVkJBTVRDMGhsY205dVp5QlpZVzVuZ2dFQU1Bd0dBMVVkRXdRRk1BTUJBZjh3RFFZSktvWklodmNOQVFFRQpCUUFEUVFBL3VnekJyampLOWpjV25EVmZHSGxrM2ljTlJxMG9WN1JpMzJ6LytIUVg2N2FSZmdadTdLV2RJK0p1CldtN0RDZnJQTkdWd0ZXVVFPbXNQdWU5clpCZ08KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo="
},
"name": "default-cluster"
}
],
"contexts": [
{
"context": {
"cluster": "default-cluster",
"user": "default-user"
},
"name": "default-context"
}
],
"users": [
{
"user": {
"token": "token-123"
},
"name": "default-user"
}
]
}`,
),
},
},
).
Expand All @@ -60,21 +88,18 @@ func TestClientProvider_GetClusterClient(t *testing.T) {
clusterName: "external-cluster",
internalClusterClient: func(t *testing.T) client.Client {
s := runtime.NewScheme()
require.NoError(t, cdPipeApi.AddToScheme(s))
require.NoError(t, corev1.AddToScheme(s))

return fake.NewClientBuilder().
WithScheme(s).
WithObjects(
&corev1.Secret{
ObjectMeta: metaV1.ObjectMeta{
Name: "secret",
Name: "external-cluster",
Namespace: "default",
Labels: map[string]string{argocdClusterSecretLabel: argocdClusterSecretLabelVal},
},
Data: map[string][]byte{
"config": []byte(`not json data`),
"name": []byte("external-cluster"),
},
},
).
Expand All @@ -83,15 +108,40 @@ func TestClientProvider_GetClusterClient(t *testing.T) {
want: require.Nil,
wantErr: func(t require.TestingT, err error, i ...interface{}) {
require.Error(t, err)
require.Contains(t, err.Error(), "failed to unmarshal cluster config")
require.Contains(t, err.Error(), "failed to create rest config from cluster secret")
},
},
{
name: "secret does not contain config data",
clusterName: "external-cluster",
internalClusterClient: func(t *testing.T) client.Client {
s := runtime.NewScheme()
require.NoError(t, corev1.AddToScheme(s))

return fake.NewClientBuilder().
WithScheme(s).
WithObjects(
&corev1.Secret{
ObjectMeta: metaV1.ObjectMeta{
Name: "external-cluster",
Namespace: "default",
},
Data: map[string][]byte{},
},
).
Build()
},
want: require.Nil,
wantErr: func(t require.TestingT, err error, i ...interface{}) {
require.Error(t, err)
require.Contains(t, err.Error(), "no config data in the secret")
},
},
{
name: "secret not found",
clusterName: "external-cluster",
internalClusterClient: func(t *testing.T) client.Client {
s := runtime.NewScheme()
require.NoError(t, cdPipeApi.AddToScheme(s))
require.NoError(t, corev1.AddToScheme(s))

return fake.NewClientBuilder().
Expand All @@ -110,7 +160,6 @@ func TestClientProvider_GetClusterClient(t *testing.T) {
clusterName: cdPipeApi.InCluster,
internalClusterClient: func(t *testing.T) client.Client {
s := runtime.NewScheme()
require.NoError(t, cdPipeApi.AddToScheme(s))
require.NoError(t, corev1.AddToScheme(s))

return fake.NewClientBuilder().
Expand Down

0 comments on commit 68e4a15

Please sign in to comment.