Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
neolit123 committed Aug 19, 2024
1 parent cb7b4ea commit 174edd6
Show file tree
Hide file tree
Showing 25 changed files with 1,363 additions and 954 deletions.
14 changes: 9 additions & 5 deletions cmd/kubeadm/app/cmd/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -502,12 +502,16 @@ func (d *initData) OutputWriter() io.Writer {

// getDryRunClient creates a fake client that answers some GET calls in order to be able to do the full init flow in dry-run mode.
func getDryRunClient(d *initData) (clientset.Interface, error) {
svcSubnetCIDR, err := kubeadmconstants.GetKubernetesServiceCIDR(d.cfg.Networking.ServiceSubnet)
if err != nil {
return nil, errors.Wrapf(err, "unable to get internal Kubernetes Service IP from the given service CIDR (%s)", d.cfg.Networking.ServiceSubnet)
dryRun := apiclient.NewDryRun()
if err := dryRun.WithKubeConfigFile(d.KubeConfigPath()); err != nil {
return nil, err
}
dryRunGetter := apiclient.NewInitDryRunGetter(d.cfg.NodeRegistration.Name, svcSubnetCIDR.String())
return apiclient.NewDryRunClient(dryRunGetter, os.Stdout), nil
dryRun.WithDefaultMarshalFunction().
WithWriter(os.Stdout).
PrependReactor(dryRun.GetNodeReactor()).
PrependReactor(dryRun.PatchNodeReactor())

return dryRun.FakeClient(), nil
}

// Client returns a Kubernetes client to be used by kubeadm.
Expand Down
84 changes: 71 additions & 13 deletions cmd/kubeadm/app/cmd/join.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ import (
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
"k8s.io/kubernetes/cmd/kubeadm/app/discovery"
"k8s.io/kubernetes/cmd/kubeadm/app/discovery/token"
"k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient"
configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config"
kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig"
)
Expand Down Expand Up @@ -535,8 +537,19 @@ func (j *joinData) TLSBootstrapCfg() (*clientcmdapi.Config, error) {
if j.tlsBootstrapCfg != nil {
return j.tlsBootstrapCfg, nil
}

var (
client clientset.Interface
err error
)
if j.dryRun {
client, err = j.Client()
if err != nil {
return nil, errors.Wrap(err, "could not create a client for TLS bootstrap")
}
}
klog.V(1).Infoln("[preflight] Discovering cluster-info")
tlsBootstrapCfg, err := discovery.For(j.cfg)
tlsBootstrapCfg, err := discovery.For(client, j.cfg, j.dryRun)
j.tlsBootstrapCfg = tlsBootstrapCfg
return tlsBootstrapCfg, err
}
Expand All @@ -550,19 +563,58 @@ func (j *joinData) InitCfg() (*kubeadmapi.InitConfiguration, error) {
return nil, err
}
klog.V(1).Infoln("[preflight] Fetching init configuration")
initCfg, err := fetchInitConfigurationFromJoinConfiguration(j.cfg, j.tlsBootstrapCfg)
var client clientset.Interface
if j.dryRun {
var err error
client, err = j.Client()
if err != nil {
return nil, errors.Wrap(err, "could not get dry-run client for fetching InitConfiguration")
}
}
initCfg, err := fetchInitConfigurationFromJoinConfiguration(j.cfg, client, j.tlsBootstrapCfg)
j.initCfg = initCfg
return initCfg, err
}

// Client returns the Client for accessing the cluster with the identity defined in admin.conf.
func (j *joinData) Client() (clientset.Interface, error) {
if j.client != nil {
pathAdmin := filepath.Join(j.KubeConfigDir(), kubeadmconstants.AdminKubeConfigFileName)

if j.dryRun {
dryRun := apiclient.NewDryRun()
// For the dynamic dry-run client use this kubeconfig only if it exists.
// That would happen presumably after TLS bootstrap.
if _, err := os.Stat(pathAdmin); err == nil {
if err := dryRun.WithKubeConfigFile(pathAdmin); err != nil {
return nil, err
}
} else if j.tlsBootstrapCfg != nil {
if err := dryRun.WithKubeConfig(j.tlsBootstrapCfg); err != nil {
return nil, err
}
} else if j.cfg.Discovery.BootstrapToken != nil {
insecureConfig := token.BuildInsecureBootstrapKubeConfig(j.cfg.Discovery.BootstrapToken.APIServerEndpoint)
resetConfig, err := clientcmd.NewDefaultClientConfig(*insecureConfig, &clientcmd.ConfigOverrides{}).ClientConfig()
if err != nil {
return nil, errors.Wrap(err, "failed to create API client configuration from kubeconfig")
}
if err := dryRun.WithRestConfig(resetConfig); err != nil {
return nil, err
}
}

dryRun.WithDefaultMarshalFunction().
WithWriter(os.Stdout).
AppendReactor(dryRun.GetClusterInfoReactor()).
AppendReactor(dryRun.GetKubeadmConfigReactor()).
AppendReactor(dryRun.GetKubeProxyConfigReactor()).
AppendReactor(dryRun.GetKubeletConfigReactor())

j.client = dryRun.FakeClient()
return j.client, nil
}
path := filepath.Join(j.KubeConfigDir(), kubeadmconstants.AdminKubeConfigFileName)

client, err := kubeconfigutil.ClientSetFromFile(path)
client, err := kubeconfigutil.ClientSetFromFile(pathAdmin)
if err != nil {
return nil, errors.Wrap(err, "[preflight] couldn't create Kubernetes client")
}
Expand Down Expand Up @@ -593,10 +645,10 @@ func (j *joinData) PatchesDir() string {
}

// fetchInitConfigurationFromJoinConfiguration retrieves the init configuration from a join configuration, performing the discovery
func fetchInitConfigurationFromJoinConfiguration(cfg *kubeadmapi.JoinConfiguration, tlsBootstrapCfg *clientcmdapi.Config) (*kubeadmapi.InitConfiguration, error) {
func fetchInitConfigurationFromJoinConfiguration(cfg *kubeadmapi.JoinConfiguration, dryRunClient clientset.Interface, tlsBootstrapCfg *clientcmdapi.Config) (*kubeadmapi.InitConfiguration, error) {
// Retrieves the kubeadm configuration
klog.V(1).Infoln("[preflight] Retrieving KubeConfig objects")
initConfiguration, err := fetchInitConfiguration(tlsBootstrapCfg)
initConfiguration, err := fetchInitConfiguration(dryRunClient, tlsBootstrapCfg)
if err != nil {
return nil, err
}
Expand All @@ -618,15 +670,21 @@ func fetchInitConfigurationFromJoinConfiguration(cfg *kubeadmapi.JoinConfigurati
}

// fetchInitConfiguration reads the cluster configuration from the kubeadm-admin configMap
func fetchInitConfiguration(tlsBootstrapCfg *clientcmdapi.Config) (*kubeadmapi.InitConfiguration, error) {
// creates a client to access the cluster using the bootstrap token identity
tlsClient, err := kubeconfigutil.ToClientSet(tlsBootstrapCfg)
if err != nil {
return nil, errors.Wrap(err, "unable to access the cluster")
func fetchInitConfiguration(dryRunClient clientset.Interface, tlsBootstrapCfg *clientcmdapi.Config) (*kubeadmapi.InitConfiguration, error) {
var err error

client := dryRunClient
if dryRunClient == nil {
// creates a client to access the cluster using the bootstrap token identity
client, err = kubeconfigutil.ToClientSet(tlsBootstrapCfg)
if err != nil {
return nil, errors.Wrap(err, "unable to access the cluster")
}

}

// Fetches the init configuration
initConfiguration, err := configutil.FetchInitConfigurationFromCluster(tlsClient, nil, "preflight", true, false)
initConfiguration, err := configutil.FetchInitConfigurationFromCluster(client, nil, "preflight", true, false)
if err != nil {
return nil, errors.Wrap(err, "unable to fetch the kubeadm-config ConfigMap")
}
Expand Down
20 changes: 14 additions & 6 deletions cmd/kubeadm/app/cmd/phases/join/kubelet.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/wait"
clientset "k8s.io/client-go/kubernetes"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
certutil "k8s.io/client-go/util/cert"
"k8s.io/klog/v2"
Expand Down Expand Up @@ -150,11 +151,18 @@ func runKubeletStartJoinPhase(c workflow.RunData) (returnErr error) {
}()
}

// Create the bootstrap client before we possibly overwrite the server address
// for ControlPlaneKubeletLocalMode.
bootstrapClient, err := kubeconfigutil.ToClientSet(tlsBootstrapCfg)
if err != nil {
return errors.Errorf("could not create client from bootstrap kubeconfig")
var client clientset.Interface
// If dry-use the client from joinData, else create a new bootstrap client
if data.DryRun() {
client, err = data.Client()
if err != nil {
return err
}
} else {
client, err = kubeconfigutil.ToClientSet(tlsBootstrapCfg)
if err != nil {
return errors.Errorf("could not create client from bootstrap kubeconfig")
}
}

if features.Enabled(initCfg.FeatureGates, features.ControlPlaneKubeletLocalMode) {
Expand Down Expand Up @@ -204,7 +212,7 @@ func runKubeletStartJoinPhase(c workflow.RunData) (returnErr error) {
// A new Node with the same name as an existing control-plane Node can cause undefined
// behavior and ultimately control-plane failure.
klog.V(1).Infof("[kubelet-start] Checking for an existing Node in the cluster with name %q and status %q", nodeName, v1.NodeReady)
node, err := bootstrapClient.CoreV1().Nodes().Get(context.TODO(), nodeName, metav1.GetOptions{})
node, err := client.CoreV1().Nodes().Get(context.TODO(), nodeName, metav1.GetOptions{})
if err != nil && !apierrors.IsNotFound(err) {
return errors.Wrapf(err, "cannot get Node %q", nodeName)
}
Expand Down
12 changes: 6 additions & 6 deletions cmd/kubeadm/app/cmd/phases/reset/cleanupnode.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ func runCleanupNode(c workflow.RunData) error {
klog.Warningln("[reset] Please ensure kubelet is stopped manually")
}
} else {
fmt.Println("[reset] Would stop the kubelet service")
fmt.Println("[dryrun] Would stop the kubelet service")
}
}

Expand All @@ -96,7 +96,7 @@ func runCleanupNode(c workflow.RunData) error {
dirsToClean = append(dirsToClean, kubeletRunDirectory)
}
} else {
fmt.Printf("[reset] Would unmount mounted directories in %q\n", kubeadmconstants.KubeletRunDirectory)
fmt.Printf("[dryrun] Would unmount mounted directories in %q\n", kubeadmconstants.KubeletRunDirectory)
}

if !r.DryRun() {
Expand All @@ -105,7 +105,7 @@ func runCleanupNode(c workflow.RunData) error {
klog.Warningf("[reset] Failed to remove containers: %v\n", err)
}
} else {
fmt.Println("[reset] Would remove Kubernetes-managed containers")
fmt.Println("[dyrrun] Would remove Kubernetes-managed containers")
}

// Remove contents from the config and pki directories
Expand All @@ -127,7 +127,7 @@ func runCleanupNode(c workflow.RunData) error {
klog.Warningf("[reset] Failed to remove users and groups: %v\n", err)
}
} else {
fmt.Println("[reset] Would remove users and groups created for rootless control-plane")
fmt.Println("[dryrun] Would remove users and groups created for rootless control-plane")
}
}

Expand Down Expand Up @@ -156,7 +156,7 @@ func resetConfigDir(configPathDir string, dirsToClean []string, isDryRun bool) {
}
}
} else {
fmt.Printf("[reset] Would delete contents of directories: %v\n", dirsToClean)
fmt.Printf("[dryrun] Would delete contents of directories: %v\n", dirsToClean)
}

filesToClean := []string{
Expand All @@ -176,7 +176,7 @@ func resetConfigDir(configPathDir string, dirsToClean []string, isDryRun bool) {
}
}
} else {
fmt.Printf("[reset] Would delete files: %v\n", filesToClean)
fmt.Printf("[dryrun] Would delete files: %v\n", filesToClean)
}
}

Expand Down
4 changes: 2 additions & 2 deletions cmd/kubeadm/app/cmd/phases/reset/removeetcdmember.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,8 @@ func runRemoveETCDMemberPhase(c workflow.RunData) error {
}
}
} else {
fmt.Println("[reset] Would remove the etcd member on this node from the etcd cluster")
fmt.Printf("[reset] Would delete contents of the etcd data directory: %v\n", etcdDataDir)
fmt.Println("[dryrun] Would remove the etcd member on this node from the etcd cluster")
fmt.Printf("[dryrun] Would delete contents of the etcd data directory: %v\n", etcdDataDir)
}
}
// This could happen if the phase `cleanup-node` is run before the `remove-etcd-member`.
Expand Down
26 changes: 23 additions & 3 deletions cmd/kubeadm/app/cmd/reset.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"errors"
"fmt"
"io"
"os"
"path"

