Learning k8s series on mac (14) kubectl source code reading

The entries of all components of k8s are in the cmd directory, the list is as follows:

  OWNERS
  clicheck
  cloud-controller-manager
  dependencycheck
  gendocs
  genkubedocs
  genman
  genswaggertypedocs
  genutils
  genyaml
  importverifier
  kube-apiserver
  kube-controller-manager
  kube-proxy
  kube-scheduler
  kubeadm
  kubectl
  kubectl-convert
  kubelet
  kubemark
  linkcheck
  preferredimports
copy

There is a file in the kubectl directory: cmd/kubectl/kubectl.go. Like docker, k8s is also assembled with spf13 cobra for command line parameter parsing:

command := cmd.NewDefaultKubectlCommand()
pflag.CommandLine.SetNormalizeFunc(cliflag.WordSepNormalizeFunc)
pflag.CommandLine.AddGoFlagSet(goflag.CommandLine)
copy

There is no logic in the entry. The main logic is placed in the file vendor/k8s.io/kubectl/pkg/cmd/cmd.go, which mainly calls two functions

NewDefaultKubectlCommandWithArgs()
HandlePluginCommand(pluginHandler, cmdPathPieces)
copy

1,NewDefaultKubectlCommandWithArgs

cmd := NewKubectlCommand(in, out, errout)
f := cmdutil.NewFactory(matchVersionKubeConfigFlags)
proxyCmd := proxy.NewCmdProxy(f, ioStreams)
groups := templates.CommandGroups
groups.Add(cmds)
templates.ActsAsRootCommand(cmds, filters, groups...)
copy

Create cmd and then put it into CommandGroups, and finally use the template for unified processing. CommandGroups classifies the commands, and the classification contains detailed cmds

Beginner
  create.NewCmdCreate(f, ioStreams),
  expose.NewCmdExposeService(f, ioStreams),
  run.NewCmdRun(f, ioStreams),
  set.NewCmdSet(f, ioStreams),
Intermediate
  explain.NewCmdExplain("kubectl", f, ioStreams),
  get.NewCmdGet("kubectl", f, ioStreams),
  edit.NewCmdEdit(f, ioStreams),
  delete.NewCmdDelete(f, ioStreams),
Deploy
  rollout.NewCmdRollout(f, ioStreams),
  scale.NewCmdScale(f, ioStreams),
  autoscale.NewCmdAutoscale(f, ioStreams),
Cluster
  certificates.NewCmdCertificate(f, ioStreams),
  clusterinfo.NewCmdClusterInfo(f, ioStreams),
  top.NewCmdTop(f, ioStreams),
  drain.NewCmdCordon(f, ioStreams),
  drain.NewCmdUncordon(f, ioStreams),
  drain.NewCmdDrain(f, ioStreams),
  taint.NewCmdTaint(f, ioStreams),
Troubleshooting
  describe.NewCmdDescribe("kubectl", f, ioStreams),
  logs.NewCmdLogs(f, ioStreams),
  attach.NewCmdAttach(f, ioStreams),
  cmdexec.NewCmdExec(f, ioStreams),
  portforward.NewCmdPortForward(f, ioStreams),
  proxyCmd,
  cp.NewCmdCp(f, ioStreams),
  auth.NewCmdAuth(f, ioStreams),
  debug.NewCmdDebug(f, ioStreams),
Advanced
  diff.NewCmdDiff(f, ioStreams),
  apply.NewCmdApply("kubectl", f, ioStreams),
  patch.NewCmdPatch(f, ioStreams),
  replace.NewCmdReplace(f, ioStreams),
  wait.NewCmdWait(f, ioStreams),
  kustomize.NewCmdKustomize(ioStreams),
Settings
  label.NewCmdLabel(f, ioStreams),
  annotate.NewCmdAnnotate("kubectl", f, ioStreams),
  completion.NewCmdCompletion(ioStreams.Out, ""),
copy

Here are all the commands we use in kubectl, which are divided into the following categories: basic, intermediate, deployment-related, cluster-related, troubleshooting-related, advanced, and setting-related. This is a good idea. The tedious commands become clear and clear through classification, just like a storage box, so that messy things quickly become orderly.

2,HandlePluginCommand

pluginHandler.Execute(foundBinaryPath, cmdArgs[len(remainingArgs):], os.Environ())
copy

The above is the overall framework of kubectl basic commands. The following two commands, kubectl proxy and kubectl get, are used as examples for detailed introduction:

1.kubectl proxy

Implemented an http proxy. The bottom layer actually calls a reverse proxy function httputil.NewSingleHostReverseProxy encapsulated in the golang source code. The entry code is in

In vendor/k8s.io/kubectl/pkg/cmd/proxy/proxy.go:

o := NewProxyOptions(ioStreams)
  cmdutil.CheckErr(o.Complete(f))
  cmdutil.CheckErr(o.Validate())
  cmdutil.CheckErr(o.RunProxy())
copy

Other commands are also the same routine: get option parameters, complete the complete command, verify the validity of the command, and run the corresponding command.

First look at the parameter verification:

AcceptPaths:   proxy.MakeRegexpArrayOrDie(o.acceptPaths),
RejectPaths:   proxy.MakeRegexpArrayOrDie(o.rejectPaths),
AcceptHosts:   proxy.MakeRegexpArrayOrDie(o.acceptHosts),
RejectMethods: proxy.MakeRegexpArrayOrDie(o.rejectMethods),
copy

