Skip to content

Commit

Permalink
read in the file refs into data fields
Browse files Browse the repository at this point in the history
so that they can be used on the clusters where the files are not
available.

Some other minor cosmetic changes.
  • Loading branch information
metlos committed Jan 9, 2025
1 parent d2b39a0 commit 14c564a
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 19 deletions.
56 changes: 38 additions & 18 deletions pkg/cmd/adm/register_member.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"os"
"strings"
"time"

Expand Down Expand Up @@ -36,8 +37,6 @@ const (
TokenExpirationDays = 3650
)

var invalidKubeConfigError = errors.New("invalid kubeconfig file")

// newClientFromRestConfigFunc is a function to create a new Kubernetes client using the provided
// rest configuration.
type newClientFromRestConfigFunc func(*rest.Config) (runtimeclient.Client, error)
Expand Down Expand Up @@ -106,7 +105,7 @@ func newRegisterMemberCmd(exec func(*extendedCommandContext, registerMemberArgs)
flags.MustMarkRequired(cmd, "host-kubeconfig")
cmd.Flags().StringVar(&commandArgs.memberKubeConfig, "member-kubeconfig", "", "Path to the kubeconfig file of the member cluster")
flags.MustMarkRequired(cmd, "member-kubeconfig")
cmd.Flags().Bool("insecure-skip-tls-verify", false, "Whether to ignore TLS verification errors during the connection to both host and member. If not specified, the value is inherited from the respective kubeconfig files.")
cmd.Flags().Bool("insecure-skip-tls-verify", false, "If true, the TLS verification errors are ignored during the connection to both host and member. If false, TLS verification is required to succeed. If not specified, the value is inherited from the respective kubeconfig files.")
cmd.Flags().StringVar(&commandArgs.nameSuffix, "name-suffix", defaultNameSuffix, "The suffix to append to the member name used when there are multiple members in a single cluster.")
cmd.Flags().StringVar(&commandArgs.hostNamespace, "host-ns", defaultHostNs, "The namespace of the host operator in the host cluster.")
cmd.Flags().StringVar(&commandArgs.memberNamespace, "member-ns", defaultMemberNs, "The namespace of the member operator in the member cluster.")
Expand Down Expand Up @@ -264,11 +263,11 @@ func newRestClient(kubeConfigPath string) (*rest.RESTClient, error) {
func generateKubeConfig(token, namespace string, insecureSkipTLSVerify *bool, sourceKubeConfig *clientcmdapi.Config) (*clientcmdapi.Config, error) {
sourceContext, present := sourceKubeConfig.Contexts[sourceKubeConfig.CurrentContext]
if !present {
return nil, fmt.Errorf("%w: current context not present", invalidKubeConfigError)
return nil, errors.New("invalid kubeconfig file: current context not present")
}
sourceCluster, present := sourceKubeConfig.Clusters[sourceContext.Cluster]
if !present {
return nil, fmt.Errorf("%w: cluster definition not found", invalidKubeConfigError)
return nil, errors.New("invalid kubeconfig file: cluster definition not found")
}
sourceAuth, present := sourceKubeConfig.AuthInfos[sourceContext.AuthInfo]
if !present {
Expand All @@ -277,24 +276,32 @@ func generateKubeConfig(token, namespace string, insecureSkipTLSVerify *bool, so
sourceAuth = clientcmdapi.NewAuthInfo()
}

// set the token in the kubeconfig and clear out any other potential auth method and impersonation
targetAuth := sourceAuth.DeepCopy()
// let's only set what we need in the auth. If there are any certificate files, we need to copy
// their data into the data fields, because those files are not going to be available on the target
// cluster.
targetAuth := clientcmdapi.NewAuthInfo()
targetAuth.Token = token
targetAuth.TokenFile = ""
targetAuth.Username = ""
targetAuth.Password = ""
targetAuth.Impersonate = ""
targetAuth.ImpersonateUID = ""
targetAuth.ImpersonateGroups = []string{}
targetAuth.ImpersonateUserExtra = map[string][]string{}
targetAuth.Exec = nil
targetAuth.AuthProvider = nil

targetCluster := sourceCluster.DeepCopy()
if err := loadDataInto(sourceAuth.ClientCertificate, sourceAuth.ClientCertificateData, &targetAuth.ClientCertificateData); err != nil {
return nil, fmt.Errorf("failed to read the data of the client certificate: %w", err)
}
if err := loadDataInto(sourceAuth.ClientKey, sourceAuth.ClientKeyData, &targetAuth.ClientKeyData); err != nil {
return nil, fmt.Errorf("failed to read the data of the client key: %w", err)
}

targetCluster := clientcmdapi.NewCluster()
targetCluster.Server = sourceCluster.Server
targetCluster.ProxyURL = sourceCluster.ProxyURL
// if there was an explicit value set for the insecureSkipTlsVerify, we use that instead of what's
// in the kubeconfig.
if insecureSkipTLSVerify != nil {
targetCluster.InsecureSkipTLSVerify = *insecureSkipTLSVerify
} else {
targetCluster.InsecureSkipTLSVerify = sourceCluster.InsecureSkipTLSVerify
}
if !targetCluster.InsecureSkipTLSVerify {
if err := loadDataInto(sourceCluster.CertificateAuthority, sourceCluster.CertificateAuthorityData, &targetCluster.CertificateAuthorityData); err != nil {
return nil, fmt.Errorf("failed to read the data of the certificate authority: %w", err)
}
}

return &clientcmdapi.Config{
Expand Down Expand Up @@ -562,3 +569,16 @@ func findToolchainClusterForMember(allToolchainClusters []toolchainv1alpha1.Tool
}
return nil
}

func loadDataInto(path string, source []byte, target *[]byte) error {
if path != "" && len(source) == 0 {
data, err := os.ReadFile(path)
if err != nil {
return err
}
*target = data
} else {
*target = source
}
return nil
}
35 changes: 34 additions & 1 deletion pkg/cmd/adm/register_member_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package adm
import (
"context"
"fmt"
"os"
"path/filepath"
"strings"
"testing"
Expand Down Expand Up @@ -206,7 +207,7 @@ func TestRegisterMember(t *testing.T) {
assert.Contains(t, term.Output(), "kind: SpaceProvisionerConfig")
})

t.Run("single toolchain in cluster with --lets-encrypt", func(t *testing.T) {
t.Run("single toolchain in cluster with --insecure-skip-tls-verify", func(t *testing.T) {
// given
term := NewFakeTerminalWithResponse("Y")
newClient, fakeClient := newFakeClientsFromRestConfig(t, &toolchainClusterMemberSa, &toolchainClusterHostSa)
Expand Down Expand Up @@ -609,6 +610,38 @@ func TestCreateKubeConfig(t *testing.T) {

assert.Equal(t, "ns", generatedContext.Namespace)
})

t.Run("reads referenced files in kubeconfig to appropriate data fields", func(t *testing.T) {
// given
f, err := os.CreateTemp("", "ref-test")
require.NoError(t, err)
require.NoError(t, os.WriteFile(f.Name(), []byte("data"), 0))
defer os.Remove(f.Name())

kubeConfig := HostKubeConfig()
kubeConfig.Clusters["host"].CertificateAuthority = f.Name()
kubeConfig.AuthInfos["auth"] = clientcmdapi.NewAuthInfo()
kubeConfig.AuthInfos["auth"].ClientCertificate = f.Name()
kubeConfig.AuthInfos["auth"].ClientKey = f.Name()

kubeConfig.Contexts[kubeConfig.CurrentContext].AuthInfo = "auth"

// when
config, err := generateKubeConfig("token", "ns", nil, kubeConfig)
require.NoError(t, err)

// then
context := config.Contexts[config.CurrentContext]
generatedCluster := config.Clusters[context.Cluster]
generatedAuth := config.AuthInfos[context.AuthInfo]

assert.Equal(t, []byte("data"), generatedCluster.CertificateAuthorityData)
assert.Empty(t, generatedCluster.CertificateAuthority)
assert.Equal(t, []byte("data"), generatedAuth.ClientKeyData)
assert.Empty(t, generatedAuth.ClientKey)
assert.Equal(t, []byte("data"), generatedAuth.ClientCertificateData)
assert.Empty(t, generatedAuth.ClientCertificate)
})
}

func mockCreateToolchainClusterInNamespaceWithReadyCondition(t *testing.T, fakeClient *test.FakeClient, namespace string) {
Expand Down

0 comments on commit 14c564a

Please sign in to comment.