// NewCloudControllerManagerOptionsWithProviderDefaults creates a new
// ExternalCMServer with a default config, but allows the cloud provider to
// override a select number of default option values.
func NewCloudControllerManagerOptionsWithProviderDefaults(defaults ProviderDefaults) (*CloudControllerManagerOptions, error) {
	componentConfig, err := NewDefaultComponentConfig()
	if err != nil {
		return nil, err
	}

	s := CloudControllerManagerOptions{
		Generic:         cmoptions.NewGenericControllerManagerConfigurationOptions(&componentConfig.Generic),
		KubeCloudShared: NewKubeCloudSharedOptions(&componentConfig.KubeCloudShared),
		NodeController: &NodeControllerOptions{
			NodeControllerConfiguration: &componentConfig.NodeController,
		},
		ServiceController: &ServiceControllerOptions{
			ServiceControllerConfiguration: &componentConfig.ServiceController,
		},
		SecureServing:             apiserveroptions.NewSecureServingOptions().WithLoopback(),
		Webhook:                   NewWebhookOptions(),
		WebhookServing:            NewWebhookServingOptions(defaults),
		Authentication:            apiserveroptions.NewDelegatingAuthenticationOptions(),
		Authorization:             apiserveroptions.NewDelegatingAuthorizationOptions(),
		NodeStatusUpdateFrequency: componentConfig.NodeStatusUpdateFrequency,
	}

	s.Authentication.RemoteKubeConfigFileOptional = true
	s.Authorization.RemoteKubeConfigFileOptional = true

	// Set the PairName but leave certificate directory blank to generate in-memory by default
	s.SecureServing.ServerCert.CertDirectory = ""
	s.SecureServing.ServerCert.PairName = "cloud-controller-manager"
	s.SecureServing.BindPort = cloudprovider.CloudControllerManagerPort

	s.Generic.LeaderElection.ResourceName = "cloud-controller-manager"
	s.Generic.LeaderElection.ResourceNamespace = "kube-system"

	return &s, nil
}


NewDefaultComponentConfig

// NewDefaultComponentConfig returns cloud-controller manager configuration object.
func NewDefaultComponentConfig() (*ccmconfig.CloudControllerManagerConfiguration, error) {
	versioned := &ccmconfigv1alpha1.CloudControllerManagerConfiguration{}
	ccmconfigscheme.Scheme.Default(versioned)

	internal := &ccmconfig.CloudControllerManagerConfiguration{}
	if err := ccmconfigscheme.Scheme.Convert(versioned, internal, nil); err != nil {
		return nil, err
	}

	return internal, nil
}


Flags

// Flags returns flags for a specific CloudController by section name
func (o *CloudControllerManagerOptions) Flags(allControllers, disabledByDefaultControllers, allWebhooks, disabledByDefaultWebhooks []string) cliflag.NamedFlagSets {
	fss := cliflag.NamedFlagSets{}
	o.Generic.AddFlags(&fss, allControllers, disabledByDefaultControllers)
	o.KubeCloudShared.AddFlags(fss.FlagSet("generic"))
	o.NodeController.AddFlags(fss.FlagSet("node controller"))
	o.ServiceController.AddFlags(fss.FlagSet("service controller"))
	if o.Webhook != nil {
		o.Webhook.AddFlags(fss.FlagSet("webhook"), allWebhooks, disabledByDefaultWebhooks)
	}
	if o.WebhookServing != nil {
		o.WebhookServing.AddFlags(fss.FlagSet("webhook serving"))
	}

	o.SecureServing.AddFlags(fss.FlagSet("secure serving"))
	o.Authentication.AddFlags(fss.FlagSet("authentication"))
	o.Authorization.AddFlags(fss.FlagSet("authorization"))

	fs := fss.FlagSet("misc")
	fs.StringVar(&o.Master, "master", o.Master, "The address of the Kubernetes API server (overrides any value in kubeconfig).")
	fs.StringVar(&o.Generic.ClientConnection.Kubeconfig, "kubeconfig", o.Generic.ClientConnection.Kubeconfig, "Path to kubeconfig file with authorization and master location information (the master location can be overridden by the master flag).")
	fs.DurationVar(&o.NodeStatusUpdateFrequency.Duration, "node-status-update-frequency", o.NodeStatusUpdateFrequency.Duration, "Specifies how often the controller updates nodes' status.")
	utilfeature.DefaultMutableFeatureGate.AddFlag(fss.FlagSet("generic"))

	return fss
}


ApplyTo

// ApplyTo fills up cloud controller manager config with options.
func (o *CloudControllerManagerOptions) ApplyTo(c *config.Config, userAgent string) error {
	var err error

	// Build kubeconfig first to so that if it fails, it doesn't cause leaking
	// goroutines (started from initializing secure serving - which underneath
	// creates a queue which in its constructor starts a goroutine).
	c.Kubeconfig, err = clientcmd.BuildConfigFromFlags(o.Master, o.Generic.ClientConnection.Kubeconfig)
	if err != nil {
		return err
	}
	c.Kubeconfig.DisableCompression = true
	c.Kubeconfig.ContentConfig.AcceptContentTypes = o.Generic.ClientConnection.AcceptContentTypes
	c.Kubeconfig.ContentConfig.ContentType = o.Generic.ClientConnection.ContentType
	c.Kubeconfig.QPS = o.Generic.ClientConnection.QPS
	c.Kubeconfig.Burst = int(o.Generic.ClientConnection.Burst)

	if err = o.Generic.ApplyTo(&c.ComponentConfig.Generic); err != nil {
		return err
	}
	if err = o.KubeCloudShared.ApplyTo(&c.ComponentConfig.KubeCloudShared); err != nil {
		return err
	}
	if err = o.ServiceController.ApplyTo(&c.ComponentConfig.ServiceController); err != nil {
		return err
	}
	if o.Webhook != nil {
		if err = o.Webhook.ApplyTo(&c.ComponentConfig.Webhook); err != nil {
			return err
		}
	}
	if o.WebhookServing != nil {
		if err = o.WebhookServing.ApplyTo(&c.WebhookSecureServing); err != nil {
			return err
		}
	}
	if err = o.SecureServing.ApplyTo(&c.SecureServing, &c.LoopbackClientConfig); err != nil {
		return err
	}
	if o.SecureServing.BindPort != 0 || o.SecureServing.Listener != nil {
		if err = o.Authentication.ApplyTo(&c.Authentication, c.SecureServing, nil); err != nil {
			return err
		}
		if err = o.Authorization.ApplyTo(&c.Authorization); err != nil {
			return err
		}
	}

	c.Client, err = clientset.NewForConfig(restclient.AddUserAgent(c.Kubeconfig, userAgent))
	if err != nil {
		return err
	}

	c.EventBroadcaster = record.NewBroadcaster()
	c.EventRecorder = c.EventBroadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: userAgent})

	rootClientBuilder := clientbuilder.SimpleControllerClientBuilder{
		ClientConfig: c.Kubeconfig,
	}
	if c.ComponentConfig.KubeCloudShared.UseServiceAccountCredentials {
		c.ClientBuilder = clientbuilder.NewDynamicClientBuilder(
			restclient.AnonymousClientConfig(c.Kubeconfig),
			c.Client.CoreV1(),
			metav1.NamespaceSystem)
	} else {
		c.ClientBuilder = rootClientBuilder
	}
	c.VersionedClient = rootClientBuilder.ClientOrDie("shared-informers")
	c.SharedInformers = informers.NewSharedInformerFactory(c.VersionedClient, resyncPeriod(c)())

	// sync back to component config
	// TODO: find more elegant way than syncing back the values.
	c.ComponentConfig.NodeStatusUpdateFrequency = o.NodeStatusUpdateFrequency
	c.ComponentConfig.NodeController.ConcurrentNodeSyncs = o.NodeController.ConcurrentNodeSyncs

	return nil
}


Validate

// Validate is used to validate config before launching the cloud controller manager
func (o *CloudControllerManagerOptions) Validate(allControllers, disabledByDefaultControllers, allWebhooks, disabledByDefaultWebhooks []string) error {
	errors := []error{}

	errors = append(errors, o.Generic.Validate(allControllers, disabledByDefaultControllers)...)
	errors = append(errors, o.KubeCloudShared.Validate()...)
	errors = append(errors, o.ServiceController.Validate()...)
	errors = append(errors, o.SecureServing.Validate()...)
	errors = append(errors, o.Authentication.Validate()...)
	errors = append(errors, o.Authorization.Validate()...)

	if o.Webhook != nil {
		errors = append(errors, o.Webhook.Validate(allWebhooks, disabledByDefaultWebhooks)...)
	}
	if o.WebhookServing != nil {
		errors = append(errors, o.WebhookServing.Validate()...)

		if o.WebhookServing.BindPort == o.SecureServing.BindPort {
			errors = append(errors, fmt.Errorf("--webhook-secure-port cannot be the same value as --secure-port"))
		}
	}
	if len(o.KubeCloudShared.CloudProvider.Name) == 0 {
		errors = append(errors, fmt.Errorf("--cloud-provider cannot be empty"))
	}

	return utilerrors.NewAggregate(errors)
}


resyncPeriod

// resyncPeriod computes the time interval a shared informer waits before resyncing with the api server
func resyncPeriod(c *config.Config) func() time.Duration {
	return func() time.Duration {
		factor := rand.Float64() + 1
		return time.Duration(float64(c.ComponentConfig.Generic.MinResyncPeriod.Nanoseconds()) * factor)
	}
}


Config

// Config return a cloud controller manager config objective
func (o *CloudControllerManagerOptions) Config(allControllers, disabledByDefaultControllers, allWebhooks, disabledByDefaultWebhooks []string) (*config.Config, error) {
	if err := o.Validate(allControllers, disabledByDefaultControllers, allWebhooks, disabledByDefaultWebhooks); err != nil {
		return nil, err
	}

	if err := o.SecureServing.MaybeDefaultWithSelfSignedCerts("localhost", nil, []net.IP{netutils.ParseIPSloppy("127.0.0.1")}); err != nil {
		return nil, fmt.Errorf("error creating self-signed certificates: %v", err)
	}

	if o.WebhookServing != nil {
		if err := o.WebhookServing.MaybeDefaultWithSelfSignedCerts("localhost", nil, []net.IP{netutils.ParseIPSloppy("127.0.0.1")}); err != nil {
			return nil, fmt.Errorf("error creating self-signed certificates for webhook: %v", err)
		}
	}

	c := &config.Config{}
	if err := o.ApplyTo(c, CloudControllerManagerUserAgent); err != nil {
		return nil, err
	}

	return c, nil
}


AddFlags

// AddFlags adds flags related to ServiceController for controller manager to the specified FlagSet.
func (o *ServiceControllerOptions) AddFlags(fs *pflag.FlagSet) {
	if o == nil {
		return
	}

	fs.Int32Var(&o.ConcurrentServiceSyncs, "concurrent-service-syncs", o.ConcurrentServiceSyncs, "The number of services that are allowed to sync concurrently. Larger number = more responsive service management, but more CPU (and network) load")
}


ApplyTo

// ApplyTo fills up ServiceController config with options.
func (o *ServiceControllerOptions) ApplyTo(cfg *serviceconfig.ServiceControllerConfiguration) error {
	if o == nil {
		return nil
	}

	cfg.ConcurrentServiceSyncs = o.ConcurrentServiceSyncs

	return nil
}


Validate

// Validate checks validation of ServiceControllerOptions.
func (o *ServiceControllerOptions) Validate() []error {
	if o == nil {
		return nil
	}

	errs := []error{}
	return errs
}


NewWebhookOptions

func NewWebhookOptions() *WebhookOptions {
	o := &WebhookOptions{}
	return o
}


AddFlags

func (o *WebhookOptions) AddFlags(fs *pflag.FlagSet, allWebhooks, disabledByDefaultWebhooks []string) {
	fs.StringSliceVar(&o.Webhooks, "webhooks", o.Webhooks, fmt.Sprintf(""+
		"A list of webhooks to enable. '*' enables all on-by-default webhooks, 'foo' enables the webhook "+
		"named 'foo', '-foo' disables the webhook named 'foo'.\nAll webhooks: %s\nDisabled-by-default webhooks: %s",
		strings.Join(allWebhooks, ", "), strings.Join(disabledByDefaultWebhooks, ", ")))
}


Validate

func (o *WebhookOptions) Validate(allWebhooks, disabledByDefaultWebhooks []string) []error {
	allErrors := []error{}

	allWebhooksSet := sets.NewString(allWebhooks...)
	toValidate := sets.NewString(o.Webhooks...)
	toValidate.Insert(disabledByDefaultWebhooks...)
	for _, webhook := range toValidate.List() {
		if webhook == "*" {
			continue
		}
		webhook = strings.TrimPrefix(webhook, "-")
		if !allWebhooksSet.Has(webhook) {
			allErrors = append(allErrors, fmt.Errorf("%q is not in the list of known webhooks", webhook))
		}
	}

	return allErrors
}


ApplyTo

func (o *WebhookOptions) ApplyTo(cfg *config.WebhookConfiguration) error {
	if o == nil {
		return nil
	}

	cfg.Webhooks = o.Webhooks

	return nil
}


NewWebhookServingOptions

func NewWebhookServingOptions(defaults ProviderDefaults) *WebhookServingOptions {
	var (
		bindAddress net.IP
		bindPort    int
	)

	if defaults.WebhookBindAddress != nil {
		bindAddress = *defaults.WebhookBindAddress
	} else {
		bindAddress = netutils.ParseIPSloppy("0.0.0.0")
	}

	if defaults.WebhookBindPort != nil {
		bindPort = *defaults.WebhookBindPort
	} else {
		bindPort = CloudControllerManagerWebhookPort
	}

	return &WebhookServingOptions{
		SecureServingOptions: &apiserveroptions.SecureServingOptions{
			BindAddress: bindAddress,
			BindPort:    bindPort,
			ServerCert: apiserveroptions.GeneratableKeyCert{
				CertDirectory: "",
				PairName:      "cloud-controller-manager-webhook",
			},
		},
	}
}