Then watch the command in action:

server, err := proxy.NewServer(o.staticDir, o.apiPrefix, o.staticPrefix, o.filter, o.clientConfig, o.keepalive)
l, err = server.Listen(o.address, o.port)
l, err = server.ListenUnix(o.unixSocket)
return server.ServeOnListener(l)
copy

A proxy serve is started, and then it runs as a listening unixSocket, and all received requests are reversely proxied to the backend. The implementation code of proxy_server is at: vendor/k8s.io/kubectl/pkg/proxy/proxy_server.go focuses on the next three functions, creating server, setting processing handler, and listening port.

func NewServer(filebase string, apiProxyPrefix string, staticPrefix string, filter *FilterServer, cfg *rest.Config, keepalive time.Duration) (*Server, error)
    proxyHandler, err := NewProxyHandler(apiProxyPrefix, filter, cfg, keepalive)
    mux := http.NewServeMux()
    mux.Handle(apiProxyPrefix, proxyHandler)
copy
func (s *Server) ServeOnListener(l net.Listener) error 
    server.Serve(l)
copy
func NewProxyHandler(apiProxyPrefix string, filter *FilterServer, cfg *rest.Config, keepalive time.Duration) (http.Handler, error)
    target, err := url.Parse(host)
    transport, err := rest.TransportFor(cfg)
    upgradeTransport, err := makeUpgradeTransport(cfg, keepalive)
    proxy := proxy.NewUpgradeAwareHandler(target, transport, false, false, responder)
    proxyServer := http.Handler(proxy)
copy

In fact, the code is similar to an ordinary http server. It can be seen that NewProxyHandler is a simple encapsulation of NewUpgradeAwareHandler. The specific implementation code is located at:

vendor/k8s.io/apimachinery/pkg/util/proxy/upgradeaware.go

func NewUpgradeAwareHandler(location *url.URL, transport http.RoundTripper, wrapTransport, upgradeRequired bool, responder ErrorResponder) *UpgradeAwareHandler
copy

The ServeHTTP function actually calls httputil.NewSingleHostReverseProxy

func (h *UpgradeAwareHandler) ServeHTTP(w http.ResponseWriter, req *http.Request)
    proxy := httputil.NewSingleHostReverseProxy(&url.URL{Scheme: h.Location.Scheme, Host: h.Location.Host})
    proxy.ServeHTTP(w, newReq)
copy

In one sentence, kubectl proxy is actually a reverse proxy tool.

2´╝îkubectl get

The code implementation location of get is vendor/k8s.io/kubectl/pkg/cmd/get/get.go The routine of initializing cmd is the same

func NewCmdGet(parent string, f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command 
    o := NewGetOptions(parent, streams)
    cmdutil.CheckErr(o.Complete(f, cmd, args))
    cmdutil.CheckErr(o.Validate(cmd))
    cmdutil.CheckErr(o.Run(f, cmd, args))
copy

Focus on the run function

func (o *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []string) error 
    restClient, err := f.RESTClient()
    return rawhttp.RawGet(restClient, o.IOStreams, o.Raw)
    return o.watch(f, cmd, args)
    
  r := f.NewBuilder().
    Unstructured().
    NamespaceParam(o.Namespace).DefaultNamespace().AllNamespaces(o.AllNamespaces).
    FilenameParam(o.ExplicitNamespace, &o.FilenameOptions).
    LabelSelectorParam(o.LabelSelector).
    FieldSelectorParam(o.FieldSelector).
    RequestChunksOf(chunkSize).
    ResourceTypeOrNameArgs(true, args...).
    ContinueOnError().
    Latest().
    Flatten().
    TransformRequests(o.transformRequests).
    Do()
copy

The data is obtained through the get method of restfull's http client. The code related to restfull client creation is as follows:

vendor/k8s.io/kubectl/pkg/cmd/util/factory.go

type Factory interface {}
    RESTClient() (*restclient.RESTClient, error)
copy

vendor/k8s.io/kubectl/pkg/cmd/util/factory_client_access.go

func (f *factoryImpl) RESTClient() (*restclient.RESTClient, error) 
    clientConfig, err := f.ToRESTConfig()
    return restclient.RESTClientFor(clientConfig)
copy

vendor/k8s.io/client-go/rest/config.go

func RESTClientFor(config *Config) (*RESTClient, error) 
    baseURL, versionedAPIPath, err := defaultServerUrlFor(config)
    transport, err := TransportFor(config)
    restClient, err := NewRESTClient(baseURL, versionedAPIPath, clientContent, rateLimiter, httpClient)
copy

vendor/k8s.io/client-go/rest/client.go

func NewRESTClient(baseURL *url.URL, versionedAPIPath string, config ClientContentConfig, rateLimiter flowcontrol.RateLimiter, client *http.Client) (*RESTClient, error) 
  type RESTClient struct {}
copy

In fact, kubectl just encapsulates the command line interface provided by kubeapiserver, which is essentially a client, which is the same idea as the implementation of docker source code.

Posted by Kenwio on Mon, 26 Sep 2022 21:07:03 +0300