kubectl源码分析之set env

 欢迎关注我的公众号:

 目前刚开始写一个月,一共写了18篇原创文章,文章目录如下:

istio多集群探秘,部署了50次多集群后我得出的结论

istio多集群链路追踪,附实操视频

istio防故障利器,你知道几个,istio新手不要读,太难!

istio业务权限控制,原来可以这么玩

istio实现非侵入压缩,微服务之间如何实现压缩

不懂envoyfilter也敢说精通istio系列-http-rbac-不要只会用AuthorizationPolicy配置权限

不懂envoyfilter也敢说精通istio系列-02-http-corsFilter-不要只会vs

不懂envoyfilter也敢说精通istio系列-03-http-csrf filter-再也不用再代码里写csrf逻辑了

不懂envoyfilter也敢说精通istio系列http-jwt_authn-不要只会RequestAuthorization

不懂envoyfilter也敢说精通istio系列-05-fault-filter-故障注入不止是vs

不懂envoyfilter也敢说精通istio系列-06-http-match-配置路由不只是vs

不懂envoyfilter也敢说精通istio系列-07-负载均衡配置不止是dr

不懂envoyfilter也敢说精通istio系列-08-连接池和断路器

不懂envoyfilter也敢说精通istio系列-09-http-route filter

不懂envoyfilter也敢说精通istio系列-network filter-redis proxy

不懂envoyfilter也敢说精通istio系列-network filter-HttpConnectionManager

不懂envoyfilter也敢说精通istio系列-ratelimit-istio ratelimit完全手册

 

加qq群,请联系:


————————————————