AddFlags

func (o *WebhookServingOptions) AddFlags(fs *pflag.FlagSet) {
	fs.IPVar(&o.BindAddress, "webhook-bind-address", o.BindAddress, ""+
		"The IP address on which to listen for the --webhook-secure-port port. The "+
		"associated interface(s) must be reachable by the rest of the cluster, and by CLI/web "+
		fmt.Sprintf("clients. If set to an unspecified address (0.0.0.0 or ::), all interfaces will be used. If unset, defaults to %v.", o.BindAddress))

	fs.IntVar(&o.BindPort, "webhook-secure-port", o.BindPort, fmt.Sprintf("Secure port to serve cloud provider webhooks. If unset, defaults to %d.", o.BindPort))

	fs.StringVar(&o.ServerCert.CertDirectory, "webhook-cert-dir", o.ServerCert.CertDirectory, ""+
		"The directory where the TLS certs are located. "+
		"If --tls-cert-file and --tls-private-key-file are provided, this flag will be ignored.")

	fs.StringVar(&o.ServerCert.CertKey.CertFile, "webhook-tls-cert-file", o.ServerCert.CertKey.CertFile, ""+
		"File containing the default x509 Certificate for HTTPS. (CA cert, if any, concatenated "+
		"after server cert). If HTTPS serving is enabled, and --tls-cert-file and "+
		"--tls-private-key-file are not provided, a self-signed certificate and key "+
		"are generated for the public address and saved to the directory specified by --cert-dir.")

	fs.StringVar(&o.ServerCert.CertKey.KeyFile, "webhook-tls-private-key-file", o.ServerCert.CertKey.KeyFile,
		"File containing the default x509 private key matching --tls-cert-file.")
}


Validate

func (o *WebhookServingOptions) Validate() []error {
	allErrors := []error{}
	if o.BindPort < 0 || o.BindPort > 65535 {
		allErrors = append(allErrors, fmt.Errorf("--webhook-secure-port %v must be between 0 and 65535, inclusive. A value of 0 disables the webhook endpoint entirely.", o.BindPort))
	}

	if (len(o.ServerCert.CertKey.CertFile) != 0 || len(o.ServerCert.CertKey.KeyFile) != 0) && o.ServerCert.GeneratedCert != nil {
		allErrors = append(allErrors, fmt.Errorf("cert/key file and in-memory certificate cannot both be set"))
	}

	return allErrors
}


ApplyTo

func (o *WebhookServingOptions) ApplyTo(cfg **server.SecureServingInfo) error {
	if o == nil {
		return nil
	}

	if o.BindPort <= 0 {
		return nil
	}

	var err error
	var listener net.Listener
	addr := net.JoinHostPort(o.BindAddress.String(), strconv.Itoa(o.BindPort))

	l := net.ListenConfig{}

	listener, o.BindPort, err = createListener(addr, l)
	if err != nil {
		return fmt.Errorf("failed to create listener: %v", err)
	}

	*cfg = &server.SecureServingInfo{
		Listener: listener,
	}

	serverCertFile, serverKeyFile := o.ServerCert.CertKey.CertFile, o.ServerCert.CertKey.KeyFile
	if len(serverCertFile) != 0 || len(serverKeyFile) != 0 {
		var err error
		(*cfg).Cert, err = dynamiccertificates.NewDynamicServingContentFromFiles("serving-cert", serverCertFile, serverKeyFile)
		if err != nil {
			return err
		}
	} else if o.ServerCert.GeneratedCert != nil {
		(*cfg).Cert = o.ServerCert.GeneratedCert
	}

	return nil
}


createListener

func createListener(addr string, config net.ListenConfig) (net.Listener, int, error) {
	ln, err := config.Listen(context.TODO(), "tcp", addr)
	if err != nil {
		return nil, 0, fmt.Errorf("failed to listen on %v: %v", addr, err)
	}

	// get port
	tcpAddr, ok := ln.Addr().(*net.TCPAddr)
	if !ok {
		ln.Close()
		return nil, 0, fmt.Errorf("invalid listen address: %q", ln.Addr().String())
	}

	return ln, tcpAddr.Port, nil
}


RegisterCloudProvider

// RegisterCloudProvider registers a cloudprovider.Factory by name.  This
// is expected to happen during app startup.
func RegisterCloudProvider(name string, cloud Factory) {
	providersMutex.Lock()
	defer providersMutex.Unlock()
	if _, found := providers[name]; found {
		klog.Fatalf("Cloud provider %q was registered twice", name)
	}
	klog.V(1).Infof("Registered cloud provider %q", name)
	providers[name] = cloud
}


IsCloudProvider

// IsCloudProvider returns true if name corresponds to an already registered
// cloud provider.
func IsCloudProvider(name string) bool {
	providersMutex.Lock()
	defer providersMutex.Unlock()
	_, found := providers[name]
	return found
}


GetCloudProvider

// GetCloudProvider creates an instance of the named cloud provider, or nil if
// the name is unknown.  The error return is only used if the named provider
// was known but failed to initialize. The config parameter specifies the
// io.Reader handler of the configuration file for the cloud provider, or nil
// for no configuration.
func GetCloudProvider(name string, config io.Reader) (Interface, error) {
	providersMutex.Lock()
	defer providersMutex.Unlock()
	f, found := providers[name]
	if !found {
		return nil, nil
	}
	return f(config)
}


IsExternal

// Detects if the string is an external cloud provider
func IsExternal(name string) bool {
	return name == externalCloudProvider
}


IsDeprecatedInternal

// IsDeprecatedInternal is responsible for preventing cloud.Interface
// from being initialized in kubelet, kube-controller-manager or kube-api-server
func IsDeprecatedInternal(name string) bool {
	for _, provider := range deprecatedCloudProviders {
		if provider.name == name {
			return true
		}
	}

	return false
}


DisableWarningForProvider

// DisableWarningForProvider logs information about disabled cloud provider state
func DisableWarningForProvider(providerName string) {
	for _, provider := range deprecatedCloudProviders {
		if provider.name == providerName {
			klog.Infof("INFO: Please make sure you are running external cloud controller manager binary for provider %q."+
				"In-tree cloud providers are currently disabled. Refer to https://github.com/kubernetes/kubernetes/tree/master/staging/src/k8s.io/cloud-provider/sample"+
				"for example implementation.", providerName)
			detail := fmt.Sprintf("Please reach to sig-cloud-provider and use 'external' cloud provider for %q: %s", providerName, provider.detail)
			klog.Warningf("WARNING: %q built-in cloud provider is now disabled. %s", providerName, detail)
			break
		}
	}
}


DeprecationWarningForProvider

// DeprecationWarningForProvider logs information about deprecated cloud provider state
func DeprecationWarningForProvider(providerName string) {
	for _, provider := range deprecatedCloudProviders {
		if provider.name != providerName {
			continue
		}

		detail := provider.detail
		if provider.external {
			detail = fmt.Sprintf("Please use 'external' cloud provider for %s: %s", providerName, provider.detail)
		}

		klog.Warningf("WARNING: %s built-in cloud provider is now deprecated. %s", providerName, detail)
		break
	}
}


InitCloudProvider

// InitCloudProvider creates an instance of the named cloud provider.
func InitCloudProvider(name string, configFilePath string) (Interface, error) {
	var cloud Interface
	var err error

	if name == "" {
		return nil, nil
	}

	if IsExternal(name) {
		klog.Info("External cloud provider specified")
		return nil, nil
	}

	if configFilePath != "" {
		var config *os.File
		config, err = os.Open(configFilePath)
		if err != nil {
			klog.Fatalf("Couldn't open cloud provider configuration %s: %#v",
				configFilePath, err)
		}

		defer config.Close()
		cloud, err = GetCloudProvider(name, config)
	} else {
		// Pass explicit nil so plugins can actually check for nil. See
		// "Why is my nil error value not equal to nil?" in golang.org/doc/faq.
		cloud, err = GetCloudProvider(name, nil)
	}

	if err != nil {
		return nil, fmt.Errorf("could not init cloud provider %q: %v", name, err)
	}
	if cloud == nil {
		return nil, fmt.Errorf("unknown cloud provider %q", name)
	}

	return cloud, nil
}


IsAllowAll

// IsAllowAll checks whether the utilnet.IPNet allows traffic from 0.0.0.0/0
func IsAllowAll(ipnets utilnet.IPNetSet) bool {
	for _, s := range ipnets.StringSlice() {
		if s == "0.0.0.0/0" {
			return true
		}
	}
	return false
}


GetLoadBalancerSourceRanges

// GetLoadBalancerSourceRanges first try to parse and verify LoadBalancerSourceRanges field from a service.
// If the field is not specified, turn to parse and verify the AnnotationLoadBalancerSourceRangesKey annotation from a service,
// extracting the source ranges to allow, and if not present returns a default (allow-all) value.
func GetLoadBalancerSourceRanges(service *v1.Service) (utilnet.IPNetSet, error) {
	var ipnets utilnet.IPNetSet
	var err error
	// if SourceRange field is specified, ignore sourceRange annotation
	if len(service.Spec.LoadBalancerSourceRanges) > 0 {
		specs := service.Spec.LoadBalancerSourceRanges
		ipnets, err = utilnet.ParseIPNets(specs...)

		if err != nil {
			return nil, fmt.Errorf("service.Spec.LoadBalancerSourceRanges: %v is not valid. Expecting a list of IP ranges. For example, 10.0.0.0/24. Error msg: %v", specs, err)
		}
	} else {
		val := service.Annotations[v1.AnnotationLoadBalancerSourceRangesKey]
		val = strings.TrimSpace(val)
		if val == "" {
			val = defaultLoadBalancerSourceRanges
		}
		specs := strings.Split(val, ",")
		ipnets, err = utilnet.ParseIPNets(specs...)
		if err != nil {
			return nil, fmt.Errorf("%s: %s is not valid. Expecting a comma-separated list of source IP ranges. For example, 10.0.0.0/24,192.168.2.0/24", v1.AnnotationLoadBalancerSourceRangesKey, val)
		}
	}
	return ipnets, nil
}


GetServiceHealthCheckPathPort

// GetServiceHealthCheckPathPort returns the path and nodePort programmed into the Cloud LB Health Check
func GetServiceHealthCheckPathPort(service *v1.Service) (string, int32) {
	if !NeedsHealthCheck(service) {
		return "", 0
	}
	port := service.Spec.HealthCheckNodePort
	if port == 0 {
		return "", 0
	}
	return "/healthz", port
}


RequestsOnlyLocalTraffic

// RequestsOnlyLocalTraffic checks if service requests OnlyLocal traffic.
func RequestsOnlyLocalTraffic(service *v1.Service) bool {
	if service.Spec.Type != v1.ServiceTypeLoadBalancer &&
		service.Spec.Type != v1.ServiceTypeNodePort {
		return false
	}
	return service.Spec.ExternalTrafficPolicy == v1.ServiceExternalTrafficPolicyLocal
}


NeedsHealthCheck

// NeedsHealthCheck checks if service needs health check.
func NeedsHealthCheck(service *v1.Service) bool {
	if service.Spec.Type != v1.ServiceTypeLoadBalancer {
		return false
	}
	return RequestsOnlyLocalTraffic(service)
}


HasLBFinalizer

// HasLBFinalizer checks if service contains LoadBalancerCleanupFinalizer.
func HasLBFinalizer(service *v1.Service) bool {
	for _, finalizer := range service.ObjectMeta.Finalizers {
		if finalizer == LoadBalancerCleanupFinalizer {
			return true
		}
	}
	return false
}


LoadBalancerStatusEqual

// LoadBalancerStatusEqual checks if load balancer status are equal
func LoadBalancerStatusEqual(l, r *v1.LoadBalancerStatus) bool {
	return ingressSliceEqual(l.Ingress, r.Ingress)
}


PatchService

// PatchService patches the given service's Status or ObjectMeta based on the original and
// updated ones. Change to spec will be ignored.
func PatchService(c corev1.CoreV1Interface, oldSvc, newSvc *v1.Service) (*v1.Service, error) {
	// Reset spec to make sure only patch for Status or ObjectMeta.
	newSvc.Spec = oldSvc.Spec

	patchBytes, err := getPatchBytes(oldSvc, newSvc)
	if err != nil {
		return nil, err
	}

	return c.Services(oldSvc.Namespace).Patch(context.TODO(), oldSvc.Name, types.StrategicMergePatchType, patchBytes, metav1.PatchOptions{}, "status")

}


getPatchBytes

func getPatchBytes(oldSvc, newSvc *v1.Service) ([]byte, error) {
	oldData, err := json.Marshal(oldSvc)
	if err != nil {
		return nil, fmt.Errorf("failed to Marshal oldData for svc %s/%s: %v", oldSvc.Namespace, oldSvc.Name, err)
	}

	newData, err := json.Marshal(newSvc)
	if err != nil {
		return nil, fmt.Errorf("failed to Marshal newData for svc %s/%s: %v", newSvc.Namespace, newSvc.Name, err)
	}

	patchBytes, err := strategicpatch.CreateTwoWayMergePatch(oldData, newData, v1.Service{})
	if err != nil {
		return nil, fmt.Errorf("failed to CreateTwoWayMergePatch for svc %s/%s: %v", oldSvc.Namespace, oldSvc.Name, err)
	}
	return patchBytes, nil

}


ingressSliceEqual