"github.com/lithammer/dedent"
Expand All @@ -39,7 +40,9 @@ import (
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/phases/workflow"
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
"k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient"
configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config"
kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig"
utilruntime "k8s.io/kubernetes/cmd/kubeadm/app/util/runtime"
)

Expand Down Expand Up @@ -104,7 +107,10 @@ func newResetData(cmd *cobra.Command, opts *resetOptions, in io.Reader, out io.W
return nil, err
}

var initCfg *kubeadmapi.InitConfiguration
var (
initCfg *kubeadmapi.InitConfiguration
client clientset.Interface
)

// Either use the config file if specified, or convert public kubeadm API to the internal ResetConfiguration and validates cfg.
resetCfg, err := configutil.LoadOrDefaultResetConfiguration(opts.cfgPath, opts.externalcfg, configutil.LoadOrDefaultConfigurationOptions{
Expand All @@ -115,7 +121,21 @@ func newResetData(cmd *cobra.Command, opts *resetOptions, in io.Reader, out io.W
return nil, err
}

client, err := cmdutil.GetClientSet(opts.kubeconfigPath, false)
dryRunFlag := cmdutil.ValueFromFlagsOrConfig(cmd.Flags(), options.DryRun, resetCfg.DryRun, opts.externalcfg.DryRun).(bool)
if dryRunFlag {
dryRun := apiclient.NewDryRun().WithDefaultMarshalFunction().WithWriter(os.Stdout)
dryRun.AppendReactor(dryRun.GetKubeadmConfigReactor()).
AppendReactor(dryRun.GetKubeletConfigReactor()).
AppendReactor(dryRun.GetKubeProxyConfigReactor())
client = dryRun.FakeClient()
_, err = os.Stat(opts.kubeconfigPath)
if err == nil {
err = dryRun.WithKubeConfigFile(opts.kubeconfigPath)
}
} else {
client, err = kubeconfigutil.ClientSetFromFile(opts.kubeconfigPath)
}

if err == nil {
klog.V(1).Infof("[reset] Loaded client set from kubeconfig file: %s", opts.kubeconfigPath)
initCfg, err = configutil.FetchInitConfigurationFromCluster(client, nil, "reset", false, false)
Expand Down Expand Up @@ -162,7 +182,7 @@ func newResetData(cmd *cobra.Command, opts *resetOptions, in io.Reader, out io.W
outputWriter: out,
cfg: initCfg,
resetCfg: resetCfg,
dryRun: cmdutil.ValueFromFlagsOrConfig(cmd.Flags(), options.DryRun, resetCfg.DryRun, opts.externalcfg.DryRun).(bool),
dryRun: dryRunFlag,
forceReset: cmdutil.ValueFromFlagsOrConfig(cmd.Flags(), options.ForceReset, resetCfg.Force, opts.externalcfg.Force).(bool),
cleanupTmpDir: cmdutil.ValueFromFlagsOrConfig(cmd.Flags(), options.CleanupTmpDir, resetCfg.CleanupTmpDir, opts.externalcfg.CleanupTmpDir).(bool),
}, nil
Expand Down
3 changes: 3 additions & 0 deletions cmd/kubeadm/app/cmd/reset_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,9 @@ func TestNewResetData(t *testing.T) {
resetOptions := newResetOptions()
cmd := newCmdReset(nil, nil, resetOptions)

// make sure all cases use dry-run as we are not constructing a kubeconfig
tc.flags[options.DryRun] = "true"

// sets cmd flags (that will be reflected on the reset options)
for f, v := range tc.flags {
cmd.Flags().Set(f, v)
Expand Down
23 changes: 20 additions & 3 deletions cmd/kubeadm/app/cmd/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"context"
"fmt"
"io"
"os"
"strings"
"text/tabwriter"
"time"
Expand Down Expand Up @@ -48,7 +49,9 @@ import (
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
tokenphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/bootstraptoken/node"
"k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient"
configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config"
kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig"
"k8s.io/kubernetes/cmd/kubeadm/app/util/output"
)

Expand Down Expand Up @@ -127,7 +130,7 @@ func newCmdToken(out io.Writer, errW io.Writer) *cobra.Command {

klog.V(1).Infoln("[token] getting Clientsets from kubeconfig file")
kubeConfigFile = cmdutil.GetKubeConfigPath(kubeConfigFile)
client, err := cmdutil.GetClientSet(kubeConfigFile, dryRun)
client, err := getClientForTokenCommands(kubeConfigFile, dryRun)
if err != nil {
return err
}
Expand Down Expand Up @@ -159,7 +162,7 @@ func newCmdToken(out io.Writer, errW io.Writer) *cobra.Command {
`),
RunE: func(tokenCmd *cobra.Command, args []string) error {
kubeConfigFile = cmdutil.GetKubeConfigPath(kubeConfigFile)
client, err := cmdutil.GetClientSet(kubeConfigFile, dryRun)
client, err := getClientForTokenCommands(kubeConfigFile, dryRun)
if err != nil {
return err
}
Expand Down Expand Up @@ -193,7 +196,7 @@ func newCmdToken(out io.Writer, errW io.Writer) *cobra.Command {
return errors.Errorf("missing argument; 'token delete' is missing token of form %q or %q", bootstrapapi.BootstrapTokenPattern, bootstrapapi.BootstrapTokenIDPattern)
}
kubeConfigFile = cmdutil.GetKubeConfigPath(kubeConfigFile)
client, err := cmdutil.GetClientSet(kubeConfigFile, dryRun)
client, err := getClientForTokenCommands(kubeConfigFile, dryRun)
if err != nil {
return err
}
Expand Down Expand Up @@ -432,3 +435,17 @@ func RunDeleteTokens(out io.Writer, client clientset.Interface, tokenIDsOrTokens
}
return nil
}

// getClientForTokenCommands returns a client to be used with token commands.
// When dry-running it includes token specific reactors.
func getClientForTokenCommands(file string, dryRun bool) (clientset.Interface, error) {
if dryRun {
dryRun := apiclient.NewDryRun().WithDefaultMarshalFunction().WithWriter(os.Stdout)
dryRun.AppendReactor(dryRun.DeleteBootstrapTokenReactor())
if err := dryRun.WithKubeConfigFile(file); err != nil {
return nil, err
}
return dryRun.FakeClient(), nil
}
return kubeconfigutil.ClientSetFromFile(file)
}
Loading

0 comments on commit 174edd6

Please sign in to comment.