type EnvOptions struct {//set env结构体
	PrintFlags *genericclioptions.PrintFlags
	resource.FilenameOptions

	EnvParams         []string
	All               bool
	Resolve           bool
	List              bool
	Local             bool
	Overwrite         bool
	ContainerSelector string
	Selector          string
	From              string
	Prefix            string
	Keys              []string

	PrintObj printers.ResourcePrinterFunc

	envArgs                []string
	resources              []string
	output                 string
	dryRun                 bool
	builder                func() *resource.Builder
	updatePodSpecForObject polymorphichelpers.UpdatePodSpecForObjectFunc
	namespace              string
	enforceNamespace       bool
	clientset              *kubernetes.Clientset

	genericclioptions.IOStreams
}
func NewEnvOptions(streams genericclioptions.IOStreams) *EnvOptions {
	return &EnvOptions{//初始化结构体
		PrintFlags: genericclioptions.NewPrintFlags("env updated").WithTypeSetter(scheme.Scheme),

		ContainerSelector: "*",
		Overwrite:         true,

		IOStreams: streams,
	}
}
//创建set env命令
func NewCmdEnv(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command {
	o := NewEnvOptions(streams)//初始化结构体
	cmd := &cobra.Command{//创建cobra命令
		Use:                   "env RESOURCE/NAME KEY_1=VAL_1 ... KEY_N=VAL_N",
		DisableFlagsInUseLine: true,
		Short:                 "Update environment variables on a pod template",
		Long:                  envLong,
		Example:               fmt.Sprintf(envExample),
		Run: func(cmd *cobra.Command, args []string) {
			cmdutil.CheckErr(o.Complete(f, cmd, args))//准备
			cmdutil.CheckErr(o.Validate())//校验
			cmdutil.CheckErr(o.RunEnv())//运行
		},
	}
	usage := "the resource to update the env"
	cmdutil.AddFilenameOptionFlags(cmd, &o.FilenameOptions, usage)//设置文件选项
	cmd.Flags().StringVarP(&o.ContainerSelector, "containers", "c", o.ContainerSelector, "The names of containers in the selected pod templates to change - may use wildcards")//设置container选项
	cmd.Flags().StringVarP(&o.From, "from", "", "", "The name of a resource from which to inject environment variables")//设置from选项
	cmd.Flags().StringVarP(&o.Prefix, "prefix", "", "", "Prefix to append to variable names")//设置prefix选项
	cmd.Flags().StringArrayVarP(&o.EnvParams, "env", "e", o.EnvParams, "Specify a key-value pair for an environment variable to set into each container.")//设置env选项
	cmd.Flags().StringSliceVarP(&o.Keys, "keys", "", o.Keys, "Comma-separated list of keys to import from specified resource")//设置keys选项
	cmd.Flags().BoolVar(&o.List, "list", o.List, "If true, display the environment and any changes in the standard format. this flag will removed when we have kubectl view env.")//设置list选项
	cmd.Flags().BoolVar(&o.Resolve, "resolve", o.Resolve, "If true, show secret or configmap references when listing variables")//设置resolve选项
	cmd.Flags().StringVarP(&o.Selector, "selector", "l", o.Selector, "Selector (label query) to filter on")//设置selector选项
	cmd.Flags().BoolVar(&o.Local, "local", o.Local, "If true, set env will NOT contact api-server but run locally.")//设置local选项
	cmd.Flags().BoolVar(&o.All, "all", o.All, "If true, select all resources in the namespace of the specified resource types")//设置all选项
	cmd.Flags().BoolVar(&o.Overwrite, "overwrite", o.Overwrite, "If true, allow environment to be overwritten, otherwise reject updates that overwrite existing environment.")//设置overrite选项

	o.PrintFlags.AddFlags(cmd)//设置打印选项

	cmdutil.AddDryRunFlag(cmd)//设置干跑选项
	return cmd
}
//准备
func (o *EnvOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
	if o.All && len(o.Selector) > 0 {//all和selector不能同时存在
		return fmt.Errorf("cannot set --all and --selector at the same time")
	}
	ok := false
	o.resources, o.envArgs, ok = envutil.SplitEnvironmentFromResources(args)//获取resource和env
	if !ok {
		return fmt.Errorf("all resources must be specified before environment changes: %s", strings.Join(args, " "))
	}

	o.updatePodSpecForObject = polymorphichelpers.UpdatePodSpecForObjectFn//设置updatePodSpecForObject函数
	o.output = cmdutil.GetFlagString(cmd, "output")//设置output
	o.dryRun = cmdutil.GetDryRunFlag(cmd)//设置干跑

	if o.dryRun {
		// TODO(juanvallejo): This can be cleaned up even further by creating
		// a PrintFlags struct that binds the --dry-run flag, and whose
		// ToPrinter method returns a printer that understands how to print
		// this success message.
		o.PrintFlags.Complete("%s (dry run)")
	}
	printer, err := o.PrintFlags.ToPrinter()//printflag转printer
	if err != nil {
		return err
	}
	o.PrintObj = printer.PrintObj//设置printObj函数

	o.clientset, err = f.KubernetesClientSet()//设置clientSet
	if err != nil {
		return err
	}
	o.namespace, o.enforceNamespace, err = f.ToRawKubeConfigLoader().Namespace()//设置namespace和enforceNamespace
	if err != nil {
		return err
	}
	o.builder = f.NewBuilder//设置builder

	return nil
}
func (o *EnvOptions) Validate() error {
	if len(o.Filenames) == 0 && len(o.resources) < 1 {//文件和资源至少有一个
		return fmt.Errorf("one or more resources must be specified as <resource> <name> or <resource>/<name>")
	}
	if o.List && len(o.output) > 0 {//list和output不能同时指定
		return fmt.Errorf("--list and --output may not be specified together")
	}
	if len(o.Keys) > 0 && len(o.From) == 0 {指定keys的时候必须指定from
		return fmt.Errorf("when specifying --keys, a configmap or secret must be provided with --from")
	}
	return nil
}
//运行
func (o *EnvOptions) RunEnv() error {
	env, remove, err := envutil.ParseEnv(append(o.EnvParams, o.envArgs...), o.In)//解析env
	if err != nil {
		return err
	}

	if len(o.From) != 0 {
		b := o.builder().
			WithScheme(scheme.Scheme, scheme.Scheme.PrioritizedVersionsAllGroups()...).
			LocalParam(o.Local).
			ContinueOnError().
			NamespaceParam(o.namespace).DefaultNamespace().
			FilenameParam(o.enforceNamespace, &o.FilenameOptions).
			Flatten()//构造from的info对象

		if !o.Local {
			b = b.
				LabelSelectorParam(o.Selector).
				ResourceTypeOrNameArgs(o.All, o.From).
				Latest()
		}

		infos, err := b.Do().Infos()//获取from info对象
		if err != nil {
			return err
		}

		for _, info := range infos {//遍历from info对象
			switch from := info.Object.(type) {
			case *v1.Secret://如果类型是secret
				for key := range from.Data {//遍历key
					if contains(key, o.Keys) {//如果keys包含key
						envVar := v1.EnvVar{
							Name: keyToEnvName(key),
							ValueFrom: &v1.EnvVarSource{
								SecretKeyRef: &v1.SecretKeySelector{
									LocalObjectReference: v1.LocalObjectReference{
										Name: from.Name,
									},
									Key: key,
								},
							},
						}//构造env
						env = append(env, envVar)//append env
					}
				}
			case *v1.ConfigMap://如果类型是configMap
				for key := range from.Data {//遍历key
					if contains(key, o.Keys) {//如果keys包含key
						envVar := v1.EnvVar{
							Name: keyToEnvName(key),
							ValueFrom: &v1.EnvVarSource{
								ConfigMapKeyRef: &v1.ConfigMapKeySelector{
									LocalObjectReference: v1.LocalObjectReference{
										Name: from.Name,
									},
									Key: key,
								},
							},
						}//构造env
						env = append(env, envVar)//append env
					}
				}
			default:
				return fmt.Errorf("unsupported resource specified in --from")
			}
		}
	}

	if len(o.Prefix) != 0 {//如果prefix不为空
		for i := range env {
			env[i].Name = fmt.Sprintf("%s%s", o.Prefix, env[i].Name)//env名称加prefix
		}
	}

	b := o.builder().
		WithScheme(scheme.Scheme, scheme.Scheme.PrioritizedVersionsAllGroups()...).
		LocalParam(o.Local).
		ContinueOnError().
		NamespaceParam(o.namespace).DefaultNamespace().
		FilenameParam(o.enforceNamespace, &o.FilenameOptions).
		Flatten()//构造resource 的info对象

	if !o.Local {
		b.LabelSelectorParam(o.Selector).
			ResourceTypeOrNameArgs(o.All, o.resources...).
			Latest()
	}

	infos, err := b.Do().Infos()//获取info对象
	if err != nil {
		return err
	}
	patches := CalculatePatches(infos, scheme.DefaultJSONEncoder(), func(obj runtime.Object) ([]byte, error) {//计算patch
		_, err := o.updatePodSpecForObject(obj, func(spec *v1.PodSpec) error {//更新pod spec
			resolutionErrorsEncountered := false
			containers, _ := selectContainers(spec.Containers, o.ContainerSelector)//选择容器
			objName, err := meta.NewAccessor().Name(obj)//获取对象名称
			if err != nil {
				return err
			}

			gvks, _, err := scheme.Scheme.ObjectKinds(obj)//从obj获取groupversionkind
			if err != nil {
				return err
			}
			objKind := obj.GetObjectKind().GroupVersionKind().Kind//获取kind
			if len(objKind) == 0 {//如果kind为空,则从gvks获取
				for _, gvk := range gvks {
					if len(gvk.Kind) == 0 {
						continue
					}
					if len(gvk.Version) == 0 || gvk.Version == runtime.APIVersionInternal {
						continue
					}

					objKind = gvk.Kind
					break
				}
			}

			if len(containers) == 0 {//如果容器为空
				if gvks, _, err := scheme.Scheme.ObjectKinds(obj); err == nil {//获取gvks
					objKind := obj.GetObjectKind().GroupVersionKind().Kind//获取kind
					if len(objKind) == 0 {//如果kind为空则从gvks获取
						for _, gvk := range gvks {
							if len(gvk.Kind) == 0 {
								continue
							}
							if len(gvk.Version) == 0 || gvk.Version == runtime.APIVersionInternal {
								continue
							}

							objKind = gvk.Kind
							break
						}
					}

					fmt.Fprintf(o.ErrOut, "warning: %s/%s does not have any containers matching %q\n", objKind, objName, o.ContainerSelector)//打印错误返回
				}
				return nil
			}
			for _, c := range containers {//遍历容器
				if !o.Overwrite {//如果overrite为false,校验env是否有覆盖
					if err := validateNoOverwrites(c.Env, env); err != nil {
						return err
					}
				}

				c.Env = updateEnv(c.Env, env, remove)//更新env
				if o.List {//如果list为true
					resolveErrors := map[string][]string{}
					store := envutil.NewResourceStore()

					fmt.Fprintf(o.Out, "# %s %s, container %s\n", objKind, objName, c.Name)
					for _, env := range c.Env {//遍历env
						// Print the simple value
						if env.ValueFrom == nil {//打印env
							fmt.Fprintf(o.Out, "%s=%s\n", env.Name, env.Value)
							continue
						}

						// Print the reference version
						if !o.Resolve {//如果resolve为false,不解析env,打印
							fmt.Fprintf(o.Out, "# %s from %s\n", env.Name, envutil.GetEnvVarRefString(env.ValueFrom))
							continue
						}

						value, err := envutil.GetEnvVarRefValue(o.clientset, o.namespace, store, env.ValueFrom, obj, c)//解析env
						// Print the resolved value
						if err == nil {//打印解析后的env
							fmt.Fprintf(o.Out, "%s=%s\n", env.Name, value)
							continue
						}

						// Print the reference version and save the resolve error
						fmt.Fprintf(o.Out, "# %s from %s\n", env.Name, envutil.GetEnvVarRefString(env.ValueFrom))//解析错误,打印未解析env
						errString := err.Error()
						resolveErrors[errString] = append(resolveErrors[errString], env.Name)
						resolutionErrorsEncountered = true
					}

					// Print any resolution errors
					errs := []string{}
					for err, vars := range resolveErrors {
						sort.Strings(vars)
						errs = append(errs, fmt.Sprintf("error retrieving reference for %s: %v", strings.Join(vars, ", "), err))//组装error
					}
					sort.Strings(errs)//排序error
					for _, err := range errs {//打印错误
						fmt.Fprintln(o.ErrOut, err)
					}
				}
			}
			if resolutionErrorsEncountered {//解析错误返回
				return errors.New("failed to retrieve valueFrom references")
			}
			return nil
		})

		if err == nil {
			return runtime.Encode(scheme.DefaultJSONEncoder(), obj)//obj转json
		}
		return nil, err
	})

	if o.List {
		return nil
	}

	allErrs := []error{}

	for _, patch := range patches {//遍历patch
		info := patch.Info
		if patch.Err != nil {
			name := info.ObjectName()
			allErrs = append(allErrs, fmt.Errorf("error: %s %v\n", name, patch.Err))
			continue
		}

		// no changes
		if string(patch.Patch) == "{}" || len(patch.Patch) == 0 {
			continue
		}

		if o.Local || o.dryRun {//loal或干跑,打印结果继续
			if err := o.PrintObj(info.Object, o.Out); err != nil {
				allErrs = append(allErrs, err)
			}
			continue
		}

		actual, err := resource.NewHelper(info.Client, info.Mapping).Patch(info.Namespace, info.Name, types.StrategicMergePatchType, patch.Patch, nil)//应用patch到服务端
		if err != nil {
			allErrs = append(allErrs, fmt.Errorf("failed to patch env update to pod template: %v", err))
			continue
		}

		// make sure arguments to set or replace environment variables are set
		// before returning a successful message
		if len(env) == 0 && len(o.envArgs) == 0 {//如果env和envArgs为空则报错
			return fmt.Errorf("at least one environment variable must be provided")
		}

		if err := o.PrintObj(actual, o.Out); err != nil {//打印结果
			allErrs = append(allErrs, err)
		}
	}
	return utilerrors.NewAggregate(allErrs)
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hxpjava1

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值