func ingressSliceEqual(lhs, rhs []v1.LoadBalancerIngress) bool {
	if len(lhs) != len(rhs) {
		return false
	}
	for i := range lhs {
		if !ingressEqual(&lhs[i], &rhs[i]) {
			return false
		}
	}
	return true
}


ingressEqual

func ingressEqual(lhs, rhs *v1.LoadBalancerIngress) bool {
	if lhs.IP != rhs.IP {
		return false
	}
	if lhs.Hostname != rhs.Hostname {
		return false
	}
	return true
}


NewDeletedVolumeInUseError

// NewDeletedVolumeInUseError returns a new instance of DeletedVolumeInUseError
// error.
func NewDeletedVolumeInUseError(message string) error {
	return deletedVolumeInUseError(message)
}


IsDeletedVolumeInUse

// IsDeletedVolumeInUse returns true if an error returned from Delete() is
// deletedVolumeInUseError
func IsDeletedVolumeInUse(err error) bool {
	switch err.(type) {
	case deletedVolumeInUseError:
		return true
	default:
		return false
	}
}


Error

func (err deletedVolumeInUseError) Error() string {
	return string(err)
}


func (err *DanglingAttachError) Error() string {
	return err.msg
}


NewDanglingError

// NewDanglingError create a new dangling error
func NewDanglingError(msg string, node k8stypes.NodeName, devicePath string) error {
	return &DanglingAttachError{
		msg:         msg,
		CurrentNode: node,
		DevicePath:  devicePath,
	}
}


IsDanglingError

// IsDanglingError returns true if an error is DanglingAttachError
func IsDanglingError(err error) bool {
	switch err.(type) {
	case *DanglingAttachError:
		return true
	default:
		return false
	}
}


RoundUpToGiB

// RoundUpToGiB rounds up given quantity upto chunks of GiB
func RoundUpToGiB(size resource.Quantity) (int64, error) {
	return roundUpSizeInt64(size, GiB)
}


RoundUpToMB

// RoundUpToMB rounds up given quantity to chunks of MB
func RoundUpToMB(size resource.Quantity) (int64, error) {
	return roundUpSizeInt64(size, MB)
}


RoundUpToMiB

// RoundUpToMiB rounds up given quantity upto chunks of MiB
func RoundUpToMiB(size resource.Quantity) (int64, error) {
	return roundUpSizeInt64(size, MiB)
}


RoundUpToKB

// RoundUpToKB rounds up given quantity to chunks of KB
func RoundUpToKB(size resource.Quantity) (int64, error) {
	return roundUpSizeInt64(size, KB)
}


RoundUpToKiB

// RoundUpToKiB rounds up given quantity to chunks of KiB
func RoundUpToKiB(size resource.Quantity) (int64, error) {
	return roundUpSizeInt64(size, KiB)
}


RoundUpToB

// RoundUpToB rounds up given quantity to chunks of bytes
func RoundUpToB(size resource.Quantity) (int64, error) {
	return roundUpSizeInt64(size, 1)
}


RoundUpToGiBInt

// RoundUpToGiBInt rounds up given quantity upto chunks of GiB. It returns an
// int instead of an int64 and an error if there's overflow
func RoundUpToGiBInt(size resource.Quantity) (int, error) {
	return roundUpSizeInt(size, GiB)
}


RoundUpToMBInt

// RoundUpToMBInt rounds up given quantity to chunks of MB. It returns an
// int instead of an int64 and an error if there's overflow
func RoundUpToMBInt(size resource.Quantity) (int, error) {
	return roundUpSizeInt(size, MB)
}


RoundUpToMiBInt

// RoundUpToMiBInt rounds up given quantity upto chunks of MiB. It returns an
// int instead of an int64 and an error if there's overflow
func RoundUpToMiBInt(size resource.Quantity) (int, error) {
	return roundUpSizeInt(size, MiB)
}


RoundUpToKBInt

// RoundUpToKBInt rounds up given quantity to chunks of KB. It returns an
// int instead of an int64 and an error if there's overflow
func RoundUpToKBInt(size resource.Quantity) (int, error) {
	return roundUpSizeInt(size, KB)
}


RoundUpToKiBInt

// RoundUpToKiBInt rounds up given quantity upto chunks of KiB. It returns an
// int instead of an int64 and an error if there's overflow
func RoundUpToKiBInt(size resource.Quantity) (int, error) {
	return roundUpSizeInt(size, KiB)
}


// RoundUpToGiBInt32 rounds up given quantity up to chunks of GiB. It returns an
// int32 instead of an int64 and an error if there's overflow
func RoundUpToGiBInt32(size resource.Quantity) (int32, error) {
	return roundUpSizeInt32(size, GiB)
}


roundUpSizeInt

// roundUpSizeInt calculates how many allocation units are needed to accommodate
// a volume of a given size. It returns an int and an error if there's overflow
func roundUpSizeInt(size resource.Quantity, allocationUnitBytes int64) (int, error) {
	if bits.UintSize == 32 {
		res, err := roundUpSizeInt32(size, allocationUnitBytes)
		return int(res), err
	}
	res, err := roundUpSizeInt64(size, allocationUnitBytes)
	return int(res), err
}


// roundUpSizeInt32 calculates how many allocation units are needed to accommodate
// a volume of a given size. It returns an int32 and an error if there's overflow
func roundUpSizeInt32(size resource.Quantity, allocationUnitBytes int64) (int32, error) {
	roundedUpInt32, err := roundUpSizeInt64(size, allocationUnitBytes)
	if err != nil {
		return 0, err
	}
	if roundedUpInt32 > math.MaxInt32 {
		return 0, fmt.Errorf("quantity %s is too great, overflows int32", size.String())
	}
	return int32(roundedUpInt32), nil
}


// roundUpSizeInt64 calculates how many allocation units are needed to accommodate
// a volume of a given size. It returns an int64 and an error if there's overflow
func roundUpSizeInt64(size resource.Quantity, allocationUnitBytes int64) (int64, error) {
	// Use CmpInt64() to find out if the value of "size" would overflow an
	// int64 and therefore have Value() return a wrong result. Then, retrieve
	// the value as int64 and perform the rounding.
	// It's not convenient to use AsScale() and related functions as they don't
	// support BinarySI format, nor can we use AsInt64() directly since it's
	// only implemented for int64 scaled numbers (int64Amount).

	// CmpInt64() actually returns 0 when comparing an amount bigger than MaxInt64.
	if size.CmpInt64(math.MaxInt64) >= 0 {
		return 0, fmt.Errorf("quantity %s is too great, overflows int64", size.String())
	}
	volumeSizeBytes := size.Value()

	roundedUp := volumeSizeBytes / allocationUnitBytes
	if volumeSizeBytes%allocationUnitBytes > 0 {
		roundedUp++
	}
	return roundedUp, nil
}


LabelZonesToSet

// LabelZonesToSet converts a PV label value from string containing a delimited list of zones to set
func LabelZonesToSet(labelZonesValue string) (sets.String, error) {
	return stringToSet(labelZonesValue, cloudvolume.LabelMultiZoneDelimiter)
}


ZonesSetToLabelValue

// ZonesSetToLabelValue converts zones set to label value
func ZonesSetToLabelValue(strSet sets.String) string {
	return strings.Join(strSet.UnsortedList(), cloudvolume.LabelMultiZoneDelimiter)
}


ZonesToSet

// ZonesToSet converts a string containing a comma separated list of zones to set
func ZonesToSet(zonesString string) (sets.String, error) {
	zones, err := stringToSet(zonesString, ",")
	if err != nil {
		return nil, fmt.Errorf("error parsing zones %s, must be strings separated by commas: %v", zonesString, err)
	}
	return zones, nil
}


stringToSet

// StringToSet converts a string containing list separated by specified delimiter to a set
func stringToSet(str, delimiter string) (sets.String, error) {
	zonesSlice := strings.Split(str, delimiter)
	zonesSet := make(sets.String)
	for _, zone := range zonesSlice {
		trimmedZone := strings.TrimSpace(zone)
		if trimmedZone == "" {
			return make(sets.String), fmt.Errorf(
				"%q separated list (%q) must not contain an empty string",
				delimiter,
				str)
		}
		zonesSet.Insert(trimmedZone)
	}
	return zonesSet, nil
}


LabelZonesToList

// LabelZonesToList converts a PV label value from string containing a delimited list of zones to list
func LabelZonesToList(labelZonesValue string) ([]string, error) {
	return stringToList(labelZonesValue, cloudvolume.LabelMultiZoneDelimiter)
}


stringToList

// StringToList converts a string containing list separated by specified delimiter to a list
func stringToList(str, delimiter string) ([]string, error) {
	zonesSlice := make([]string, 0)
	for _, zone := range strings.Split(str, delimiter) {
		trimmedZone := strings.TrimSpace(zone)
		if trimmedZone == "" {
			return nil, fmt.Errorf(
				"%q separated list (%q) must not contain an empty string",
				delimiter,
				str)
		}
		zonesSlice = append(zonesSlice, trimmedZone)
	}
	return zonesSlice, nil
}


SelectZoneForVolume

// SelectZoneForVolume is a wrapper around SelectZonesForVolume
// to select a single zone for a volume based on parameters
func SelectZoneForVolume(zoneParameterPresent, zonesParameterPresent bool, zoneParameter string, zonesParameter, zonesWithNodes sets.String, node *v1.Node, allowedTopologies []v1.TopologySelectorTerm, pvcName string) (string, error) {
	zones, err := SelectZonesForVolume(zoneParameterPresent, zonesParameterPresent, zoneParameter, zonesParameter, zonesWithNodes, node, allowedTopologies, pvcName, 1)
	if err != nil {
		return "", err
	}
	zone, ok := zones.PopAny()
	if !ok {
		return "", fmt.Errorf("could not determine a zone to provision volume in")
	}
	return zone, nil
}


SelectZonesForVolume

// SelectZonesForVolume selects zones for a volume based on several factors:
// node.zone, allowedTopologies, zone/zones parameters from storageclass,
// zones with active nodes from the cluster. The number of zones = replicas.
func SelectZonesForVolume(zoneParameterPresent, zonesParameterPresent bool, zoneParameter string, zonesParameter, zonesWithNodes sets.String, node *v1.Node, allowedTopologies []v1.TopologySelectorTerm, pvcName string, numReplicas uint32) (sets.String, error) {
	if zoneParameterPresent && zonesParameterPresent {
		return nil, fmt.Errorf("both zone and zones StorageClass parameters must not be used at the same time")
	}

	var zoneFromNode string
	// pick one zone from node if present
	if node != nil {
		// VolumeScheduling implicit since node is not nil
		if zoneParameterPresent || zonesParameterPresent {
			return nil, fmt.Errorf("zone[s] cannot be specified in StorageClass if VolumeBindingMode is set to WaitForFirstConsumer. Please specify allowedTopologies in StorageClass for constraining zones")
		}

		// pick node's zone for one of the replicas
		var ok bool
		zoneFromNode, ok = node.ObjectMeta.Labels[v1.LabelTopologyZone]
		if !ok {
			zoneFromNode, ok = node.ObjectMeta.Labels[v1.LabelFailureDomainBetaZone]
			if !ok {
				return nil, fmt.Errorf("Either %s or %s Label for node missing", v1.LabelTopologyZone, v1.LabelFailureDomainBetaZone)
			}
		}
		// if single replica volume and node with zone found, return immediately
		if numReplicas == 1 {
			return sets.NewString(zoneFromNode), nil
		}
	}

	// pick zone from allowedZones if specified
	allowedZones, err := ZonesFromAllowedTopologies(allowedTopologies)
	if err != nil {
		return nil, err
	}

	if (len(allowedTopologies) > 0) && (allowedZones.Len() == 0) {
		return nil, fmt.Errorf("no matchLabelExpressions with %s key found in allowedTopologies. Please specify matchLabelExpressions with %s key", v1.LabelTopologyZone, v1.LabelTopologyZone)
	}

	if allowedZones.Len() > 0 {
		// VolumeScheduling implicit since allowedZones present
		if zoneParameterPresent || zonesParameterPresent {
			return nil, fmt.Errorf("zone[s] cannot be specified in StorageClass if allowedTopologies specified")
		}
		// scheduler will guarantee if node != null above, zoneFromNode is member of allowedZones.
		// so if zoneFromNode != "", we can safely assume it is part of allowedZones.
		zones, err := chooseZonesForVolumeIncludingZone(allowedZones, pvcName, zoneFromNode, numReplicas)
		if err != nil {
			return nil, fmt.Errorf("cannot process zones in allowedTopologies: %v", err)
		}
		return zones, nil
	}

	// pick zone from parameters if present
	if zoneParameterPresent {
		if numReplicas > 1 {
			return nil, fmt.Errorf("zone cannot be specified if desired number of replicas for pv is greather than 1. Please specify zones or allowedTopologies to specify desired zones")
		}
		return sets.NewString(zoneParameter), nil
	}

	if zonesParameterPresent {
		if uint32(zonesParameter.Len()) < numReplicas {
			return nil, fmt.Errorf("not enough zones found in zones parameter to provision a volume with %d replicas. Found %d zones, need %d zones", numReplicas, zonesParameter.Len(), numReplicas)
		}
		// directly choose from zones parameter; no zone from node need to be considered
		return ChooseZonesForVolume(zonesParameter, pvcName, numReplicas), nil
	}

	// pick zone from zones with nodes
	if zonesWithNodes.Len() > 0 {
		// If node != null (and thus zoneFromNode != ""), zoneFromNode will be member of zonesWithNodes
		zones, err := chooseZonesForVolumeIncludingZone(zonesWithNodes, pvcName, zoneFromNode, numReplicas)
		if err != nil {
			return nil, fmt.Errorf("cannot process zones where nodes exist in the cluster: %v", err)
		}
		return zones, nil
	}
	return nil, fmt.Errorf("cannot determine zones to provision volume in")
}


