基于代码源码tag:v1.26.3
入口:cmd/kubectl
command := cmd.NewDefaultKubectlCommand()
cli.RunNoErrOutput(command)
NewDefaultKubectlCommand创建一个默认实例,调用NewDefaultKubectlCommandWithArgs
cmd := NewKubectlCommand(o)
调用NewDefaultKubectlCommandWithArgs主要调用NewKubectlCommand
// NewKubectlCommand 创建`kubectl`命令以及嵌套的子命令
func NewKubectlCommand(o KubectlOptions) *cobra.Command {
warningHandler := rest.NewWarningWriter(o.IOStreams.ErrOut, rest.WarningWriterOptions{Deduplicate: true, Color: term.AllowsColorOutput(o.IOStreams.ErrOut)})
warningsAsErrors := false
// 所有子命令的父命令
cmds := &cobra.Command{
Use: "kubectl",
Short: i18n.T("kubectl controls the Kubernetes cluster manager"),
Long: templates.LongDesc(`
kubectl controls the Kubernetes cluster manager.
Find more information at:
https://kubernetes.io/docs/reference/kubectl/`),
Run: runHelp,
// 在运行初始化和将配置文件写入磁盘之前的钩子函数
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
rest.SetDefaultWarningHandler(warningHandler)
if cmd.Name() == cobra.ShellCompRequestCmd {
// This is the __complete or __completeNoDesc command which
// indicates shell completion has been requested.
plugin.SetupPluginCompletion(cmd, args)
}
return initProfiling()
},
// 在运行初始化和将配置文件写入磁盘之后的钩子函数
PersistentPostRunE: func(*cobra.Command, []string) error {
if err := flushProfiling(); err != nil {
return err
}
if warningsAsErrors {
count := warningHandler.WarningCount()
switch count {
case 0:
// no warnings
case 1:
return fmt.Errorf("%d warning received", count)
default:
return fmt.Errorf("%d warnings received", count)
}
}
return nil
},
}
// 参数中下划线_会被替换成中划线-,并输出warning日志
cmds.SetGlobalNormalizationFunc(cliflag.WarnWordSepNormalizeFunc)
flags := cmds.PersistentFlags()
addProfilingFlags(flags)
flags.BoolVar(&warningsAsErrors, "warnings-as-errors", warningsAsErrors, "Treat warnings received from the server as errors and exit with a non-zero exit code")
kubeConfigFlags := o.ConfigFlags
if kubeConfigFlags == nil {
kubeConfigFlags = defaultConfigFlags
}
kubeConfigFlags.AddFlags(flags)
matchVersionKubeConfigFlags := cmdutil.NewMatchVersionFlags(kubeConfigFlags)
matchVersionKubeConfigFlags.AddFlags(flags)
// Updates hooks to add kubectl command headers: SIG CLI KEP 859.
addCmdHeaderHooks(cmds, kubeConfigFlags)
// 为了多种资源和不同的api集合扩展抽象出来的工厂
f := cmdutil.NewFactory(matchVersionKubeConfigFlags)
// 代理子命令
proxyCmd := proxy.NewCmdProxy(f, o.IOStreams)
proxyCmd.PreRun = func(cmd *cobra.Command, args []string) {
kubeConfigFlags.WrapConfigFn = nil
}
// get子命令
getCmd := get.NewCmdGet("kubectl", f, o.IOStreams)
getCmd.ValidArgsFunction = utilcomp.ResourceTypeAndNameCompletionFunc(f)
// 子命令组
groups := templates.CommandGroups{
{
Message: "Basic Commands (Beginner):",
Commands: []*cobra.Command{
create.NewCmdCreate(f, o.IOStreams),
expose.NewCmdExposeService(f, o.IOStreams),
run.NewCmdRun(f, o.IOStreams),
set.NewCmdSet(f, o.IOStreams),
},
},
{
Message: "Basic Commands (Intermediate):",
Commands: []*cobra.Command{
explain.NewCmdExplain("kubectl", f, o.IOStreams),
getCmd,
edit.NewCmdEdit(f, o.IOStreams),
delete.NewCmdDelete(f, o.IOStreams),
},
},
{
Message: "Deploy Commands:",
Commands: []*cobra.Command{
rollout.NewCmdRollout(f, o.IOStreams),
scale.NewCmdScale(f, o.IOStreams),
autoscale.NewCmdAutoscale(f, o.IOStreams),
},
},
{
Message: "Cluster Management Commands:",
Commands: []*cobra.Command{
certificates.NewCmdCertificate(f, o.IOStreams),
clusterinfo.NewCmdClusterInfo(f, o.IOStreams),
top.NewCmdTop(f, o.IOStreams),
drain.NewCmdCordon(f, o.IOStreams),
drain.NewCmdUncordon(f, o.IOStreams),
drain.NewCmdDrain(f, o.IOStreams),
taint.NewCmdTaint(f, o.IOStreams),
},
},
{
Message: "Troubleshooting and Debugging Commands:",
Commands: []*cobra.Command{
describe.NewCmdDescribe("kubectl", f, o.IOStreams),
logs.NewCmdLogs(f, o.IOStreams),
attach.NewCmdAttach(f, o.IOStreams),
cmdexec.NewCmdExec(f, o.IOStreams),
portforward.NewCmdPortForward(f, o.IOStreams),
proxyCmd,
cp.NewCmdCp(f, o.IOStreams),
auth.NewCmdAuth(f, o.IOStreams),
debug.NewCmdDebug(f, o.IOStreams),
events.NewCmdEvents(f, o.IOStreams),
},
},
{
Message: "Advanced Commands:",
Commands: []*cobra.Command{
diff.NewCmdDiff(f, o.IOStreams),
apply.NewCmdApply("kubectl", f, o.IOStreams),
patch.NewCmdPatch(f, o.IOStreams),
replace.NewCmdReplace(f, o.IOStreams),
wait.NewCmdWait(f, o.IOStreams),
kustomize.NewCmdKustomize(o.IOStreams),
},
},
{
Message: "Settings Commands:",
Commands: []*cobra.Command{
label.NewCmdLabel(f, o.IOStreams),
annotate.NewCmdAnnotate("kubectl", f, o.IOStreams),
completion.NewCmdCompletion(o.IOStreams.Out, ""),
},
},
}
// 将子命令组添加到父命令中
groups.Add(cmds)
filters := []string{"options"}
// Hide the "alpha" subcommand if there are no alpha commands in this build.
alpha := NewCmdAlpha(f, o.IOStreams)
if !alpha.HasSubCommands() {
filters = append(filters, alpha.Name())
}
templates.ActsAsRootCommand(cmds, filters, groups...)
utilcomp.SetFactoryForCompletion(f)
registerCompletionFuncForGlobalFlags(cmds, f)
// 继续添加一些子命令
cmds.AddCommand(alpha)
cmds.AddCommand(cmdconfig.NewCmdConfig(clientcmd.NewDefaultPathOptions(), o.IOStreams))
cmds.AddCommand(plugin.NewCmdPlugin(o.IOStreams))
cmds.AddCommand(version.NewCmdVersion(f, o.IOStreams))
cmds.AddCommand(apiresources.NewCmdAPIVersions(f, o.IOStreams))
cmds.AddCommand(apiresources.NewCmdAPIResources(f, o.IOStreams))
cmds.AddCommand(options.NewCmdOptions(o.IOStreams.Out))
// Stop warning about normalization of flags. That makes it possible to
// add the klog flags later.
cmds.SetGlobalNormalizationFunc(cliflag.WordSepNormalizeFunc)
return cmds
}
至此,kubectl命令创建完成
cli.RunNoErrOutput(command)
// RunNoErrOutput 是Run的一个版本,返回 cobra command error而不是打印
func RunNoErrOutput(cmd *cobra.Command) error {
_, err := run(cmd)
return err
}
func run(cmd *cobra.Command) (logsInitialized bool, err error) {
rand.Seed(time.Now().UnixNano())
defer logs.FlushLogs()
cmd.SetGlobalNormalizationFunc(cliflag.WordSepNormalizeFunc)
// 开启了,当执行异常时日志输出可读性更好
if !cmd.SilenceUsage {
cmd.SilenceUsage = true
cmd.SetFlagErrorFunc(func(c *cobra.Command, err error) error {
// Re-enable usage printing.
c.SilenceUsage = false
return err
})
}
// In all cases error printing is done below.
cmd.SilenceErrors = true
// This is idempotent.
logs.AddFlags(cmd.PersistentFlags())
// 在PersistentPre*相关函数执行前,初始化日志
switch {
case cmd.PersistentPreRun != nil:
pre := cmd.PersistentPreRun
cmd.PersistentPreRun = func(cmd *cobra.Command, args []string) {
logs.InitLogs()
logsInitialized = true
pre(cmd, args)
}
case cmd.PersistentPreRunE != nil:
pre := cmd.PersistentPreRunE
cmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
logs.InitLogs()
logsInitialized = true
return pre(cmd, args)
}
default:
cmd.PersistentPreRun = func(cmd *cobra.Command, args []string) {
logs.InitLogs()
logsInitialized = true
}
}
// 执行命令
err = cmd.Execute()
return
}
func (c *Command) Execute() error {
_, err := c.ExecuteC()
return err
}
func (c *Command) ExecuteC() (cmd *Command, err error) {
if c.ctx == nil {
c.ctx = context.Background()
}
// 优先执行根命令
if c.HasParent() {
return c.Root().ExecuteC()
}
// windows钩子函数
if preExecHookFn != nil {
preExecHookFn(c)
}
// 初始化默认help命令
c.InitDefaultHelpCmd()
// 初始化默认completion命令
c.InitDefaultCompletionCmd()
args := c.args
// Workaround FAIL with "go test -v" or "cobra.test -test.v", see #155
if c.args == nil && filepath.Base(os.Args[0]) != "cobra.test" {
args = os.Args[1:]
}
// 命令补全
c.initCompleteCmd(args)
var flags []string
if c.TraverseChildren {
cmd, flags, err = c.Traverse(args)
} else {
// 查找子命令,比如执行kubectl get pods命令,这里会找到get命令
cmd, flags, err = c.Find(args)
}
if err != nil {
// `If found parse to a subcommand and then failed, talk about the subcommand`
if cmd != nil {
c = cmd
}
if !c.SilenceErrors {
c.PrintErrln("Error:", err.Error())
c.PrintErrf("Run '%v --help' for usage.\n", c.CommandPath())
}
return c, err
}
cmd.commandCalledAs.called = true
if cmd.commandCalledAs.name == "" {
cmd.commandCalledAs.name = cmd.Name()
}
// 继承父命令的ctx
if cmd.ctx == nil {
cmd.ctx = c.ctx
}
// 执行命令
err = cmd.execute(flags)
if err != nil {
// Always show help if requested, even if SilenceErrors is in
// effect
if errors.Is(err, flag.ErrHelp) {
cmd.HelpFunc()(cmd, args)
return cmd, nil
}
// If root command has SilenceErrors flagged,
// all subcommands should respect it
if !cmd.SilenceErrors && !c.SilenceErrors {
c.PrintErrln("Error:", err.Error())
}
// If root command has SilenceUsage flagged,
// all subcommands should respect it
if !cmd.SilenceUsage && !c.SilenceUsage {
c.Println(cmd.UsageString())
}
}
return cmd, err
}
func (c *Command) execute(a []string) (err error) {
if c == nil {
return fmt.Errorf("Called Execute() on a nil Command")
}
if len(c.Deprecated) > 0 {
c.Printf("Command %q is deprecated, %s\n", c.Name(), c.Deprecated)
}
// initialize help and version flag at the last point possible to allow for user
// overriding
c.InitDefaultHelpFlag()
c.InitDefaultVersionFlag()
err = c.ParseFlags(a)
if err != nil {
return c.FlagErrorFunc()(c, err)
}
// If help is called, regardless of other flags, return we want help.
// Also say we need help if the command isn't runnable.
helpVal, err := c.Flags().GetBool("help")
if err != nil {
// should be impossible to get here as we always declare a help
// flag in InitDefaultHelpFlag()
c.Println("\"help\" flag declared as non-bool. Please correct your code")
return err
}
if helpVal {
return flag.ErrHelp
}
// for back-compat, only add version flag behavior if version is defined
if c.Version != "" {
versionVal, err := c.Flags().GetBool("version")
if err != nil {
c.Println("\"version\" flag declared as non-bool. Please correct your code")
return err
}
if versionVal {
err := tmpl(c.OutOrStdout(), c.VersionTemplate(), c)
if err != nil {
c.Println(err)
}
return err
}
}
if !c.Runnable() {
return flag.ErrHelp
}
// 前置函数
c.preRun()
// 后置函数
defer c.postRun()
argWoFlags := c.Flags().Args()
if c.DisableFlagParsing {
argWoFlags = a
}
if err := c.ValidateArgs(argWoFlags); err != nil {
return err
}
// 运行当前命令以及所有父命令的PersistentPre*函数
for p := c; p != nil; p = p.Parent() {
if p.PersistentPreRunE != nil {
if err := p.PersistentPreRunE(c, argWoFlags); err != nil {
return err
}
break
} else if p.PersistentPreRun != nil {
p.PersistentPreRun(c, argWoFlags)
break
}
}
if c.PreRunE != nil {
if err := c.PreRunE(c, argWoFlags); err != nil {
return err
}
} else if c.PreRun != nil {
c.PreRun(c, argWoFlags)
}
if err := c.ValidateRequiredFlags(); err != nil {
return err
}
if err := c.ValidateFlagGroups(); err != nil {
return err
}
// 真正执行命令的位置,如果是kubectl get pods命令,会执行NewCmdGet函数返回的cmd.Run
if c.RunE != nil {
if err := c.RunE(c, argWoFlags); err != nil {
return err
}
} else {
c.Run(c, argWoFlags)
}
if c.PostRunE != nil {
if err := c.PostRunE(c, argWoFlags); err != nil {
return err
}
} else if c.PostRun != nil {
c.PostRun(c, argWoFlags)
}
// 运行当前命令以及所有父命令的PersistentPost*函数
for p := c; p != nil; p = p.Parent() {
if p.PersistentPostRunE != nil {
if err := p.PersistentPostRunE(c, argWoFlags); err != nil {
return err
}
break
} else if p.PersistentPostRun != nil {
p.PersistentPostRun(c, argWoFlags)
break
}
}
return nil
}
以kubectl get pods命令为例,再看下具体get命令的执行
// NewCmdGet 创建get命令
func NewCmdGet(parent string, f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command {
o := NewGetOptions(parent, streams)
cmd := &cobra.Command{
Use: fmt.Sprintf("get [(-o|--output=)%s] (TYPE[.VERSION][.GROUP] [NAME | -l label] | TYPE[.VERSION][.GROUP]/NAME ...) [flags]", strings.Join(o.PrintFlags.AllowedFormats(), "|")),
DisableFlagsInUseLine: true,
Short: i18n.T("Display one or many resources"),
Long: getLong + "\n\n" + cmdutil.SuggestAPIResources(parent),
Example: getExample,
// ValidArgsFunction is set when this function is called so that we have access to the util package
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate())
cmdutil.CheckErr(o.Run(f, args)) // 执行命令在这里
},
SuggestFor: []string{"list", "ps"},
}
o.PrintFlags.AddFlags(cmd)
cmd.Flags().StringVar(&o.Raw, "raw", o.Raw, "Raw URI to request from the server. Uses the transport specified by the kubeconfig file.")
cmd.Flags().BoolVarP(&o.Watch, "watch", "w", o.Watch, "After listing/getting the requested object, watch for changes.")
cmd.Flags().BoolVar(&o.WatchOnly, "watch-only", o.WatchOnly, "Watch for changes to the requested object(s), without listing/getting first.")
cmd.Flags().BoolVar(&o.OutputWatchEvents, "output-watch-events", o.OutputWatchEvents, "Output watch event objects when --watch or --watch-only is used. Existing objects are output as initial ADDED events.")
cmd.Flags().BoolVar(&o.IgnoreNotFound, "ignore-not-found", o.IgnoreNotFound, "If the requested object does not exist the command will return exit code 0.")
cmd.Flags().StringVar(&o.FieldSelector, "field-selector", o.FieldSelector, "Selector (field query) to filter on, supports '=', '==', and '!='.(e.g. --field-selector key1=value1,key2=value2). The server only supports a limited number of field queries per type.")
cmd.Flags().BoolVarP(&o.AllNamespaces, "all-namespaces", "A", o.AllNamespaces, "If present, list the requested object(s) across all namespaces. Namespace in current context is ignored even if specified with --namespace.")
addServerPrintColumnFlags(cmd, o)
cmdutil.AddFilenameOptionFlags(cmd, &o.FilenameOptions, "identifying the resource to get from a server.")
cmdutil.AddChunkSizeFlag(cmd, &o.ChunkSize)
cmdutil.AddLabelSelectorFlagVar(cmd, &o.LabelSelector)
cmdutil.AddSubresourceFlags(cmd, &o.Subresource, "If specified, gets the subresource of the requested object.", supportedSubresources...)
return cmd
}
func (o *GetOptions) Run(f cmdutil.Factory, args []string) error {
if len(o.Raw) > 0 {
restClient, err := f.RESTClient()
if err != nil {
return err
}
return rawhttp.RawGet(restClient, o.IOStreams, o.Raw)
}
if o.Watch || o.WatchOnly {
return o.watch(f, args)
}
chunkSize := o.ChunkSize
if len(o.SortBy) > 0 {
// TODO(juanvallejo): in the future, we could have the client use chunking
// to gather all results, then sort them all at the end to reduce server load.
chunkSize = 0
}
// 建造者模式,设置各个部分,Do返回一个被装饰的访问者(装饰器模式、访问者模式)
r := f.NewBuilder().
Unstructured().
NamespaceParam(o.Namespace).DefaultNamespace().AllNamespaces(o.AllNamespaces).
FilenameParam(o.ExplicitNamespace, &o.FilenameOptions).
LabelSelectorParam(o.LabelSelector).
FieldSelectorParam(o.FieldSelector).
Subresource(o.Subresource).
RequestChunksOf(chunkSize).
ResourceTypeOrNameArgs(true, args...).
ContinueOnError().
Latest().
Flatten().
TransformRequests(o.transformRequests).
Do()
if o.IgnoreNotFound {
r.IgnoreErrors(apierrors.IsNotFound)
}
if err := r.Err(); err != nil {
return err
}
if !o.IsHumanReadablePrinter {
return o.printGeneric(r)
}
allErrs := []error{}
errs := sets.NewString()
// Infos里调用所有访问者
infos, err := r.Infos()
if err != nil {
allErrs = append(allErrs, err)
}
printWithKind := multipleGVKsRequested(infos)
objs := make([]runtime.Object, len(infos))
for ix := range infos {
objs[ix] = infos[ix].Object
}
var positioner OriginalPositioner
if len(o.SortBy) > 0 {
sorter := NewRuntimeSorter(objs, o.SortBy)
if err := sorter.Sort(); err != nil {
return err
}
positioner = sorter
}
var printer printers.ResourcePrinter
var lastMapping *meta.RESTMapping
// track if we write any output
trackingWriter := &trackingWriterWrapper{Delegate: o.Out}
// output an empty line separating output
separatorWriter := &separatorWriterWrapper{Delegate: trackingWriter}
w := printers.GetNewTabWriter(separatorWriter)
allResourcesNamespaced := !o.AllNamespaces
for ix := range objs {
var mapping *meta.RESTMapping
var info *resource.Info
if positioner != nil {
info = infos[positioner.OriginalPosition(ix)]
mapping = info.Mapping
} else {
info = infos[ix]
mapping = info.Mapping
}
allResourcesNamespaced = allResourcesNamespaced && info.Namespaced()
printWithNamespace := o.AllNamespaces
if mapping != nil && mapping.Scope.Name() == meta.RESTScopeNameRoot {
printWithNamespace = false
}
if shouldGetNewPrinterForMapping(printer, lastMapping, mapping) {
w.Flush()
w.SetRememberedWidths(nil)
// add linebreaks between resource groups (if there is more than one)
// when it satisfies all following 3 conditions:
// 1) it's not the first resource group
// 2) it has row header
// 3) we've written output since the last time we started a new set of headers
if lastMapping != nil && !o.NoHeaders && trackingWriter.Written > 0 {
separatorWriter.SetReady(true)
}
printer, err = o.ToPrinter(mapping, nil, printWithNamespace, printWithKind)
if err != nil {
if !errs.Has(err.Error()) {
errs.Insert(err.Error())
allErrs = append(allErrs, err)
}
continue
}
lastMapping = mapping
}
printer.PrintObj(info.Object, w)
}
w.Flush()
if trackingWriter.Written == 0 && !o.IgnoreNotFound && len(allErrs) == 0 {
// if we wrote no output, and had no errors, and are not ignoring NotFound, be sure we output something
if allResourcesNamespaced {
fmt.Fprintf(o.ErrOut, "No resources found in %s namespace.\n", o.Namespace)
} else {
fmt.Fprintln(o.ErrOut, "No resources found")
}
}
return utilerrors.NewAggregate(allErrs)
}
func (r *Result) Infos() ([]*Info, error) {
if r.err != nil {
return nil, r.err
}
if r.info != nil {
return r.info, nil
}
infos := []*Info{}
// r.visitor首先是个被装饰器装饰的访问者,会遍历所有的visitor
err := r.visitor.Visit(func(info *Info, err error) error {
if err != nil {
return err
}
infos = append(infos, info)
return nil
})
err = utilerrors.FilterOut(err, r.ignoreErrors...)
r.info, r.err = infos, err
return infos, err
}
func (b *Builder) Do() *Result {
// 返回一个包含访问者的结果,这里会包含selector等visitor
r := b.visitorResult()
r.mapper = b.Mapper()
if r.err != nil {
return r
}
if b.flatten {
r.visitor = NewFlattenListVisitor(r.visitor, b.objectTyper, b.mapper)
}
helpers := []VisitorFunc{}
if b.defaultNamespace {
helpers = append(helpers, SetNamespace(b.namespace))
}
if b.requireNamespace {
helpers = append(helpers, RequireNamespace(b.namespace))
}
helpers = append(helpers, FilterNamespace)
if b.requireObject {
helpers = append(helpers, RetrieveLazy)
}
if b.continueOnError {
r.visitor = ContinueOnErrorVisitor{Visitor: r.visitor}
}
// 返回一个被装饰器装饰的访问者
r.visitor = NewDecoratedVisitor(r.visitor, helpers...)
return r
}
func (b *Builder) visitorResult() *Result {
if len(b.errs) > 0 {
return &Result{err: utilerrors.NewAggregate(b.errs)}
}
if b.selectAll {
selector := labels.Everything().String()
b.labelSelector = &selector
}
// visit items specified by paths
if len(b.paths) != 0 {
return b.visitByPaths()
}
// 用selectors访问
if b.labelSelector != nil || b.fieldSelector != nil {
return b.visitBySelector()
}
// visit items specified by resource and name
if len(b.resourceTuples) != 0 {
return b.visitByResource()
}
// visit items specified by name
if len(b.names) != 0 {
return b.visitByName()
}
if len(b.resources) != 0 {
for _, r := range b.resources {
_, err := b.mappingFor(r)
if err != nil {
return &Result{err: err}
}
}
return &Result{err: fmt.Errorf("resource(s) were provided, but no name was specified")}
}
return &Result{err: missingResourceError}
}
func (b *Builder) visitBySelector() *Result {
result := &Result{
targetsSingleItems: false,
}
if len(b.names) != 0 {
return result.withError(fmt.Errorf("name cannot be provided when a selector is specified"))
}
if len(b.resourceTuples) != 0 {
return result.withError(fmt.Errorf("selectors and the all flag cannot be used when passing resource/name arguments"))
}
if len(b.resources) == 0 {
return result.withError(fmt.Errorf("at least one resource must be specified to use a selector"))
}
if len(b.subresource) != 0 {
return result.withError(fmt.Errorf("subresource cannot be used when bulk resources are specified"))
}
mappings, err := b.resourceMappings()
if err != nil {
result.err = err
return result
}
var labelSelector, fieldSelector string
if b.labelSelector != nil {
labelSelector = *b.labelSelector
}
if b.fieldSelector != nil {
fieldSelector = *b.fieldSelector
}
visitors := []Visitor{}
for _, mapping := range mappings {
client, err := b.getClient(mapping.GroupVersionKind.GroupVersion())
if err != nil {
result.err = err
return result
}
selectorNamespace := b.namespace
if mapping.Scope.Name() != meta.RESTScopeNameNamespace {
selectorNamespace = ""
}
// NewSelector返回的Selector访问会发起restful请求
visitors = append(visitors, NewSelector(client, mapping, selectorNamespace, labelSelector, fieldSelector, b.limitChunks))
}
if b.continueOnError {
result.visitor = EagerVisitorList(visitors)
} else {
result.visitor = VisitorList(visitors)
}
result.sources = visitors
return result
}
// Visit implements Visitor and uses request chunking by default.
func (r *Selector) Visit(fn VisitorFunc) error {
helper := NewHelper(r.Client, r.Mapping)
initialOpts := metav1.ListOptions{
LabelSelector: r.LabelSelector,
FieldSelector: r.FieldSelector,
Limit: r.LimitChunks,
}
return FollowContinue(&initialOpts, func(options metav1.ListOptions) (runtime.Object, error) {
// 使用RESTClient发起Get请求
list, err := helper.List(
r.Namespace,
r.ResourceMapping().GroupVersionKind.GroupVersion().String(),
&options,
)
if err != nil {
return nil, EnhanceListError(err, options, r.Mapping.Resource.String())
}
resourceVersion, _ := metadataAccessor.ResourceVersion(list)
info := &Info{
Client: r.Client,
Mapping: r.Mapping,
Namespace: r.Namespace,
ResourceVersion: resourceVersion,
Object: list,
}
if err := fn(info, nil); err != nil {
return nil, err
}
return list, nil
})
}