Add kubeconfig subcommand (#87)

* Add kubeconfig subcommand

Signed-off-by: galal-hussein <hussein.galal.ahmed.11@gmail.com>

* Add kubeconfig subcommand

Signed-off-by: galal-hussein <hussein.galal.ahmed.11@gmail.com>

* Add kubeconfig subcommand

Signed-off-by: galal-hussein <hussein.galal.ahmed.11@gmail.com>

* Add kubeconfig subcommand

Signed-off-by: galal-hussein <hussein.galal.ahmed.11@gmail.com>

---------

Signed-off-by: galal-hussein <hussein.galal.ahmed.11@gmail.com>
This commit is contained in:
Hussein Galal
2024-01-11 00:57:46 +02:00
committed by GitHub
parent 4b2308e709
commit 5e141fe98e
11 changed files with 392 additions and 213 deletions
+3 -3
View File
@@ -5,7 +5,7 @@ import (
"github.com/urfave/cli"
)
var clusterSubcommands = []cli.Command{
var subcommands = []cli.Command{
{
Name: "create",
Usage: "Create new cluster",
@@ -16,10 +16,10 @@ var clusterSubcommands = []cli.Command{
},
}
func NewClusterCommand() cli.Command {
func NewCommand() cli.Command {
return cli.Command{
Name: "cluster",
Usage: "cluster command",
Subcommands: clusterSubcommands,
Subcommands: subcommands,
}
}
+9 -87
View File
@@ -3,7 +3,6 @@ package cluster
import (
"context"
"errors"
"fmt"
"net/url"
"os"
"path/filepath"
@@ -14,19 +13,17 @@ import (
"github.com/rancher/k3k/pkg/apis/k3k.io/v1alpha1"
"github.com/rancher/k3k/pkg/controller/cluster"
"github.com/rancher/k3k/pkg/controller/cluster/server"
"github.com/rancher/k3k/pkg/controller/kubeconfig"
"github.com/rancher/k3k/pkg/controller/util"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
v1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/apiserver/pkg/authentication/user"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
"k8s.io/client-go/util/retry"
"sigs.k8s.io/controller-runtime/pkg/client"
)
@@ -174,11 +171,16 @@ func createCluster(clx *cli.Context) error {
}
logrus.Infof("Extracting Kubeconfig for [%s] cluster", name)
cfg := &kubeconfig.KubeConfig{
CN: util.AdminCommonName,
ORG: []string{user.SystemPrivilegedGroup},
ExpiryDate: 0,
}
logrus.Infof("waiting for cluster to be available..")
var kubeconfig []byte
if err := retry.OnError(backoff, apierrors.IsNotFound, func() error {
kubeconfig, err = extractKubeconfig(ctx, ctrlClient, cluster, host[0])
kubeconfig, err = cfg.Extract(ctx, ctrlClient, cluster, host[0])
if err != nil {
logrus.Infof("waiting for cluster to be available: %v", err)
return err
}
return nil
@@ -250,83 +252,3 @@ func newCluster(name, token string, servers, agents int32, clusterCIDR, serviceC
},
}
}
func extractKubeconfig(ctx context.Context, client client.Client, cluster *v1alpha1.Cluster, serverIP string) ([]byte, error) {
nn := types.NamespacedName{
Name: cluster.Name + "-kubeconfig",
Namespace: util.ClusterNamespace(cluster),
}
var kubeSecret v1.Secret
if err := client.Get(ctx, nn, &kubeSecret); err != nil {
return nil, err
}
kubeconfig := kubeSecret.Data["kubeconfig.yaml"]
if kubeconfig == nil {
return nil, errors.New("empty kubeconfig")
}
nn = types.NamespacedName{
Name: "k3k-server-service",
Namespace: util.ClusterNamespace(cluster),
}
var k3kService v1.Service
if err := client.Get(ctx, nn, &k3kService); err != nil {
return nil, err
}
if k3kService.Spec.Type == v1.ServiceTypeNodePort {
nodePort := k3kService.Spec.Ports[0].NodePort
restConfig, err := clientcmd.RESTConfigFromKubeConfig(kubeconfig)
if err != nil {
return nil, err
}
hostURL := fmt.Sprintf("https://%s:%d", serverIP, nodePort)
restConfig.Host = hostURL
clientConfig := generateKubeconfigFromRest(restConfig)
b, err := clientcmd.Write(clientConfig)
if err != nil {
return nil, err
}
kubeconfig = b
}
return kubeconfig, nil
}
func generateKubeconfigFromRest(config *rest.Config) clientcmdapi.Config {
clusters := make(map[string]*clientcmdapi.Cluster)
clusters["default-cluster"] = &clientcmdapi.Cluster{
Server: config.Host,
CertificateAuthorityData: config.CAData,
}
contexts := make(map[string]*clientcmdapi.Context)
contexts["default-context"] = &clientcmdapi.Context{
Cluster: "default-cluster",
Namespace: "default",
AuthInfo: "default",
}
authinfos := make(map[string]*clientcmdapi.AuthInfo)
authinfos["default"] = &clientcmdapi.AuthInfo{
ClientCertificateData: config.CertData,
ClientKeyData: config.KeyData,
}
clientConfig := clientcmdapi.Config{
Kind: "Config",
APIVersion: "v1",
Clusters: clusters,
Contexts: contexts,
CurrentContext: "default-context",
AuthInfos: authinfos,
}
return clientConfig
}
+169
View File
@@ -0,0 +1,169 @@
package kubeconfig
import (
"context"
"net/url"
"os"
"path/filepath"
"strings"
"time"
"github.com/rancher/k3k/cli/cmds"
"github.com/rancher/k3k/pkg/apis/k3k.io/v1alpha1"
"github.com/rancher/k3k/pkg/controller/kubeconfig"
"github.com/rancher/k3k/pkg/controller/util"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/apiserver/pkg/authentication/user"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/util/retry"
"sigs.k8s.io/controller-runtime/pkg/client"
)
func init() {
_ = clientgoscheme.AddToScheme(Scheme)
_ = v1alpha1.AddToScheme(Scheme)
}
var (
Scheme = runtime.NewScheme()
name string
cn string
org cli.StringSlice
altNames cli.StringSlice
expirationDays int64
configName string
backoff = wait.Backoff{
Steps: 5,
Duration: 20 * time.Second,
Factor: 2,
Jitter: 0.1,
}
generateKubeconfigFlags = []cli.Flag{
cli.StringFlag{
Name: "name",
Usage: "cluster name",
Destination: &name,
},
cli.StringFlag{
Name: "config-name",
Usage: "the name of the generated kubeconfig file",
Destination: &configName,
},
cli.StringFlag{
Name: "cn",
Usage: "Common name (CN) of the generated certificates for the kubeconfig",
Destination: &cn,
Value: util.AdminCommonName,
},
cli.StringSliceFlag{
Name: "org",
Usage: "Organization name (ORG) of the generated certificates for the kubeconfig",
Value: &org,
},
cli.StringSliceFlag{
Name: "altNames",
Usage: "altNames of the generated certificates for the kubeconfig",
Value: &altNames,
},
cli.Int64Flag{
Name: "expiration-days",
Usage: "Expiration date of the certificates used for the kubeconfig",
Destination: &expirationDays,
Value: 356,
},
}
)
var subcommands = []cli.Command{
{
Name: "generate",
Usage: "Generate kubeconfig for clusters",
SkipFlagParsing: false,
SkipArgReorder: true,
Action: generate,
Flags: append(cmds.CommonFlags, generateKubeconfigFlags...),
},
}
func NewCommand() cli.Command {
return cli.Command{
Name: "kubeconfig",
Usage: "Manage kubeconfig for clusters",
Subcommands: subcommands,
}
}
func generate(clx *cli.Context) error {
var cluster v1alpha1.Cluster
ctx := context.Background()
restConfig, err := clientcmd.BuildConfigFromFlags("", cmds.Kubeconfig)
if err != nil {
return err
}
ctrlClient, err := client.New(restConfig, client.Options{
Scheme: Scheme,
})
if err != nil {
return err
}
clusterKey := types.NamespacedName{
Name: name,
}
if err := ctrlClient.Get(ctx, clusterKey, &cluster); err != nil {
return err
}
url, err := url.Parse(restConfig.Host)
if err != nil {
return err
}
host := strings.Split(url.Host, ":")
certAltNames := kubeconfig.AddSANs(altNames)
if org == nil {
org = cli.StringSlice{user.SystemPrivilegedGroup}
}
cfg := kubeconfig.KubeConfig{
CN: cn,
ORG: org,
ExpiryDate: time.Hour * 24 * time.Duration(expirationDays),
AltNames: certAltNames,
}
logrus.Infof("waiting for cluster to be available..")
var kubeconfig []byte
if err := retry.OnError(backoff, apierrors.IsNotFound, func() error {
kubeconfig, err = cfg.Extract(ctx, ctrlClient, &cluster, host[0])
if err != nil {
return err
}
return nil
}); err != nil {
return err
}
pwd, err := os.Getwd()
if err != nil {
return err
}
if configName == "" {
configName = cluster.Name + "-kubeconfig.yaml"
}
logrus.Infof(`You can start using the cluster with:
export KUBECONFIG=%s
kubectl cluster-info
`, filepath.Join(pwd, configName))
return os.WriteFile(configName, kubeconfig, 0644)
}
+3 -1
View File
@@ -5,6 +5,7 @@ import (
"github.com/rancher/k3k/cli/cmds"
"github.com/rancher/k3k/cli/cmds/cluster"
"github.com/rancher/k3k/cli/cmds/kubeconfig"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
@@ -18,7 +19,8 @@ const (
func main() {
app := cmds.NewApp()
app.Commands = []cli.Command{
cluster.NewClusterCommand(),
cluster.NewCommand(),
kubeconfig.NewCommand(),
}
app.Version = version + " (" + gitCommit + ")"
+8 -6
View File
@@ -15,6 +15,8 @@ import (
"github.com/rancher/k3k/pkg/controller/cluster/agent"
"github.com/rancher/k3k/pkg/controller/cluster/config"
"github.com/rancher/k3k/pkg/controller/cluster/server"
"github.com/rancher/k3k/pkg/controller/cluster/server/bootstrap"
"github.com/rancher/k3k/pkg/controller/kubeconfig"
"github.com/rancher/k3k/pkg/controller/util"
"github.com/sirupsen/logrus"
"go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
@@ -244,12 +246,12 @@ func (c *ClusterReconciler) createCluster(ctx context.Context, cluster *v1alpha1
}
}
kubeconfigSecret, err := s.GenerateNewKubeConfig(ctx, serviceIP)
bootstrapSecret, err := bootstrap.Generate(ctx, cluster, serviceIP)
if err != nil {
return util.LogAndReturnErr("failed to generate new kubeconfig", err)
}
if err := c.Client.Create(ctx, kubeconfigSecret); err != nil {
if err := c.Client.Create(ctx, bootstrapSecret); err != nil {
if !apierrors.IsAlreadyExists(err) {
return util.LogAndReturnErr("failed to create kubeconfig secret", err)
}
@@ -540,18 +542,18 @@ func (c *ClusterReconciler) getETCDTLS(cluster *v1alpha1.Cluster) (*tls.Config,
klog.Infof("generating etcd TLS client certificate for cluster [%s]", cluster.Name)
token := cluster.Spec.Token
endpoint := "k3k-server-service." + util.ClusterNamespace(cluster)
var bootstrap *server.ControlRuntimeBootstrap
var b *bootstrap.ControlRuntimeBootstrap
if err := retry.OnError(retry.DefaultBackoff, func(err error) bool {
return true
}, func() error {
var err error
bootstrap, err = server.DecodedBootstrap(token, endpoint)
b, err = bootstrap.DecodedBootstrap(token, endpoint)
return err
}); err != nil {
return nil, err
}
etcdCert, etcdKey, err := server.CreateClientCertKey("etcd-client", nil, nil, []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, bootstrap.ETCDServerCA.Content, bootstrap.ETCDServerCAKey.Content)
etcdCert, etcdKey, err := kubeconfig.CreateClientCertKey("etcd-client", nil, nil, []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, 0, b.ETCDServerCA.Content, b.ETCDServerCAKey.Content)
if err != nil {
return nil, err
}
@@ -560,7 +562,7 @@ func (c *ClusterReconciler) getETCDTLS(cluster *v1alpha1.Cluster) (*tls.Config,
return nil, err
}
// create rootCA CertPool
cert, err := certutil.ParseCertsPEM([]byte(bootstrap.ETCDServerCA.Content))
cert, err := certutil.ParseCertsPEM([]byte(b.ETCDServerCA.Content))
if err != nil {
return nil, err
}
@@ -1,34 +1,22 @@
package server
package bootstrap
import (
"context"
"crypto"
"crypto/tls"
"crypto/x509"
"encoding/base64"
"encoding/json"
"fmt"
"net/http"
"time"
certutil "github.com/rancher/dynamiclistener/cert"
"github.com/rancher/k3k/pkg/controller/util"
"github.com/rancher/k3k/pkg/apis/k3k.io/v1alpha1"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apiserver/pkg/authentication/user"
"k8s.io/client-go/tools/clientcmd"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
"k8s.io/client-go/util/retry"
)
const (
adminCommonName = "system:admin"
port = 6443
)
type ControlRuntimeBootstrap struct {
ServerCA content
ServerCAKey content
ServerCA content `json:"serverCA"`
ServerCAKey content `json:"server"`
ClientCA content
ClientCAKey content
ETCDServerCA content
@@ -40,13 +28,11 @@ type content struct {
Content string
}
// GenerateNewKubeConfig generates the kubeconfig for the cluster:
// Generate generates the bootstrap for the cluster:
// 1- use the server token to get the bootstrap data from k3s
// 2- generate client admin cert/key
// 3- use the ca cert from the bootstrap data & admin cert/key to write a new kubeconfig
// 4- save the new kubeconfig as a secret
func (s *Server) GenerateNewKubeConfig(ctx context.Context, ip string) (*v1.Secret, error) {
token := s.cluster.Spec.Token
// 2- save the bootstrap data as a secret
func Generate(ctx context.Context, cluster *v1alpha1.Cluster, ip string) (*v1.Secret, error) {
token := cluster.Spec.Token
var bootstrap *ControlRuntimeBootstrap
if err := retry.OnError(retry.DefaultBackoff, func(err error) bool {
@@ -63,32 +49,21 @@ func (s *Server) GenerateNewKubeConfig(ctx context.Context, ip string) (*v1.Secr
return nil, err
}
adminCert, adminKey, err := CreateClientCertKey(
adminCommonName, []string{user.SystemPrivilegedGroup},
nil, []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
bootstrap.ClientCA.Content,
bootstrap.ClientCAKey.Content)
bootstrapData, err := json.Marshal(bootstrap)
if err != nil {
return nil, err
}
url := fmt.Sprintf("https://%s:%d", ip, port)
kubeconfigData, err := kubeconfig(url, []byte(bootstrap.ServerCA.Content), adminCert, adminKey)
if err != nil {
return nil, err
}
return &v1.Secret{
TypeMeta: metav1.TypeMeta{
Kind: "Secret",
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: s.cluster.Name + "-kubeconfig",
Namespace: util.ClusterNamespace(s.cluster),
Name: cluster.Name + "-bootstrap",
Namespace: "k3k-" + cluster.Name,
},
Data: map[string][]byte{
"kubeconfig.yaml": kubeconfigData,
"bootstrap": bootstrapData,
},
}, nil
@@ -126,80 +101,6 @@ func requestBootstrap(token, serverIP string) (*ControlRuntimeBootstrap, error)
return &runtimeBootstrap, nil
}
func CreateClientCertKey(commonName string, organization []string, altNames *certutil.AltNames, extKeyUsage []x509.ExtKeyUsage, caCert, caKey string) ([]byte, []byte, error) {
caKeyPEM, err := certutil.ParsePrivateKeyPEM([]byte(caKey))
if err != nil {
return nil, nil, err
}
caCertPEM, err := certutil.ParseCertsPEM([]byte(caCert))
if err != nil {
return nil, nil, err
}
b, err := generateKey()
if err != nil {
return nil, nil, err
}
key, err := certutil.ParsePrivateKeyPEM(b)
if err != nil {
return nil, nil, err
}
cfg := certutil.Config{
CommonName: commonName,
Organization: organization,
Usages: extKeyUsage,
}
if altNames != nil {
cfg.AltNames = *altNames
}
cert, err := certutil.NewSignedCert(cfg, key.(crypto.Signer), caCertPEM[0], caKeyPEM.(crypto.Signer))
if err != nil {
return nil, nil, err
}
return append(certutil.EncodeCertPEM(cert), certutil.EncodeCertPEM(caCertPEM[0])...), b, nil
}
func generateKey() (data []byte, err error) {
generatedData, err := certutil.MakeEllipticPrivateKeyPEM()
if err != nil {
return nil, fmt.Errorf("error generating key: %v", err)
}
return generatedData, nil
}
func kubeconfig(url string, serverCA, clientCert, clientKey []byte) ([]byte, error) {
config := clientcmdapi.NewConfig()
cluster := clientcmdapi.NewCluster()
cluster.CertificateAuthorityData = serverCA
cluster.Server = url
authInfo := clientcmdapi.NewAuthInfo()
authInfo.ClientCertificateData = clientCert
authInfo.ClientKeyData = clientKey
context := clientcmdapi.NewContext()
context.AuthInfo = "default"
context.Cluster = "default"
config.Clusters["default"] = cluster
config.AuthInfos["default"] = authInfo
config.Contexts["default"] = context
config.CurrentContext = "default"
kubeconfig, err := clientcmd.Write(*config)
if err != nil {
return nil, err
}
return kubeconfig, nil
}
func basicAuth(username, password string) string {
auth := username + ":" + password
return base64.StdEncoding.EncodeToString([]byte(auth))
+3 -1
View File
@@ -15,6 +15,8 @@ const (
nginxSSLPassthroughAnnotation = "nginx.ingress.kubernetes.io/ssl-passthrough"
nginxBackendProtocolAnnotation = "nginx.ingress.kubernetes.io/backend-protocol"
nginxSSLRedirectAnnotation = "nginx.ingress.kubernetes.io/ssl-redirect"
serverPort = 6443
etcdPort = 2379
)
func (s *Server) Ingress(ctx context.Context, client client.Client) (*networkingv1.Ingress, error) {
@@ -59,7 +61,7 @@ func (s *Server) ingressRules(addresses []string) []networkingv1.IngressRule {
Service: &networkingv1.IngressServiceBackend{
Name: "k3k-server-service",
Port: networkingv1.ServiceBackendPort{
Number: port,
Number: serverPort,
},
},
},
+4 -4
View File
@@ -36,12 +36,12 @@ func (s *Server) Service(cluster *v1alpha1.Cluster) *v1.Service {
{
Name: "k3s-server-port",
Protocol: v1.ProtocolTCP,
Port: port,
Port: serverPort,
},
{
Name: "k3s-etcd-port",
Protocol: v1.ProtocolTCP,
Port: 2379,
Port: etcdPort,
},
},
},
@@ -70,12 +70,12 @@ func (s *Server) StatefulServerService(cluster *v1alpha1.Cluster) *v1.Service {
{
Name: "k3s-server-port",
Protocol: v1.ProtocolTCP,
Port: 6443,
Port: serverPort,
},
{
Name: "k3s-etcd-port",
Protocol: v1.ProtocolTCP,
Port: 2379,
Port: etcdPort,
},
},
},
+71
View File
@@ -0,0 +1,71 @@
package kubeconfig
import (
"crypto"
"crypto/x509"
"fmt"
"net"
"time"
certutil "github.com/rancher/dynamiclistener/cert"
)
func CreateClientCertKey(commonName string, organization []string, altNames *certutil.AltNames, extKeyUsage []x509.ExtKeyUsage, expiresAt time.Duration, caCert, caKey string) ([]byte, []byte, error) {
caKeyPEM, err := certutil.ParsePrivateKeyPEM([]byte(caKey))
if err != nil {
return nil, nil, err
}
caCertPEM, err := certutil.ParseCertsPEM([]byte(caCert))
if err != nil {
return nil, nil, err
}
b, err := generateKey()
if err != nil {
return nil, nil, err
}
key, err := certutil.ParsePrivateKeyPEM(b)
if err != nil {
return nil, nil, err
}
cfg := certutil.Config{
CommonName: commonName,
Organization: organization,
Usages: extKeyUsage,
ExpiresAt: expiresAt,
}
if altNames != nil {
cfg.AltNames = *altNames
}
cert, err := certutil.NewSignedCert(cfg, key.(crypto.Signer), caCertPEM[0], caKeyPEM.(crypto.Signer))
if err != nil {
return nil, nil, err
}
return append(certutil.EncodeCertPEM(cert), certutil.EncodeCertPEM(caCertPEM[0])...), b, nil
}
func generateKey() (data []byte, err error) {
generatedData, err := certutil.MakeEllipticPrivateKeyPEM()
if err != nil {
return nil, fmt.Errorf("error generating key: %v", err)
}
return generatedData, nil
}
func AddSANs(sans []string) certutil.AltNames {
var altNames certutil.AltNames
for _, san := range sans {
ip := net.ParseIP(san)
if ip == nil {
altNames.DNSNames = append(altNames.DNSNames, san)
} else {
altNames.IPs = append(altNames.IPs, ip)
}
}
return altNames
}
+108
View File
@@ -0,0 +1,108 @@
package kubeconfig
import (
"context"
"crypto/x509"
"encoding/json"
"errors"
"fmt"
"time"
certutil "github.com/rancher/dynamiclistener/cert"
"github.com/rancher/k3k/pkg/apis/k3k.io/v1alpha1"
"github.com/rancher/k3k/pkg/controller/cluster/server/bootstrap"
"github.com/rancher/k3k/pkg/controller/util"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/tools/clientcmd"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
"sigs.k8s.io/controller-runtime/pkg/client"
)
type KubeConfig struct {
AltNames certutil.AltNames
CN string
ORG []string
ExpiryDate time.Duration
}
func (k *KubeConfig) Extract(ctx context.Context, client client.Client, cluster *v1alpha1.Cluster, hostServerIP string) ([]byte, error) {
nn := types.NamespacedName{
Name: cluster.Name + "-bootstrap",
Namespace: util.ClusterNamespace(cluster),
}
var bootstrapSecret v1.Secret
if err := client.Get(ctx, nn, &bootstrapSecret); err != nil {
return nil, err
}
bootstrapData := bootstrapSecret.Data["bootstrap"]
if bootstrapData == nil {
return nil, errors.New("empty bootstrap")
}
var bootstrap bootstrap.ControlRuntimeBootstrap
if err := json.Unmarshal(bootstrapData, &bootstrap); err != nil {
return nil, err
}
adminCert, adminKey, err := CreateClientCertKey(
k.CN, k.ORG,
&k.AltNames, []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, k.ExpiryDate,
bootstrap.ClientCA.Content,
bootstrap.ClientCAKey.Content)
if err != nil {
return nil, err
}
// get the server service to extract the right IP
nn = types.NamespacedName{
Name: "k3k-server-service",
Namespace: util.ClusterNamespace(cluster),
}
var k3kService v1.Service
if err := client.Get(ctx, nn, &k3kService); err != nil {
return nil, err
}
url := fmt.Sprintf("https://%s:%d", k3kService.Spec.ClusterIP, util.ServerPort)
if k3kService.Spec.Type == v1.ServiceTypeNodePort {
nodePort := k3kService.Spec.Ports[0].NodePort
url = fmt.Sprintf("https://%s:%d", hostServerIP, nodePort)
}
kubeconfigData, err := kubeconfig(url, []byte(bootstrap.ServerCA.Content), adminCert, adminKey)
if err != nil {
return nil, err
}
return kubeconfigData, nil
}
func kubeconfig(url string, serverCA, clientCert, clientKey []byte) ([]byte, error) {
config := clientcmdapi.NewConfig()
cluster := clientcmdapi.NewCluster()
cluster.CertificateAuthorityData = serverCA
cluster.Server = url
authInfo := clientcmdapi.NewAuthInfo()
authInfo.ClientCertificateData = clientCert
authInfo.ClientKeyData = clientKey
context := clientcmdapi.NewContext()
context.AuthInfo = "default"
context.Cluster = "default"
config.Clusters["default"] = cluster
config.AuthInfos["default"] = authInfo
config.Contexts["default"] = context
config.CurrentContext = "default"
kubeconfig, err := clientcmd.Write(*config)
if err != nil {
return nil, err
}
return kubeconfig, nil
}
+2
View File
@@ -12,6 +12,8 @@ import (
const (
namespacePrefix = "k3k-"
k3SImageName = "rancher/k3s"
AdminCommonName = "system:admin"
ServerPort = 6443
)
const (