ZonesFromAllowedTopologies

// ZonesFromAllowedTopologies returns a list of zones specified in allowedTopologies
func ZonesFromAllowedTopologies(allowedTopologies []v1.TopologySelectorTerm) (sets.String, error) {
	zones := make(sets.String)
	for _, term := range allowedTopologies {
		for _, exp := range term.MatchLabelExpressions {
			if exp.Key == v1.LabelTopologyZone || exp.Key == v1.LabelFailureDomainBetaZone {
				for _, value := range exp.Values {
					zones.Insert(value)
				}
			} else {
				return nil, fmt.Errorf("unsupported key found in matchLabelExpressions: %s", exp.Key)
			}
		}
	}
	return zones, nil
}


chooseZonesForVolumeIncludingZone

// chooseZonesForVolumeIncludingZone is a wrapper around ChooseZonesForVolume that ensures zoneToInclude is chosen
// zoneToInclude can either be empty in which case it is ignored. If non-empty, zoneToInclude is expected to be member of zones.
// numReplicas is expected to be > 0 and <= zones.Len()
func chooseZonesForVolumeIncludingZone(zones sets.String, pvcName, zoneToInclude string, numReplicas uint32) (sets.String, error) {
	if numReplicas == 0 {
		return nil, fmt.Errorf("invalid number of replicas passed")
	}
	if uint32(zones.Len()) < numReplicas {
		return nil, fmt.Errorf("not enough zones found to provision a volume with %d replicas. Need at least %d distinct zones for a volume with %d replicas", numReplicas, numReplicas, numReplicas)
	}
	if zoneToInclude != "" && !zones.Has(zoneToInclude) {
		return nil, fmt.Errorf("zone to be included: %s needs to be member of set: %v", zoneToInclude, zones)
	}
	if uint32(zones.Len()) == numReplicas {
		return zones, nil
	}
	if zoneToInclude != "" {
		zones.Delete(zoneToInclude)
		numReplicas = numReplicas - 1
	}
	zonesChosen := ChooseZonesForVolume(zones, pvcName, numReplicas)
	if zoneToInclude != "" {
		zonesChosen.Insert(zoneToInclude)
	}
	return zonesChosen, nil
}


ChooseZonesForVolume

// ChooseZonesForVolume is identical to ChooseZoneForVolume, but selects a multiple zones, for multi-zone disks.
func ChooseZonesForVolume(zones sets.String, pvcName string, numZones uint32) sets.String {
	// No zones available, return empty set.
	replicaZones := sets.NewString()
	if zones.Len() == 0 {
		return replicaZones
	}

	// We create the volume in a zone determined by the name
	// Eventually the scheduler will coordinate placement into an available zone
	hash, index := getPVCNameHashAndIndexOffset(pvcName)

	// Zones.List returns zones in a consistent order (sorted)
	// We do have a potential failure case where volumes will not be properly spread,
	// if the set of zones changes during StatefulSet volume creation.  However, this is
	// probably relatively unlikely because we expect the set of zones to be essentially
	// static for clusters.
	// Hopefully we can address this problem if/when we do full scheduler integration of
	// PVC placement (which could also e.g. avoid putting volumes in overloaded or
	// unhealthy zones)
	zoneSlice := zones.List()

	startingIndex := index * numZones
	for index = startingIndex; index < startingIndex+numZones; index++ {
		zone := zoneSlice[(hash+index)%uint32(len(zoneSlice))]
		replicaZones.Insert(zone)
	}

	klog.V(2).Infof("Creating volume for replicated PVC %q; chosen zones=%q from zones=%q",
		pvcName, replicaZones.UnsortedList(), zoneSlice)
	return replicaZones
}


getPVCNameHashAndIndexOffset

func getPVCNameHashAndIndexOffset(pvcName string) (hash uint32, index uint32) {
	if pvcName == "" {
		// We should always be called with a name; this shouldn't happen
		klog.Warningf("No name defined during volume create; choosing random zone")

		hash = rand.Uint32()
	} else {
		hashString := pvcName

		// Heuristic to make sure that volumes in a StatefulSet are spread across zones
		// StatefulSet PVCs are (currently) named ClaimName-StatefulSetName-Id,
		// where Id is an integer index.
		// Note though that if a StatefulSet pod has multiple claims, we need them to be
		// in the same zone, because otherwise the pod will be unable to mount both volumes,
		// and will be unschedulable.  So we hash _only_ the "StatefulSetName" portion when
		// it looks like `ClaimName-StatefulSetName-Id`.
		// We continue to round-robin volume names that look like `Name-Id` also; this is a useful
		// feature for users that are creating statefulset-like functionality without using statefulsets.
		lastDash := strings.LastIndexByte(pvcName, '-')
		if lastDash != -1 {
			statefulsetIDString := pvcName[lastDash+1:]
			statefulsetID, err := strconv.ParseUint(statefulsetIDString, 10, 32)
			if err == nil {
				// Offset by the statefulsetID, so we round-robin across zones
				index = uint32(statefulsetID)
				// We still hash the volume name, but only the prefix
				hashString = pvcName[:lastDash]

				// In the special case where it looks like `ClaimName-StatefulSetName-Id`,
				// hash only the StatefulSetName, so that different claims on the same StatefulSet
				// member end up in the same zone.
				// Note that StatefulSetName (and ClaimName) might themselves both have dashes.
				// We actually just take the portion after the final - of ClaimName-StatefulSetName.
				// For our purposes it doesn't much matter (just suboptimal spreading).
				lastDash := strings.LastIndexByte(hashString, '-')
				if lastDash != -1 {
					hashString = hashString[lastDash+1:]
				}

				klog.V(2).Infof("Detected StatefulSet-style volume name %q; index=%d", pvcName, index)
			}
		}

		// We hash the (base) volume name, so we don't bias towards the first N zones
		h := fnv.New32()
		h.Write([]byte(hashString))
		hash = h.Sum32()
	}

	return hash, index
}


ComputeDetachedSignature

// ComputeDetachedSignature takes content and token details and computes a detached
// JWS signature.  This is described in Appendix F of RFC 7515.  Basically, this
// is a regular JWS with the content part of the signature elided.
func ComputeDetachedSignature(content, tokenID, tokenSecret string) (string, error) {
	jwk := &jose.JSONWebKey{
		Key:   []byte(tokenSecret),
		KeyID: tokenID,
	}

	opts := &jose.SignerOptions{
		// Since this is a symmetric key, go-jose doesn't automatically include
		// the KeyID as part of the protected header. We have to pass it here
		// explicitly.
		ExtraHeaders: map[jose.HeaderKey]interface{}{
			"kid": tokenID,
		},
	}

	signer, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.HS256, Key: jwk}, opts)
	if err != nil {
		return "", fmt.Errorf("can't make a HS256 signer from the given token: %v", err)
	}

	jws, err := signer.Sign([]byte(content))
	if err != nil {
		return "", fmt.Errorf("can't HS256-sign the given token: %v", err)
	}

	fullSig, err := jws.CompactSerialize()
	if err != nil {
		return "", fmt.Errorf("can't serialize the given token: %v", err)
	}
	return stripContent(fullSig)
}


stripContent

// stripContent will remove the content part of a compact JWS
//
// The `go-jose` library doesn't support generating signatures with "detached"
// content. To make up for this we take the full compact signature, break it
// apart and put it back together without the content section.
func stripContent(fullSig string) (string, error) {
	parts := strings.Split(fullSig, ".")
	if len(parts) != 3 {
		return "", fmt.Errorf("compact JWS format must have three parts")
	}

	return parts[0] + ".." + parts[2], nil
}


DetachedTokenIsValid

// DetachedTokenIsValid checks whether a given detached JWS-encoded token matches JWS output of the given content and token
func DetachedTokenIsValid(detachedToken, content, tokenID, tokenSecret string) bool {
	newToken, err := ComputeDetachedSignature(content, tokenID, tokenSecret)
	if err != nil {
		return false
	}
	return detachedToken == newToken
}


GenerateBootstrapToken

// GenerateBootstrapToken generates a new, random Bootstrap Token.
func GenerateBootstrapToken() (string, error) {
	tokenID, err := randBytes(api.BootstrapTokenIDBytes)
	if err != nil {
		return "", err
	}

	tokenSecret, err := randBytes(api.BootstrapTokenSecretBytes)
	if err != nil {
		return "", err
	}

	return TokenFromIDAndSecret(tokenID, tokenSecret), nil
}


randBytes

// randBytes returns a random string consisting of the characters in
// validBootstrapTokenChars, with the length customized by the parameter
func randBytes(length int) (string, error) {
	// len("0123456789abcdefghijklmnopqrstuvwxyz") = 36 which doesn't evenly divide
	// the possible values of a byte: 256 mod 36 = 4. Discard any random bytes we
	// read that are >= 252 so the bytes we evenly divide the character set.
	const maxByteValue = 252

	var (
		b     byte
		err   error
		token = make([]byte, length)
	)

	reader := bufio.NewReaderSize(rand.Reader, length*2)
	for i := range token {
		for {
			if b, err = reader.ReadByte(); err != nil {
				return "", err
			}
			if b < maxByteValue {
				break
			}
		}

		token[i] = validBootstrapTokenChars[int(b)%len(validBootstrapTokenChars)]
	}

	return string(token), nil
}


TokenFromIDAndSecret

// TokenFromIDAndSecret returns the full token which is of the form "{id}.{secret}"
func TokenFromIDAndSecret(id, secret string) string {
	return fmt.Sprintf("%s.%s", id, secret)
}


IsValidBootstrapToken

// IsValidBootstrapToken returns whether the given string is valid as a Bootstrap Token and
// in other words satisfies the BootstrapTokenRegexp
func IsValidBootstrapToken(token string) bool {
	return BootstrapTokenRegexp.MatchString(token)
}


IsValidBootstrapTokenID

// IsValidBootstrapTokenID returns whether the given string is valid as a Bootstrap Token ID and
// in other words satisfies the BootstrapTokenIDRegexp
func IsValidBootstrapTokenID(tokenID string) bool {
	return BootstrapTokenIDRegexp.MatchString(tokenID)
}


BootstrapTokenSecretName

// BootstrapTokenSecretName returns the expected name for the Secret storing the
// Bootstrap Token in the Kubernetes API.
func BootstrapTokenSecretName(tokenID string) string {
	return fmt.Sprintf("%s%s", api.BootstrapTokenSecretPrefix, tokenID)
}


ValidateBootstrapGroupName

// ValidateBootstrapGroupName checks if the provided group name is a valid
// bootstrap group name. Returns nil if valid or a validation error if invalid.
// TODO(dixudx): should be moved to util/secrets
func ValidateBootstrapGroupName(name string) error {
	if BootstrapGroupRegexp.Match([]byte(name)) {
		return nil
	}
	return fmt.Errorf("bootstrap group %q is invalid (must match %s)", name, api.BootstrapGroupPattern)
}


ValidateUsages

// ValidateUsages validates that the passed in string are valid usage strings for bootstrap tokens.
func ValidateUsages(usages []string) error {
	validUsages := sets.NewString(api.KnownTokenUsages...)
	invalidUsages := sets.NewString()
	for _, usage := range usages {
		if !validUsages.Has(usage) {
			invalidUsages.Insert(usage)
		}
	}
	if len(invalidUsages) > 0 {
		return fmt.Errorf("invalid bootstrap token usage string: %s, valid usage options: %s", strings.Join(invalidUsages.List(), ","), strings.Join(api.KnownTokenUsages, ","))
	}
	return nil
}


GetData

// GetData returns the string value for the given key in the specified Secret
// If there is an error or if the key doesn't exist, an empty string is returned.
func GetData(secret *v1.Secret, key string) string {
	if secret.Data == nil {
		return ""
	}
	if val, ok := secret.Data[key]; ok {
		return string(val)
	}
	return ""
}


HasExpired

// HasExpired will identify whether the secret expires
func HasExpired(secret *v1.Secret, currentTime time.Time) bool {
	_, expired := GetExpiration(secret, currentTime)

	return expired
}


GetExpiration

// GetExpiration checks if the secret expires
// isExpired indicates if the secret is already expired.
// timeRemaining indicates how long until it does expire.
// if the secret has no expiration timestamp, returns 0, false.
// if there is an error parsing the secret's expiration timestamp, returns 0, true.
func GetExpiration(secret *v1.Secret, currentTime time.Time) (timeRemaining time.Duration, isExpired bool) {
	expiration := GetData(secret, api.BootstrapTokenExpirationKey)
	if len(expiration) == 0 {
		return 0, false
	}
	expTime, err := time.Parse(time.RFC3339, expiration)
	if err != nil {
		klog.V(3).Infof("Unparseable expiration time (%s) in %s/%s Secret: %v. Treating as expired.",
			expiration, secret.Namespace, secret.Name, err)
		return 0, true
	}

	timeRemaining = expTime.Sub(currentTime)
	if timeRemaining <= 0 {
		klog.V(3).Infof("Expired bootstrap token in %s/%s Secret: %v",
			secret.Namespace, secret.Name, expiration)
		return 0, true
	}
	return timeRemaining, false
}


ParseName

// ParseName parses the name of the secret to extract the secret ID.
func ParseName(name string) (secretID string, ok bool) {
	r := secretNameRe.FindStringSubmatch(name)
	if r == nil {
		return "", false
	}
	return r[1], true
}


GetGroups

// GetGroups loads and validates the bootstrapapi.BootstrapTokenExtraGroupsKey
// key from the bootstrap token secret, returning a list of group names or an
// error if any of the group names are invalid.
func GetGroups(secret *v1.Secret) ([]string, error) {
	// always include the default group
	groups := sets.NewString(api.BootstrapDefaultGroup)

	// grab any extra groups and if there are none, return just the default
	extraGroupsString := GetData(secret, api.BootstrapTokenExtraGroupsKey)
	if extraGroupsString == "" {
		return groups.List(), nil
	}

	// validate the names of the extra groups
	for _, group := range strings.Split(extraGroupsString, ",") {
		if err := legacyutil.ValidateBootstrapGroupName(group); err != nil {
			return nil, err
		}
		groups.Insert(group)
	}

	// return the result as a deduplicated, sorted list
	return groups.List(), nil
}


ParseToken

// ParseToken tries and parse a valid token from a string.
// A token ID and token secret are returned in case of success, an error otherwise.
func ParseToken(s string) (tokenID, tokenSecret string, err error) {
	split := bootstrapTokenRe.FindStringSubmatch(s)
	if len(split) != 3 {
		return "", "", fmt.Errorf("token [%q] was not of form [%q]", s, api.BootstrapTokenPattern)
	}
	return split[1], split[2], nil
}


NewDefaults

// NewDefaults returns default arguments for the generator.
func NewDefaults() (*args.GeneratorArgs, *CustomArgs) {
	genericArgs := args.Default().WithoutDefaultFlagParsing()
	customArgs := &CustomArgs{
		ExternalApplyConfigurations: map[types.Name]string{
			// Always include TypeMeta and ObjectMeta. They are sufficient for the vast majority of use cases.
			{Package: "k8s.io/apimachinery/pkg/apis/meta/v1", Name: "TypeMeta"}:       "k8s.io/client-go/applyconfigurations/meta/v1",
			{Package: "k8s.io/apimachinery/pkg/apis/meta/v1", Name: "ObjectMeta"}:     "k8s.io/client-go/applyconfigurations/meta/v1",
			{Package: "k8s.io/apimachinery/pkg/apis/meta/v1", Name: "OwnerReference"}: "k8s.io/client-go/applyconfigurations/meta/v1",
		},
	}
	genericArgs.CustomArgs = customArgs

	if pkg := codegenutil.CurrentPackage(); len(pkg) != 0 {
		genericArgs.OutputPackagePath = path.Join(pkg, "pkg/client/applyconfigurations")
	}

	return genericArgs, customArgs
}


AddFlags

func (ca *CustomArgs) AddFlags(fs *pflag.FlagSet, inputBase string) {
	pflag.Var(NewExternalApplyConfigurationValue(&ca.ExternalApplyConfigurations, nil), "external-applyconfigurations",
		"list of comma separated external apply configurations locations in <type-package>.<type-name>:<applyconfiguration-package> form."+
			"For example: k8s.io/api/apps/v1.Deployment:k8s.io/client-go/applyconfigurations/apps/v1")
	pflag.StringVar(&ca.OpenAPISchemaFilePath, "openapi-schema", "",
		"path to the openapi schema containing all the types that apply configurations will be generated for")
}


Validate

// Validate checks the given arguments.
func Validate(genericArgs *args.GeneratorArgs) error {
	if len(genericArgs.OutputPackagePath) == 0 {
		return fmt.Errorf("output package cannot be empty")
	}
	return nil
}


NewExternalApplyConfigurationValue

func NewExternalApplyConfigurationValue(externals *map[types.Name]string, def []string) *externalApplyConfigurationValue {
	val := new(externalApplyConfigurationValue)
	val.externals = externals
	if def != nil {
		if err := val.set(def); err != nil {
			panic(err)
		}
	}
	return val
}


set

func (s *externalApplyConfigurationValue) set(vs []string) error {
	for _, input := range vs {
		typ, pkg, err := parseExternalMapping(input)
		if err != nil {
			return err
		}
		if _, ok := (*s.externals)[typ]; ok {
			return fmt.Errorf("duplicate type found in --external-applyconfigurations: %v", typ)
		}
		(*s.externals)[typ] = pkg
	}

	return nil
}


Set

func (s *externalApplyConfigurationValue) Set(val string) error {
	vs, err := readAsCSV(val)
	if err != nil {
		return err
	}
	if err := s.set(vs); err != nil {
		return err
	}

	return nil
}


Type

func (s *externalApplyConfigurationValue) Type() string {
	return "string"
}


String

func (s *externalApplyConfigurationValue) String() string {
	var strs []string
	for k, v := range *s.externals {
		strs = append(strs, fmt.Sprintf("%s.%s:%s", k.Package, k.Name, v))
	}
	str, _ := writeAsCSV(strs)
	return "[" + str + "]"
}


readAsCSV

func readAsCSV(val string) ([]string, error) {
	if val == "" {
		return []string{}, nil
	}
	stringReader := strings.NewReader(val)
	csvReader := csv.NewReader(stringReader)
	return csvReader.Read()
}


writeAsCSV

func writeAsCSV(vals []string) (string, error) {
	b := &bytes.Buffer{}
	w := csv.NewWriter(b)
	err := w.Write(vals)
	if err != nil {
		return "", err
	}
	w.Flush()
	return strings.TrimSuffix(b.String(), "\n"), nil
}


parseExternalMapping

func parseExternalMapping(mapping string) (typ types.Name, pkg string, err error) {
	parts := strings.Split(mapping, ":")
	if len(parts) != 2 {
		return types.Name{}, "", fmt.Errorf("expected string of the form <package>.<typeName>:<applyconfiguration-package> but got %s", mapping)
	}
	packageTypeStr := parts[0]
	pkg = parts[1]
	// need to split on the *last* dot, since k8s.io (and other valid packages) have a dot in it
	lastDot := strings.LastIndex(packageTypeStr, ".")
	if lastDot == -1 || lastDot == len(packageTypeStr)-1 {
		return types.Name{}, "", fmt.Errorf("expected package and type of the form <package>.<typeName> but got %s", packageTypeStr)
	}
	structPkg := packageTypeStr[:lastDot]
	structType := packageTypeStr[lastDot+1:]

	return types.Name{Package: structPkg, Name: structType}, pkg, nil
}


Filter

func (g *applyConfigurationGenerator) Filter(_ *generator.Context, t *types.Type) bool {
	return t == g.applyConfig.Type
}


Namers

func (g *applyConfigurationGenerator) Namers(*generator.Context) namer.NameSystems {
	return namer.NameSystems{
		"raw":          namer.NewRawNamer(g.localPackage.Package, g.imports),
		"singularKind": namer.NewPublicNamer(0),
	}
}


Imports

func (g *applyConfigurationGenerator) Imports(*generator.Context) (imports []string) {
	return g.imports.ImportLines()
}


GenerateType

func (g *applyConfigurationGenerator) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error {
	sw := generator.NewSnippetWriter(w, c, "$", "$")

	klog.V(5).Infof("processing type %v", t)
	typeParams := TypeParams{
		Struct:      t,
		ApplyConfig: g.applyConfig,
		Tags:        genclientTags(t),
		APIVersion:  g.groupVersion.ToAPIVersion(),
		ExtractInto: extractInto,
		ParserFunc:  types.Ref(g.outputPackage+"/internal", "Parser"),
		OpenAPIType: g.openAPIType,
	}

	g.generateStruct(sw, typeParams)

	if typeParams.Tags.GenerateClient {
		if typeParams.Tags.NonNamespaced {
			sw.Do(clientgenTypeConstructorNonNamespaced, typeParams)
		} else {
			sw.Do(clientgenTypeConstructorNamespaced, typeParams)
		}
		if typeParams.OpenAPIType != nil {
			g.generateClientgenExtract(sw, typeParams, !typeParams.Tags.NoStatus)
		}
	} else {
		if hasTypeMetaField(t) {
			sw.Do(constructorWithTypeMeta, typeParams)
		} else {
			sw.Do(constructor, typeParams)
		}
	}
	g.generateWithFuncs(t, typeParams, sw, nil)
	return sw.Error()
}


hasTypeMetaField

func hasTypeMetaField(t *types.Type) bool {
	for _, member := range t.Members {
		if typeMeta.Name == member.Type.Name && member.Embedded {
			return true
		}
	}
	return false
}


blocklisted

func blocklisted(t *types.Type, member types.Member) bool {
	if objectMeta.Name == t.Name && member.Name == "ManagedFields" {
		return true
	}
	if objectMeta.Name == t.Name && member.Name == "SelfLink" {
		return true
	}
	// Hide any fields which are en route to deletion.
	if strings.HasPrefix(member.Name, "ZZZ_") {
		return true
	}
	return false
}


generateWithFuncs

func (g *applyConfigurationGenerator) generateWithFuncs(t *types.Type, typeParams TypeParams, sw *generator.SnippetWriter, embed *memberParams) {
	for _, member := range t.Members {
		if blocklisted(t, member) {
			continue
		}
		memberType := g.refGraph.applyConfigForType(member.Type)
		if g.refGraph.isApplyConfig(member.Type) {
			memberType = &types.Type{Kind: types.Pointer, Elem: memberType}
		}
		if jsonTags, ok := lookupJSONTags(member); ok {
			memberParams := memberParams{
				TypeParams: typeParams,
				Member:     member,
				MemberType: memberType,
				JSONTags:   jsonTags,
				EmbeddedIn: embed,
			}
			if memberParams.Member.Embedded {
				g.generateWithFuncs(member.Type, typeParams, sw, &memberParams)
				if !jsonTags.inline {
					// non-inlined embeds are nillable and need a "ensure exists" utility function
					sw.Do(ensureEmbedExists, memberParams)
				}
				continue
			}

			// For slices where the items are generated apply configuration types, accept varargs of
			// pointers of the type as "with" function arguments so the "with" function can be used like so:
			// WithFoos(Foo().WithName("x"), Foo().WithName("y"))
			if t := deref(member.Type); t.Kind == types.Slice && g.refGraph.isApplyConfig(t.Elem) {
				memberParams.ArgType = &types.Type{Kind: types.Pointer, Elem: memberType.Elem}
				g.generateMemberWithForSlice(sw, member, memberParams)
				continue
			}
			// Note: There are no maps where the values are generated apply configurations (because
			// associative lists are used instead). So if a type like this is ever introduced, the
			// default "with" function generator will produce a working (but not entirely convenient "with" function)
			// that would be used like so:
			// WithMap(map[string]FooApplyConfiguration{*Foo().WithName("x")})

			switch memberParams.Member.Type.Kind {
			case types.Slice:
				memberParams.ArgType = memberType.Elem
				g.generateMemberWithForSlice(sw, member, memberParams)
			case types.Map:
				g.generateMemberWithForMap(sw, memberParams)
			default:
				g.generateMemberWith(sw, memberParams)
			}
		}
	}
}


generateStruct

func (g *applyConfigurationGenerator) generateStruct(sw *generator.SnippetWriter, typeParams TypeParams) {
	sw.Do("// $.ApplyConfig.ApplyConfiguration|public$ represents an declarative configuration of the $.ApplyConfig.Type|public$ type for use\n", typeParams)
	sw.Do("// with apply.\n", typeParams)
	sw.Do("type $.ApplyConfig.ApplyConfiguration|public$ struct {\n", typeParams)
	for _, structMember := range typeParams.Struct.Members {
		if blocklisted(typeParams.Struct, structMember) {
			continue
		}
		if structMemberTags, ok := lookupJSONTags(structMember); ok {
			if !structMemberTags.inline {
				structMemberTags.omitempty = true
			}
			params := memberParams{
				TypeParams: typeParams,
				Member:     structMember,
				MemberType: g.refGraph.applyConfigForType(structMember.Type),
				JSONTags:   structMemberTags,
			}
			if structMember.Embedded {
				if structMemberTags.inline {
					sw.Do("$.MemberType|raw$ `json:\"$.JSONTags$\"`\n", params)
				} else {
					sw.Do("*$.MemberType|raw$ `json:\"$.JSONTags$\"`\n", params)
				}
			} else if isNillable(structMember.Type) {
				sw.Do("$.Member.Name$ $.MemberType|raw$ `json:\"$.JSONTags$\"`\n", params)
			} else {
				sw.Do("$.Member.Name$ *$.MemberType|raw$ `json:\"$.JSONTags$\"`\n", params)
			}
		}
	}
	sw.Do("}\n", typeParams)
}


deref

func deref(t *types.Type) *types.Type {
	for t.Kind == types.Pointer {
		t = t.Elem
	}
	return t
}


isNillable

func isNillable(t *types.Type) bool {
	return t.Kind == types.Slice || t.Kind == types.Map
}


generateMemberWith

func (g *applyConfigurationGenerator) generateMemberWith(sw *generator.SnippetWriter, memberParams memberParams) {
	sw.Do("// With$.Member.Name$ sets the $.Member.Name$ field in the declarative configuration to the given value\n", memberParams)
	sw.Do("// and returns the receiver, so that objects can be built by chaining \"With\" function invocations.\n", memberParams)
	sw.Do("// If called multiple times, the $.Member.Name$ field is set to the value of the last call.\n", memberParams)
	sw.Do("func (b *$.ApplyConfig.ApplyConfiguration|public$) With$.Member.Name$(value $.MemberType|raw$) *$.ApplyConfig.ApplyConfiguration|public$ {\n", memberParams)
	g.ensureEnbedExistsIfApplicable(sw, memberParams)
	if g.refGraph.isApplyConfig(memberParams.Member.Type) || isNillable(memberParams.Member.Type) {
		sw.Do("b.$.Member.Name$ = value\n", memberParams)
	} else {
		sw.Do("b.$.Member.Name$ = &value\n", memberParams)
	}
	sw.Do("  return b\n", memberParams)
	sw.Do("}\n", memberParams)
}


generateMemberWithForSlice

func (g *applyConfigurationGenerator) generateMemberWithForSlice(sw *generator.SnippetWriter, member types.Member, memberParams memberParams) {
	memberIsPointerToSlice := member.Type.Kind == types.Pointer
	if memberIsPointerToSlice {
		sw.Do(ensureNonEmbedSliceExists, memberParams)
	}

	sw.Do("// With$.Member.Name$ adds the given value to the $.Member.Name$ field in the declarative configuration\n", memberParams)
	sw.Do("// and returns the receiver, so that objects can be build by chaining \"With\" function invocations.\n", memberParams)
	sw.Do("// If called multiple times, values provided by each call will be appended to the $.Member.Name$ field.\n", memberParams)
	sw.Do("func (b *$.ApplyConfig.ApplyConfiguration|public$) With$.Member.Name$(values ...$.ArgType|raw$) *$.ApplyConfig.ApplyConfiguration|public$ {\n", memberParams)
	g.ensureEnbedExistsIfApplicable(sw, memberParams)

	if memberIsPointerToSlice {
		sw.Do("b.ensure$.MemberType.Elem|public$Exists()\n", memberParams)
	}

	sw.Do("  for i := range values {\n", memberParams)
	if memberParams.ArgType.Kind == types.Pointer {
		sw.Do("if values[i] == nil {\n", memberParams)
		sw.Do("  panic(\"nil value passed to With$.Member.Name$\")\n", memberParams)
		sw.Do("}\n", memberParams)

		if memberIsPointerToSlice {
			sw.Do("*b.$.Member.Name$ = append(*b.$.Member.Name$, *values[i])\n", memberParams)
		} else {
			sw.Do("b.$.Member.Name$ = append(b.$.Member.Name$, *values[i])\n", memberParams)
		}
	} else {
		if memberIsPointerToSlice {
			sw.Do("*b.$.Member.Name$ = append(*b.$.Member.Name$, values[i])\n", memberParams)
		} else {
			sw.Do("b.$.Member.Name$ = append(b.$.Member.Name$, values[i])\n", memberParams)
		}
	}
	sw.Do("  }\n", memberParams)
	sw.Do("  return b\n", memberParams)
	sw.Do("}\n", memberParams)
}


generateMemberWithForMap

func (g *applyConfigurationGenerator) generateMemberWithForMap(sw *generator.SnippetWriter, memberParams memberParams) {
	sw.Do("// With$.Member.Name$ puts the entries into the $.Member.Name$ field in the declarative configuration\n", memberParams)
	sw.Do("// and returns the receiver, so that objects can be build by chaining \"With\" function invocations.\n", memberParams)
	sw.Do("// If called multiple times, the entries provided by each call will be put on the $.Member.Name$ field,\n", memberParams)
	sw.Do("// overwriting an existing map entries in $.Member.Name$ field with the same key.\n", memberParams)
	sw.Do("func (b *$.ApplyConfig.ApplyConfiguration|public$) With$.Member.Name$(entries $.MemberType|raw$) *$.ApplyConfig.ApplyConfiguration|public$ {\n", memberParams)
	g.ensureEnbedExistsIfApplicable(sw, memberParams)
	sw.Do("  if b.$.Member.Name$ == nil && len(entries) > 0 {\n", memberParams)
	sw.Do("    b.$.Member.Name$ = make($.MemberType|raw$, len(entries))\n", memberParams)
	sw.Do("  }\n", memberParams)
	sw.Do("  for k, v := range entries {\n", memberParams)
	sw.Do("    b.$.Member.Name$[k] = v\n", memberParams)
	sw.Do("  }\n", memberParams)
	sw.Do("  return b\n", memberParams)
	sw.Do("}\n", memberParams)
}


ensureEnbedExistsIfApplicable

func (g *applyConfigurationGenerator) ensureEnbedExistsIfApplicable(sw *generator.SnippetWriter, memberParams memberParams) {
	// Embedded types that are not inlined must be nillable so they are not included in the apply configuration
	// when all their fields are omitted.
	if memberParams.EmbeddedIn != nil && !memberParams.EmbeddedIn.JSONTags.inline {
		sw.Do("b.ensure$.MemberType.Elem|public$Exists()\n", memberParams.EmbeddedIn)
	}
}


generateClientgenExtract

func (g *applyConfigurationGenerator) generateClientgenExtract(sw *generator.SnippetWriter, typeParams TypeParams, includeStatus bool) {
	sw.Do(`
// Extract$.ApplyConfig.Type|public$ extracts the applied configuration owned by fieldManager from
// $.Struct|private$. If no managedFields are found in $.Struct|private$ for fieldManager, a
// $.ApplyConfig.ApplyConfiguration|public$ is returned with only the Name, Namespace (if applicable),
// APIVersion and Kind populated. It is possible that no managed fields were found for because other
// field managers have taken ownership of all the fields previously owned by fieldManager, or because
// the fieldManager never owned fields any fields.
// $.Struct|private$ must be a unmodified $.Struct|public$ API object that was retrieved from the Kubernetes API.
// Extract$.ApplyConfig.Type|public$ provides a way to perform a extract/modify-in-place/apply workflow.
// Note that an extracted apply configuration will contain fewer fields than what the fieldManager previously
// applied if another fieldManager has updated or force applied any of the previously applied fields.
// Experimental!
func Extract$.ApplyConfig.Type|public$($.Struct|private$ *$.Struct|raw$, fieldManager string) (*$.ApplyConfig.ApplyConfiguration|public$, error) {
	return extract$.ApplyConfig.Type|public$($.Struct|private$, fieldManager, "")
}`, typeParams)
	if includeStatus {
		sw.Do(`
// Extract$.ApplyConfig.Type|public$Status is the same as Extract$.ApplyConfig.Type|public$ except
// that it extracts the status subresource applied configuration.
// Experimental!
func Extract$.ApplyConfig.Type|public$Status($.Struct|private$ *$.Struct|raw$, fieldManager string) (*$.ApplyConfig.ApplyConfiguration|public$, error) {
	return extract$.ApplyConfig.Type|public$($.Struct|private$, fieldManager, "status")
}
`, typeParams)
	}
	sw.Do(`
func extract$.ApplyConfig.Type|public$($.Struct|private$ *$.Struct|raw$, fieldManager string, subresource string) (*$.ApplyConfig.ApplyConfiguration|public$, error) {
	b := &$.ApplyConfig.ApplyConfiguration|public${}
	err := $.ExtractInto|raw$($.Struct|private$, $.ParserFunc|raw$().Type("$.OpenAPIType$"), fieldManager, b, subresource)
	if err != nil {
		return nil, err
	}
	b.WithName($.Struct|private$.Name)
`, typeParams)
	if !typeParams.Tags.NonNamespaced {
		sw.Do("b.WithNamespace($.Struct|private$.Namespace)\n", typeParams)
	}
	sw.Do(`
	b.WithKind("$.ApplyConfig.Type|singularKind$")
	b.WithAPIVersion("$.APIVersion$")
	return b, nil
}
`, typeParams)
}


Filter

func (g *internalGenerator) Filter(*generator.Context, *types.Type) bool {
	// generate file exactly once
	if !g.filtered {
		g.filtered = true
		return true
	}
	return false
}


Namers

func (g *internalGenerator) Namers(*generator.Context) namer.NameSystems {
	return namer.NameSystems{
		"raw":          namer.NewRawNamer(g.outputPackage, g.imports),
		"singularKind": namer.NewPublicNamer(0),
	}
}


Imports

func (g *internalGenerator) Imports(*generator.Context) (imports []string) {
	return g.imports.ImportLines()
}


GenerateType

func (g *internalGenerator) GenerateType(c *generator.Context, _ *types.Type, w io.Writer) error {
	sw := generator.NewSnippetWriter(w, c, "{{", "}}")

	schema, err := schemaconv.ToSchema(g.typeModels.models)
	if err != nil {
		return err
	}
	schemaYAML, err := yaml.Marshal(schema)
	if err != nil {
		return err
	}
	sw.Do(schemaBlock, map[string]interface{}{
		"schemaYAML":    string(schemaYAML),
		"smdParser":     smdParser,
		"smdNewParser":  smdNewParser,
		"yamlObject":    yamlObject,
		"yamlUnmarshal": yamlUnmarshal,
	})

	return sw.Error()
}


String

func (t JSONTags) String() string {
	var tag string
	if !t.inline {
		tag += t.name
	}
	if t.omitempty {
		tag += ",omitempty"
	}
	if t.inline {
		tag += ",inline"
	}
	return tag
}


lookupJSONTags

func lookupJSONTags(m types.Member) (JSONTags, bool) {
	tag := reflect.StructTag(m.Tags).Get("json")
	if tag == "" || tag == "-" {
		return JSONTags{}, false
	}
	name, opts := parseTag(tag)
	if name == "" {
		name = m.Name
	}
	return JSONTags{
		name:      name,
		omit:      false,
		inline:    opts.Contains("inline"),
		omitempty: opts.Contains("omitempty"),
	}, true
}


parseTag

// parseTag splits a struct field's json tag into its name and
// comma-separated options.
func parseTag(tag string) (string, tagOptions) {
	if idx := strings.Index(tag, ","); idx != -1 {
		return tag[:idx], tagOptions(tag[idx+1:])
	}
	return tag, ""
}


Contains

// Contains reports whether a comma-separated listAlias of options
// contains a particular substr flag. substr must be surrounded by a
// string boundary or commas.
func (o tagOptions) Contains(optionName string) bool {
	if len(o) == 0 {
		return false
	}
	s := string(o)
	for s != "" {
		var next string
		i := strings.Index(s, ",")
		if i >= 0 {
			s, next = s[:i], s[i+1:]
		}
		if s == optionName {
			return true
		}
		s = next
	}
	return false
}


newTypeModels

func newTypeModels(openAPISchemaFilePath string, pkgTypes map[string]*types.Package) (*typeModels, error) {
	if len(openAPISchemaFilePath) == 0 {
		return emptyModels, nil // No Extract<type>() functions will be generated.
	}

	rawOpenAPISchema, err := os.ReadFile(openAPISchemaFilePath)
	if err != nil {
		return nil, fmt.Errorf("failed to read openapi-schema file: %w", err)
	}

	// Read in the provided openAPI schema.
	openAPISchema := &spec.Swagger{}
	err = json.Unmarshal(rawOpenAPISchema, openAPISchema)
	if err != nil {
		return nil, fmt.Errorf("failed to unmarshal typeModels JSON: %w", err)
	}

	// Build a mapping from openAPI type name to GVK.
	// Find the root types needed by by client-go for apply.
	gvkToOpenAPIType := map[gvk]string{}
	rootDefs := map[string]spec.Schema{}
	for _, p := range pkgTypes {
		gv := groupVersion(p)
		for _, t := range p.Types {
			tags := genclientTags(t)
			hasApply := tags.HasVerb("apply") || tags.HasVerb("applyStatus")
			if tags.GenerateClient && hasApply {
				openAPIType := friendlyName(typeName(t))
				gvk := gvk{
					group:   gv.Group.String(),
					version: gv.Version.String(),
					kind:    t.Name.Name,
				}
				rootDefs[openAPIType] = openAPISchema.Definitions[openAPIType]
				gvkToOpenAPIType[gvk] = openAPIType
			}
		}
	}

	// Trim the schema down to just the types needed by client-go for apply.
	requiredDefs := make(map[string]spec.Schema)
	for name, def := range rootDefs {
		requiredDefs[name] = def
		findReferenced(&def, openAPISchema.Definitions, requiredDefs)
	}
	openAPISchema.Definitions = requiredDefs

	// Convert the openAPI schema to the models format and validate it.
	models, err := toValidatedModels(openAPISchema)
	if err != nil {
		return nil, err
	}
	return &typeModels{models: models, gvkToOpenAPIType: gvkToOpenAPIType}, nil
}


toValidatedModels

func toValidatedModels(openAPISchema *spec.Swagger) (utilproto.Models, error) {
	// openapi_v2.ParseDocument only accepts a []byte of the JSON or YAML file to be parsed.
	// so we do an inefficient marshal back to json and then read it back in as yaml
	// but get the benefit of running the models through utilproto.NewOpenAPIData to
	// validate all the references between types
	rawMinimalOpenAPISchema, err := json.Marshal(openAPISchema)
	if err != nil {
		return nil, fmt.Errorf("failed to unmarshal openAPI as JSON: %w", err)
	}

	document, err := openapiv2.ParseDocument(rawMinimalOpenAPISchema)
	if err != nil {
		return nil, fmt.Errorf("failed to parse OpenAPI document for file: %w", err)
	}
	// Construct the models and validate all references are valid.
	models, err := utilproto.NewOpenAPIData(document)
	if err != nil {
		return nil, fmt.Errorf("failed to create OpenAPI models for file: %w", err)
	}
	return models, nil
}


findReferenced

// findReferenced recursively finds all schemas referenced from the given def.
// toValidatedModels makes sure no references get missed.
func findReferenced(def *spec.Schema, allSchemas, referencedOut map[string]spec.Schema) {
	// follow $ref, if any
	refPtr := def.Ref.GetPointer()
	if refPtr != nil && !refPtr.IsEmpty() {
		name := refPtr.String()
		if !strings.HasPrefix(name, "/definitions/") {
			return
		}
		name = strings.TrimPrefix(name, "/definitions/")
		schema, ok := allSchemas[name]
		if !ok {
			panic(fmt.Sprintf("allSchemas schema is missing referenced type: %s", name))
		}
		if _, ok := referencedOut[name]; !ok {
			referencedOut[name] = schema
			findReferenced(&schema, allSchemas, referencedOut)
		}
	}

	// follow any nested schemas
	if def.Items != nil {
		if def.Items.Schema != nil {
			findReferenced(def.Items.Schema, allSchemas, referencedOut)
		}
		for _, item := range def.Items.Schemas {
			findReferenced(&item, allSchemas, referencedOut)
		}
	}
	if def.AllOf != nil {
		for _, s := range def.AllOf {
			findReferenced(&s, allSchemas, referencedOut)
		}
	}
	if def.AnyOf != nil {
		for _, s := range def.AnyOf {
			findReferenced(&s, allSchemas, referencedOut)
		}
	}
	if def.OneOf != nil {
		for _, s := range def.OneOf {
			findReferenced(&s, allSchemas, referencedOut)
		}
	}
	if def.Not != nil {
		findReferenced(def.Not, allSchemas, referencedOut)
	}
	if def.Properties != nil {
		for _, prop := range def.Properties {
			findReferenced(&prop, allSchemas, referencedOut)
		}
	}
	if def.AdditionalProperties != nil && def.AdditionalProperties.Schema != nil {
		findReferenced(def.AdditionalProperties.Schema, allSchemas, referencedOut)
	}
	if def.PatternProperties != nil {
		for _, s := range def.PatternProperties {
			findReferenced(&s, allSchemas, referencedOut)
		}
	}
	if def.Dependencies != nil {
		for _, d := range def.Dependencies {
			if d.Schema != nil {
				findReferenced(d.Schema, allSchemas, referencedOut)
			}
		}
	}
	if def.AdditionalItems != nil && def.AdditionalItems.Schema != nil {
		findReferenced(def.AdditionalItems.Schema, allSchemas, referencedOut)
	}
	if def.Definitions != nil {
		for _, s := range def.Definitions {
			findReferenced(&s, allSchemas, referencedOut)
		}
	}
}


NameSystems

// NameSystems returns the name system used by the generators in this package.
func NameSystems() namer.NameSystems {
	return namer.NameSystems{
		"public":  namer.NewPublicNamer(0),
		"private": namer.NewPrivateNamer(0),
		"raw":     namer.NewRawNamer("", nil),
	}
}


DefaultNameSystem

// DefaultNameSystem returns the default name system for ordering the types to be
// processed by the generators in this package.
func DefaultNameSystem() string {
	return "public"
}


Packages

// Packages makes the client package definition.
func Packages(context *generator.Context, arguments *args.GeneratorArgs) generator.Packages {
	boilerplate, err := arguments.LoadGoBoilerplate()
	if err != nil {
		klog.Fatalf("Failed loading boilerplate: %v", err)
	}

	pkgTypes := packageTypesForInputDirs(context, arguments.InputDirs, arguments.OutputPackagePath)
	customArgs := arguments.CustomArgs.(*applygenargs.CustomArgs)
	initialTypes := customArgs.ExternalApplyConfigurations
	refs := refGraphForReachableTypes(context.Universe, pkgTypes, initialTypes)
	typeModels, err := newTypeModels(customArgs.OpenAPISchemaFilePath, pkgTypes)
	if err != nil {
		klog.Fatalf("Failed build type models from typeModels %s: %v", customArgs.OpenAPISchemaFilePath, err)
	}

	groupVersions := make(map[string]clientgentypes.GroupVersions)
	groupGoNames := make(map[string]string)
	applyConfigsForGroupVersion := make(map[clientgentypes.GroupVersion][]applyConfig)

	var packageList generator.Packages
	for pkg, p := range pkgTypes {
		gv := groupVersion(p)

		pkgType := types.Name{Name: gv.Group.PackageName(), Package: pkg}

		var toGenerate []applyConfig
		for _, t := range p.Types {
			// If we don't have an ObjectMeta field, we lack the information required to make the Apply or ApplyStatus call
			// to the kube-apiserver, so we don't need to generate the type at all
			clientTags := genclientTags(t)
			if clientTags.GenerateClient && !hasObjectMetaField(t) {
				klog.V(5).Infof("skipping type %v because does not have ObjectMeta", t)
				continue
			}
			if typePkg, ok := refs[t.Name]; ok {
				toGenerate = append(toGenerate, applyConfig{
					Type:               t,
					ApplyConfiguration: types.Ref(typePkg, t.Name.Name+ApplyConfigurationTypeSuffix),
				})
			}
		}
		if len(toGenerate) == 0 {
			continue // Don't generate empty packages
		}
		sort.Sort(applyConfigSort(toGenerate))

		// generate the apply configurations
		packageList = append(packageList, generatorForApplyConfigurationsPackage(arguments.OutputPackagePath, boilerplate, pkgType, gv, toGenerate, refs, typeModels))

		// group all the generated apply configurations by gv so ForKind() can be generated
		groupPackageName := gv.Group.NonEmpty()
		groupVersionsEntry, ok := groupVersions[groupPackageName]
		if !ok {
			groupVersionsEntry = clientgentypes.GroupVersions{
				PackageName: groupPackageName,
				Group:       gv.Group,
			}
		}
		groupVersionsEntry.Versions = append(groupVersionsEntry.Versions, clientgentypes.PackageVersion{
			Version: gv.Version,
			Package: path.Clean(p.Path),
		})

		groupGoNames[groupPackageName] = goName(gv, p)
		applyConfigsForGroupVersion[gv] = toGenerate
		groupVersions[groupPackageName] = groupVersionsEntry
	}

	// generate ForKind() utility function
	packageList = append(packageList, generatorForUtils(arguments.OutputPackagePath, boilerplate, groupVersions, applyConfigsForGroupVersion, groupGoNames))
	// generate internal embedded schema, required for generated Extract functions
	packageList = append(packageList, generatorForInternal(filepath.Join(arguments.OutputPackagePath, "internal"), boilerplate, typeModels))

	return packageList
}


friendlyName

func friendlyName(name string) string {
	nameParts := strings.Split(name, "/")
	// Reverse first part. e.g., io.k8s... instead of k8s.io...
	if len(nameParts) > 0 && strings.Contains(nameParts[0], ".") {
		parts := strings.Split(nameParts[0], ".")
		for i, j := 0, len(parts)-1; i < j; i, j = i+1, j-1 {
			parts[i], parts[j] = parts[j], parts[i]
		}
		nameParts[0] = strings.Join(parts, ".")
	}
	return strings.Join(nameParts, ".")
}


typeName

func typeName(t *types.Type) string {
	typePackage := t.Name.Package
	if strings.Contains(typePackage, "/vendor/") {
		typePackage = typePackage[strings.Index(typePackage, "/vendor/")+len("/vendor/"):]
	}
	return fmt.Sprintf("%s.%s", typePackage, t.Name.Name)
}


generatorForApplyConfigurationsPackage

func generatorForApplyConfigurationsPackage(outputPackagePath string, boilerplate []byte, packageName types.Name, gv clientgentypes.GroupVersion, typesToGenerate []applyConfig, refs refGraph, models *typeModels) *generator.DefaultPackage {
	return &generator.DefaultPackage{
		PackageName: gv.Version.PackageName(),
		PackagePath: packageName.Package,
		HeaderText:  boilerplate,
		GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) {
			for _, toGenerate := range typesToGenerate {
				var openAPIType *string
				gvk := gvk{
					group:   gv.Group.String(),
					version: gv.Version.String(),
					kind:    toGenerate.Type.Name.Name,
				}
				if v, ok := models.gvkToOpenAPIType[gvk]; ok {
					openAPIType = &v
				}

				generators = append(generators, &applyConfigurationGenerator{
					DefaultGen: generator.DefaultGen{
						OptionalName: strings.ToLower(toGenerate.Type.Name.Name),
					},
					outputPackage: outputPackagePath,
					localPackage:  packageName,
					groupVersion:  gv,
					applyConfig:   toGenerate,
					imports:       generator.NewImportTracker(),
					refGraph:      refs,
					openAPIType:   openAPIType,
				})
			}
			return generators
		},
	}
}


generatorForUtils

func generatorForUtils(outPackagePath string, boilerplate []byte, groupVersions map[string]clientgentypes.GroupVersions, applyConfigsForGroupVersion map[clientgentypes.GroupVersion][]applyConfig, groupGoNames map[string]string) *generator.DefaultPackage {
	return &generator.DefaultPackage{
		PackageName: filepath.Base(outPackagePath),
		PackagePath: outPackagePath,
		HeaderText:  boilerplate,
		GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) {
			generators = append(generators, &utilGenerator{
				DefaultGen: generator.DefaultGen{
					OptionalName: "utils",
				},
				outputPackage:        outPackagePath,
				imports:              generator.NewImportTracker(),
				groupVersions:        groupVersions,
				typesForGroupVersion: applyConfigsForGroupVersion,
				groupGoNames:         groupGoNames,
			})
			return generators
		},
	}
}


generatorForInternal

func generatorForInternal(outPackagePath string, boilerplate []byte, models *typeModels) *generator.DefaultPackage {
	return &generator.DefaultPackage{
		PackageName: filepath.Base(outPackagePath),
		PackagePath: outPackagePath,
		HeaderText:  boilerplate,
		GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) {
			generators = append(generators, &internalGenerator{
				DefaultGen: generator.DefaultGen{
					OptionalName: "internal",
				},
				outputPackage: outPackagePath,
				imports:       generator.NewImportTracker(),
				typeModels:    models,
			})
			return generators
		},
	}
}


goName

func goName(gv clientgentypes.GroupVersion, p *types.Package) string {
	goName := namer.IC(strings.Split(gv.Group.NonEmpty(), ".")[0])
	if override := types.ExtractCommentTags("+", p.Comments)["groupGoName"]; override != nil {
		goName = namer.IC(override[0])
	}
	return goName
}


packageTypesForInputDirs

func packageTypesForInputDirs(context *generator.Context, inputDirs []string, outputPath string) map[string]*types.Package {
	pkgTypes := map[string]*types.Package{}
	for _, inputDir := range inputDirs {
		p := context.Universe.Package(inputDir)
		internal := isInternalPackage(p)
		if internal {
			klog.Warningf("Skipping internal package: %s", p.Path)
			continue
		}
		// This is how the client generator finds the package we are creating. It uses the API package name, not the group name.
		// This matches the approach of the client-gen, so the two generator can work together.
		// For example, if openshift/api/cloudnetwork/v1 contains an apigroup cloud.network.openshift.io, the client-gen
		// builds a package called cloudnetwork/v1 to contain it. This change makes the applyconfiguration-gen use the same.
		_, gvPackageString := util.ParsePathGroupVersion(p.Path)
		pkg := filepath.Join(outputPath, strings.ToLower(gvPackageString))
		pkgTypes[pkg] = p
	}
	return pkgTypes
}


groupVersion

func groupVersion(p *types.Package) (gv clientgentypes.GroupVersion) {
	parts := strings.Split(p.Path, "/")
	gv.Group = clientgentypes.Group(parts[len(parts)-2])
	gv.Version = clientgentypes.Version(parts[len(parts)-1])

	// If there's a comment of the form "// +groupName=somegroup" or
	// "// +groupName=somegroup.foo.bar.io", use the first field (somegroup) as the name of the
	// group when generating.
	if override := types.ExtractCommentTags("+", p.Comments)["groupName"]; override != nil {
		gv.Group = clientgentypes.Group(override[0])
	}
	return gv
}


isInternalPackage

// isInternalPackage returns true if the package is an internal package
func isInternalPackage(p *types.Package) bool {
	for _, t := range p.Types {
		for _, member := range t.Members {
			if member.Name == "ObjectMeta" {
				return isInternal(member)
			}
		}
	}
	return false
}


isInternal

// isInternal returns true if the tags for a member do not contain a json tag
func isInternal(m types.Member) bool {
	_, ok := lookupJSONTags(m)
	return !ok
}


hasObjectMetaField

func hasObjectMetaField(t *types.Type) bool {
	for _, member := range t.Members {
		if objectMeta.Name == member.Type.Name && member.Embedded {
			return true
		}
	}
	return false
}


refGraphForReachableTypes

// refGraphForReachableTypes returns a refGraph that contains all reachable types from
// the root clientgen types of the provided packages.
func refGraphForReachableTypes(universe types.Universe, pkgTypes map[string]*types.Package, initialTypes map[types.Name]string) refGraph {
	var refs refGraph = initialTypes

	// Include only types that are reachable from the root clientgen types.
	// We don't want to generate apply configurations for types that are not reachable from a root
	// clientgen type.
	reachableTypes := map[types.Name]*types.Type{}
	for _, p := range pkgTypes {
		for _, t := range p.Types {
			tags := genclientTags(t)
			hasApply := tags.HasVerb("apply") || tags.HasVerb("applyStatus")
			if tags.GenerateClient && hasApply {
				findReachableTypes(t, reachableTypes)
			}
			// If any apply extensions have custom inputs, add them.
			for _, extension := range tags.Extensions {
				if extension.HasVerb("apply") {
					if len(extension.InputTypeOverride) > 0 {
						inputType := *t
						if name, pkg := extension.Input(); len(pkg) > 0 {
							inputType = *(universe.Type(types.Name{Package: pkg, Name: name}))
						} else {
							inputType.Name.Name = extension.InputTypeOverride
						}
						findReachableTypes(&inputType, reachableTypes)
					}
				}
			}
		}
	}
	for pkg, p := range pkgTypes {
		for _, t := range p.Types {
			if _, ok := reachableTypes[t.Name]; !ok {
				continue
			}
			if requiresApplyConfiguration(t) {
				refs[t.Name] = pkg
			}
		}
	}

	return refs
}


applyConfigForType

// applyConfigForType find the type used in the generate apply configurations for a field.
// This may either be an existing type or one of the other generated applyConfig types.
func (t refGraph) applyConfigForType(field *types.Type) *types.Type {
	switch field.Kind {
	case types.Struct:
		if pkg, ok := t[field.Name]; ok { // TODO(jpbetz): Refs to types defined in a separate system (e.g. TypeMeta if generating a 3rd party controller) end up referencing the go struct, not the apply configuration type
			return types.Ref(pkg, field.Name.Name+ApplyConfigurationTypeSuffix)
		}
		return field
	case types.Map:
		if _, ok := t[field.Elem.Name]; ok {
			return &types.Type{
				Kind: types.Map,
				Elem: t.applyConfigForType(field.Elem),
				Key:  t.applyConfigForType(field.Key),
			}
		}
		return field
	case types.Slice:
		if _, ok := t[field.Elem.Name]; ok {
			return &types.Type{
				Kind: types.Slice,
				Elem: t.applyConfigForType(field.Elem),
			}
		}
		return field
	case types.Pointer:
		return t.applyConfigForType(field.Elem)
	default:
		return field
	}
}


isApplyConfig

func (t refGraph) isApplyConfig(field *types.Type) bool {
	switch field.Kind {
	case types.Struct:
		_, ok := t[field.Name]
		return ok
	case types.Pointer:
		return t.isApplyConfig(field.Elem)
	}
	return false
}


genclientTags

// genclientTags returns the genclient Tags for the given type.
func genclientTags(t *types.Type) util.Tags {
	return util.MustParseClientGenTags(append(t.SecondClosestCommentLines, t.CommentLines...))
}


findReachableTypes

// findReachableTypes finds all types transitively reachable from a given root type, including
// the root type itself.
func findReachableTypes(t *types.Type, referencedTypes map[types.Name]*types.Type) {
	if _, ok := referencedTypes[t.Name]; ok {
		return
	}
	referencedTypes[t.Name] = t

	if t.Elem != nil {
		findReachableTypes(t.Elem, referencedTypes)
	}
	if t.Underlying != nil {
		findReachableTypes(t.Underlying, referencedTypes)
	}
	if t.Key != nil {
		findReachableTypes(t.Key, referencedTypes)
	}
	for _, m := range t.Members {
		findReachableTypes(m.Type, referencedTypes)
	}
}


requiresApplyConfiguration

// requiresApplyConfiguration returns true if a type applyConfig should be generated for the given type.
// types applyConfig are only generated for struct types that contain fields with json tags.
func requiresApplyConfiguration(t *types.Type) bool {
	for t.Kind == types.Alias {
		t = t.Underlying
	}
	if t.Kind != types.Struct {
		return false
	}
	if _, ok := excludeTypes[t.Name]; ok {
		return false
	}
	var hasJSONTaggedMembers bool
	for _, member := range t.Members {
		if _, ok := lookupJSONTags(member); ok {
			hasJSONTaggedMembers = true
		}
	}
	if !hasJSONTaggedMembers {
		return false
	}

	return true
}


Filter

func (g *utilGenerator) Filter(*generator.Context, *types.Type) bool {
	// generate file exactly once
	if !g.filtered {
		g.filtered = true
		return true
	}
	return false
}


Namers

func (g *utilGenerator) Namers(*generator.Context) namer.NameSystems {
	return namer.NameSystems{
		"raw":          namer.NewRawNamer(g.outputPackage, g.imports),
		"singularKind": namer.NewPublicNamer(0),
	}
}


Imports

func (g *utilGenerator) Imports(*generator.Context) (imports []string) {
	return g.imports.ImportLines()
}


Len

func (g groupSort) Len() int { return len(g) }


Less

func (g groupSort) Less(i, j int) bool {
	return strings.ToLower(g[i].Name) < strings.ToLower(g[j].Name)
}


Swap

func (g groupSort) Swap(i, j int) { g[i], g[j] = g[j], g[i] }


Len

func (v versionSort) Len() int { return len(v) }


Less

func (v versionSort) Less(i, j int) bool {
	return strings.ToLower(v[i].Name) < strings.ToLower(v[j].Name)
}


Swap

func (v versionSort) Swap(i, j int) { v[i], v[j] = v[j], v[i] }


Len

func (v applyConfigSort) Len() int { return len(v) }


Less

func (v applyConfigSort) Less(i, j int) bool {
	return strings.ToLower(v[i].Type.Name.Name) < strings.ToLower(v[j].Type.Name.Name)
}


Swap

func (v applyConfigSort) Swap(i, j int) { v[i], v[j] = v[j], v[i] }


GenerateType

func (g *utilGenerator) GenerateType(c *generator.Context, _ *types.Type, w io.Writer) error {
	sw := generator.NewSnippetWriter(w, c, "{{", "}}")

	var groups []group
	schemeGVs := make(map[*version]*types.Type)

	for groupPackageName, groupVersions := range g.groupVersions {
		group := group{
			GroupGoName: g.groupGoNames[groupPackageName],
			Name:        groupVersions.Group.NonEmpty(),
			Versions:    []*version{},
		}
		for _, v := range groupVersions.Versions {
			gv := clientgentypes.GroupVersion{Group: groupVersions.Group, Version: v.Version}
			version := &version{
				Name:      v.Version.NonEmpty(),
				GoName:    namer.IC(v.Version.NonEmpty()),
				Resources: g.typesForGroupVersion[gv],
			}
			schemeGVs[version] = c.Universe.Variable(types.Name{
				Package: g.typesForGroupVersion[gv][0].Type.Name.Package,
				Name:    "SchemeGroupVersion",
			})
			group.Versions = append(group.Versions, version)
		}
		sort.Sort(versionSort(group.Versions))
		groups = append(groups, group)
	}
	sort.Sort(groupSort(groups))

	m := map[string]interface{}{
		"groups":                 groups,
		"schemeGVs":              schemeGVs,
		"schemaGroupVersionKind": groupVersionKind,
		"applyConfiguration":     applyConfiguration,
	}
	sw.Do(forKindFunc, m)

	return sw.Error()
}


main

func main() {
	klog.InitFlags(nil)
	genericArgs, customArgs := generatorargs.NewDefaults()
	genericArgs.AddFlags(pflag.CommandLine)
	customArgs.AddFlags(pflag.CommandLine, "k8s.io/kubernetes/pkg/apis") // TODO: move this input path out of applyconfiguration-gen
	if err := flag.Set("logtostderr", "true"); err != nil {
		klog.Fatalf("Error: %v", err)
	}
	pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
	pflag.Parse()

	if err := generatorargs.Validate(genericArgs); err != nil {
		klog.Fatalf("Error: %v", err)
	}

	// Run it.
	if err := genericArgs.Execute(
		generators.NameSystems(),
		generators.DefaultNameSystem(),
		generators.Packages,
	); err != nil {
		klog.Fatalf("Error: %v", err)
	}
	klog.V(2).Info("Completed successfully.")
}


NewDefaults

func NewDefaults() (*args.GeneratorArgs, *CustomArgs) {
	genericArgs := args.Default().WithoutDefaultFlagParsing()
	customArgs := &CustomArgs{
		ClientsetName:             "internalclientset",
		ClientsetAPIPath:          "/apis",
		ClientsetOnly:             false,
		FakeClient:                true,
		PluralExceptions:          []string{"Endpoints:Endpoints"},
		ApplyConfigurationPackage: "",
	}
	genericArgs.CustomArgs = customArgs
	genericArgs.InputDirs = DefaultInputDirs

	if pkg := codegenutil.CurrentPackage(); len(pkg) != 0 {
		genericArgs.OutputPackagePath = path.Join(pkg, "pkg/client/clientset")
	}

	return genericArgs, customArgs
}


AddFlags

func (ca *CustomArgs) AddFlags(fs *pflag.FlagSet, inputBase string) {
	gvsBuilder := NewGroupVersionsBuilder(&ca.Groups)
	pflag.Var(NewGVPackagesValue(gvsBuilder, nil), "input", "group/versions that client-gen will generate clients for. At most one version per group is allowed. Specified in the format \"group1/version1,group2/version2...\".")
	pflag.Var(NewGVTypesValue(&ca.IncludedTypesOverrides, []string{}), "included-types-overrides", "list of group/version/type for which client should be generated. By default, client is generated for all types which have genclient in types.go. This overrides that. For each groupVersion in this list, only the types mentioned here will be included. The default check of genclient will be used for other group versions.")
	pflag.Var(NewInputBasePathValue(gvsBuilder, inputBase), "input-base", "base path to look for the api group.")
	pflag.StringVarP(&ca.ClientsetName, "clientset-name", "n", ca.ClientsetName, "the name of the generated clientset package.")
	pflag.StringVarP(&ca.ClientsetAPIPath, "clientset-api-path", "", ca.ClientsetAPIPath, "the value of default API HTTP path, starting with / and without trailing /.")
	pflag.BoolVar(&ca.ClientsetOnly, "clientset-only", ca.ClientsetOnly, "when set, client-gen only generates the clientset shell, without generating the individual typed clients")
	pflag.BoolVar(&ca.FakeClient, "fake-clientset", ca.FakeClient, "when set, client-gen will generate the fake clientset that can be used in tests")

	fs.StringSliceVar(&ca.PluralExceptions, "plural-exceptions", ca.PluralExceptions, "list of comma separated plural exception definitions in Type:PluralizedType form")
	fs.StringVar(&ca.ApplyConfigurationPackage, "apply-configuration-package", ca.ApplyConfigurationPackage, "optional package of apply configurations, generated by applyconfiguration-gen, that are required to generate Apply functions for each type in the clientset. By default Apply functions are not generated.")

	// support old flags
	fs.SetNormalizeFunc(mapFlagName("clientset-path", "output-package", fs.GetNormalizeFunc()))
}


Validate

func Validate(genericArgs *args.GeneratorArgs) error {
	customArgs := genericArgs.CustomArgs.(*CustomArgs)

	if len(genericArgs.OutputPackagePath) == 0 {
		return fmt.Errorf("output package cannot be empty")
	}
	if len(customArgs.ClientsetName) == 0 {
		return fmt.Errorf("clientset name cannot be empty")
	}
	if len(customArgs.ClientsetAPIPath) == 0 {
		return fmt.Errorf("clientset API path cannot be empty")
	}

	return nil
}


GroupVersionPackages

// GroupVersionPackages returns a map from GroupVersion to the package with the types.go.
func (ca *CustomArgs) GroupVersionPackages() map[types.GroupVersion]string {
	res := map[types.GroupVersion]string{}
	for _, pkg := range ca.Groups {
		for _, v := range pkg.Versions {
			res[types.GroupVersion{Group: pkg.Group, Version: v.Version}] = v.Package
		}
	}
	return res
}


mapFlagName

func mapFlagName(from, to string, old func(fs *pflag.FlagSet, name string) pflag.NormalizedName) func(fs *pflag.FlagSet, name string) pflag.NormalizedName {
	return func(fs *pflag.FlagSet, name string) pflag.NormalizedName {
		if name == from {
			name = to
		}
		return old(fs, name)
	}
}


NewInputBasePathValue

func NewInputBasePathValue(builder *groupVersionsBuilder, def string) *inputBasePathValue {
	v := &inputBasePathValue{
		builder: builder,
	}
	v.Set(def)
	return v
}


Set

func (s *inputBasePathValue) Set(val string) error {
	s.builder.importBasePath = val
	return s.builder.update()
}


Type

func (s *inputBasePathValue) Type() string {
	return "string"
}


String

func (s *inputBasePathValue) String() string {
	return s.builder.importBasePath
}


NewGVPackagesValue

func NewGVPackagesValue(builder *groupVersionsBuilder, def []string) *gvPackagesValue {
	gvp := new(gvPackagesValue)
	gvp.builder = builder
	if def != nil {
		if err := gvp.set(def); err != nil {
			panic(err)
		}
	}
	return gvp
}


set

func (s *gvPackagesValue) set(vs []string) error {
	if s.changed {
		s.groups = append(s.groups, vs...)
	} else {
		s.groups = append([]string(nil), vs...)
	}

	s.builder.groups = s.groups
	return s.builder.update()
}


Set

func (s *gvPackagesValue) Set(val string) error {
	vs, err := readAsCSV(val)
	if err != nil {
		return err
	}
	if err := s.set(vs); err != nil {
		return err
	}
	s.changed = true
	return nil
}


Type

func (s *gvPackagesValue) Type() string {
	return "stringSlice"
}


String

func (s *gvPackagesValue) String() string {
	str, _ := writeAsCSV(s.groups)
	return "[" + str + "]"
}


NewGroupVersionsBuilder

func NewGroupVersionsBuilder(groups *[]types.GroupVersions) *groupVersionsBuilder {
	return &groupVersionsBuilder{
		value: groups,
	}
}


update

func (p *groupVersionsBuilder) update() error {
	var seenGroups = make(map[types.Group]*types.GroupVersions)
	for _, v := range p.groups {
		pth, gvString := util.ParsePathGroupVersion(v)
		gv, err := types.ToGroupVersion(gvString)
		if err != nil {
			return err
		}

		versionPkg := types.PackageVersion{Package: path.Join(p.importBasePath, pth, gv.Group.NonEmpty(), gv.Version.String()), Version: gv.Version}
		if group, ok := seenGroups[gv.Group]; ok {
			seenGroups[gv.Group].Versions = append(group.Versions, versionPkg)
		} else {
			seenGroups[gv.Group] = &types.GroupVersions{
				PackageName: gv.Group.NonEmpty(),
				Group:       gv.Group,
				Versions:    []types.PackageVersion{versionPkg},
			}
		}
	}

	var groupNames []string
	for groupName := range seenGroups {
		groupNames = append(groupNames, groupName.String())
	}
	sort.Strings(groupNames)
	*p.value = []types.GroupVersions{}
	for _, groupName := range groupNames {
		*p.value = append(*p.value, *seenGroups[types.Group(groupName)])
	}

	return nil
}


readAsCSV

func readAsCSV(val string) ([]string, error) {
	if val == "" {
		return []string{}, nil
	}
	stringReader := strings.NewReader(val)
	csvReader := csv.NewReader(stringReader)
	return csvReader.Read()
}