mirror of
				https://github.com/k3s-io/kubernetes.git
				synced 2025-11-04 07:49:35 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			238 lines
		
	
	
		
			8.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			238 lines
		
	
	
		
			8.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
/*
 | 
						|
Copyright 2018 The Kubernetes Authors.
 | 
						|
 | 
						|
Licensed under the Apache License, Version 2.0 (the "License");
 | 
						|
you may not use this file except in compliance with the License.
 | 
						|
You may obtain a copy of the License at
 | 
						|
 | 
						|
    http://www.apache.org/licenses/LICENSE-2.0
 | 
						|
 | 
						|
Unless required by applicable law or agreed to in writing, software
 | 
						|
distributed under the License is distributed on an "AS IS" BASIS,
 | 
						|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
						|
See the License for the specific language governing permissions and
 | 
						|
limitations under the License.
 | 
						|
*/
 | 
						|
 | 
						|
package util
 | 
						|
 | 
						|
import (
 | 
						|
	"fmt"
 | 
						|
	"io"
 | 
						|
 | 
						|
	appsv1 "k8s.io/api/apps/v1"
 | 
						|
	appsv1beta1 "k8s.io/api/apps/v1beta1"
 | 
						|
	batchv1 "k8s.io/api/batch/v1"
 | 
						|
	batchv1beta1 "k8s.io/api/batch/v1beta1"
 | 
						|
	batchv2alpha1 "k8s.io/api/batch/v2alpha1"
 | 
						|
	extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
 | 
						|
	apierrors "k8s.io/apimachinery/pkg/api/errors"
 | 
						|
	"k8s.io/apimachinery/pkg/runtime/schema"
 | 
						|
	"k8s.io/client-go/discovery"
 | 
						|
	"k8s.io/kubernetes/pkg/kubectl"
 | 
						|
)
 | 
						|
 | 
						|
const (
 | 
						|
	// TODO(sig-cli): Enforce consistent naming for generators here.
 | 
						|
	// See discussion in https://github.com/kubernetes/kubernetes/issues/46237
 | 
						|
	// before you add any more.
 | 
						|
	RunV1GeneratorName                      = "run/v1"
 | 
						|
	RunPodV1GeneratorName                   = "run-pod/v1"
 | 
						|
	ServiceV1GeneratorName                  = "service/v1"
 | 
						|
	ServiceV2GeneratorName                  = "service/v2"
 | 
						|
	ServiceNodePortGeneratorV1Name          = "service-nodeport/v1"
 | 
						|
	ServiceClusterIPGeneratorV1Name         = "service-clusterip/v1"
 | 
						|
	ServiceLoadBalancerGeneratorV1Name      = "service-loadbalancer/v1"
 | 
						|
	ServiceExternalNameGeneratorV1Name      = "service-externalname/v1"
 | 
						|
	ServiceAccountV1GeneratorName           = "serviceaccount/v1"
 | 
						|
	HorizontalPodAutoscalerV1GeneratorName  = "horizontalpodautoscaler/v1"
 | 
						|
	DeploymentV1Beta1GeneratorName          = "deployment/v1beta1"
 | 
						|
	DeploymentAppsV1Beta1GeneratorName      = "deployment/apps.v1beta1"
 | 
						|
	DeploymentBasicV1Beta1GeneratorName     = "deployment-basic/v1beta1"
 | 
						|
	DeploymentBasicAppsV1Beta1GeneratorName = "deployment-basic/apps.v1beta1"
 | 
						|
	DeploymentBasicAppsV1GeneratorName      = "deployment-basic/apps.v1"
 | 
						|
	JobV1GeneratorName                      = "job/v1"
 | 
						|
	CronJobV2Alpha1GeneratorName            = "cronjob/v2alpha1"
 | 
						|
	CronJobV1Beta1GeneratorName             = "cronjob/v1beta1"
 | 
						|
	NamespaceV1GeneratorName                = "namespace/v1"
 | 
						|
	ResourceQuotaV1GeneratorName            = "resourcequotas/v1"
 | 
						|
	SecretV1GeneratorName                   = "secret/v1"
 | 
						|
	SecretForDockerRegistryV1GeneratorName  = "secret-for-docker-registry/v1"
 | 
						|
	SecretForTLSV1GeneratorName             = "secret-for-tls/v1"
 | 
						|
	ConfigMapV1GeneratorName                = "configmap/v1"
 | 
						|
	ClusterRoleBindingV1GeneratorName       = "clusterrolebinding.rbac.authorization.k8s.io/v1alpha1"
 | 
						|
	RoleBindingV1GeneratorName              = "rolebinding.rbac.authorization.k8s.io/v1alpha1"
 | 
						|
	PodDisruptionBudgetV1GeneratorName      = "poddisruptionbudget/v1beta1"
 | 
						|
	PodDisruptionBudgetV2GeneratorName      = "poddisruptionbudget/v1beta1/v2"
 | 
						|
	PriorityClassV1Alpha1GeneratorName      = "priorityclass/v1alpha1"
 | 
						|
)
 | 
						|
 | 
						|
// GeneratorFunc returns the generators for the provided command
 | 
						|
type GeneratorFunc func(cmdName string) map[string]kubectl.Generator
 | 
						|
 | 
						|
// GeneratorFn gives a way to easily override the function for unit testing if needed
 | 
						|
var GeneratorFn GeneratorFunc = defaultGenerators
 | 
						|
 | 
						|
// defaultGenerators returns the set of default generators for use in Factory instances
 | 
						|
func defaultGenerators(cmdName string) map[string]kubectl.Generator {
 | 
						|
	var generator map[string]kubectl.Generator
 | 
						|
	switch cmdName {
 | 
						|
	case "expose":
 | 
						|
		generator = map[string]kubectl.Generator{
 | 
						|
			ServiceV1GeneratorName: kubectl.ServiceGeneratorV1{},
 | 
						|
			ServiceV2GeneratorName: kubectl.ServiceGeneratorV2{},
 | 
						|
		}
 | 
						|
	case "service-clusterip":
 | 
						|
		generator = map[string]kubectl.Generator{
 | 
						|
			ServiceClusterIPGeneratorV1Name: kubectl.ServiceClusterIPGeneratorV1{},
 | 
						|
		}
 | 
						|
	case "service-nodeport":
 | 
						|
		generator = map[string]kubectl.Generator{
 | 
						|
			ServiceNodePortGeneratorV1Name: kubectl.ServiceNodePortGeneratorV1{},
 | 
						|
		}
 | 
						|
	case "service-loadbalancer":
 | 
						|
		generator = map[string]kubectl.Generator{
 | 
						|
			ServiceLoadBalancerGeneratorV1Name: kubectl.ServiceLoadBalancerGeneratorV1{},
 | 
						|
		}
 | 
						|
	case "deployment":
 | 
						|
		// Create Deployment has only StructuredGenerators and no
 | 
						|
		// param-based Generators.
 | 
						|
		// The StructuredGenerators are as follows (as of 2018-03-16):
 | 
						|
		// DeploymentBasicV1Beta1GeneratorName -> kubectl.DeploymentBasicGeneratorV1
 | 
						|
		// DeploymentBasicAppsV1Beta1GeneratorName -> kubectl.DeploymentBasicAppsGeneratorV1Beta1
 | 
						|
		// DeploymentBasicAppsV1GeneratorName -> kubectl.DeploymentBasicAppsGeneratorV1
 | 
						|
		generator = map[string]kubectl.Generator{}
 | 
						|
	case "run":
 | 
						|
		generator = map[string]kubectl.Generator{
 | 
						|
			RunV1GeneratorName:                 kubectl.BasicReplicationController{},
 | 
						|
			RunPodV1GeneratorName:              kubectl.BasicPod{},
 | 
						|
			DeploymentV1Beta1GeneratorName:     kubectl.DeploymentV1Beta1{},
 | 
						|
			DeploymentAppsV1Beta1GeneratorName: kubectl.DeploymentAppsV1Beta1{},
 | 
						|
			JobV1GeneratorName:                 kubectl.JobV1{},
 | 
						|
			CronJobV2Alpha1GeneratorName:       kubectl.CronJobV2Alpha1{},
 | 
						|
			CronJobV1Beta1GeneratorName:        kubectl.CronJobV1Beta1{},
 | 
						|
		}
 | 
						|
	case "namespace":
 | 
						|
		generator = map[string]kubectl.Generator{
 | 
						|
			NamespaceV1GeneratorName: kubectl.NamespaceGeneratorV1{},
 | 
						|
		}
 | 
						|
	case "quota":
 | 
						|
		generator = map[string]kubectl.Generator{
 | 
						|
			ResourceQuotaV1GeneratorName: kubectl.ResourceQuotaGeneratorV1{},
 | 
						|
		}
 | 
						|
	case "secret":
 | 
						|
		generator = map[string]kubectl.Generator{
 | 
						|
			SecretV1GeneratorName: kubectl.SecretGeneratorV1{},
 | 
						|
		}
 | 
						|
	case "secret-for-docker-registry":
 | 
						|
		generator = map[string]kubectl.Generator{
 | 
						|
			SecretForDockerRegistryV1GeneratorName: kubectl.SecretForDockerRegistryGeneratorV1{},
 | 
						|
		}
 | 
						|
	case "secret-for-tls":
 | 
						|
		generator = map[string]kubectl.Generator{
 | 
						|
			SecretForTLSV1GeneratorName: kubectl.SecretForTLSGeneratorV1{},
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return generator
 | 
						|
}
 | 
						|
 | 
						|
// FallbackGeneratorNameIfNecessary returns the name of the old generator
 | 
						|
// if server does not support new generator. Otherwise, the
 | 
						|
// generator string is returned unchanged.
 | 
						|
//
 | 
						|
// If the generator name is changed, print a warning message to let the user
 | 
						|
// know.
 | 
						|
func FallbackGeneratorNameIfNecessary(
 | 
						|
	generatorName string,
 | 
						|
	discoveryClient discovery.DiscoveryInterface,
 | 
						|
	cmdErr io.Writer,
 | 
						|
) (string, error) {
 | 
						|
	switch generatorName {
 | 
						|
	case DeploymentAppsV1Beta1GeneratorName:
 | 
						|
		hasResource, err := HasResource(discoveryClient, appsv1beta1.SchemeGroupVersion.WithResource("deployments"))
 | 
						|
		if err != nil {
 | 
						|
			return "", err
 | 
						|
		}
 | 
						|
		if !hasResource {
 | 
						|
			return FallbackGeneratorNameIfNecessary(DeploymentV1Beta1GeneratorName, discoveryClient, cmdErr)
 | 
						|
		}
 | 
						|
	case DeploymentV1Beta1GeneratorName:
 | 
						|
		hasResource, err := HasResource(discoveryClient, extensionsv1beta1.SchemeGroupVersion.WithResource("deployments"))
 | 
						|
		if err != nil {
 | 
						|
			return "", err
 | 
						|
		}
 | 
						|
		if !hasResource {
 | 
						|
			return RunV1GeneratorName, nil
 | 
						|
		}
 | 
						|
	case DeploymentBasicAppsV1GeneratorName:
 | 
						|
		hasResource, err := HasResource(discoveryClient, appsv1.SchemeGroupVersion.WithResource("deployments"))
 | 
						|
		if err != nil {
 | 
						|
			return "", err
 | 
						|
		}
 | 
						|
		if !hasResource {
 | 
						|
			return FallbackGeneratorNameIfNecessary(DeploymentBasicAppsV1Beta1GeneratorName, discoveryClient, cmdErr)
 | 
						|
		}
 | 
						|
	case DeploymentBasicAppsV1Beta1GeneratorName:
 | 
						|
		hasResource, err := HasResource(discoveryClient, appsv1beta1.SchemeGroupVersion.WithResource("deployments"))
 | 
						|
		if err != nil {
 | 
						|
			return "", err
 | 
						|
		}
 | 
						|
		if !hasResource {
 | 
						|
			return DeploymentBasicV1Beta1GeneratorName, nil
 | 
						|
		}
 | 
						|
	case JobV1GeneratorName:
 | 
						|
		hasResource, err := HasResource(discoveryClient, batchv1.SchemeGroupVersion.WithResource("jobs"))
 | 
						|
		if err != nil {
 | 
						|
			return "", err
 | 
						|
		}
 | 
						|
		if !hasResource {
 | 
						|
			return RunPodV1GeneratorName, nil
 | 
						|
		}
 | 
						|
	case CronJobV1Beta1GeneratorName:
 | 
						|
		hasResource, err := HasResource(discoveryClient, batchv1beta1.SchemeGroupVersion.WithResource("cronjobs"))
 | 
						|
		if err != nil {
 | 
						|
			return "", err
 | 
						|
		}
 | 
						|
		if !hasResource {
 | 
						|
			return FallbackGeneratorNameIfNecessary(CronJobV2Alpha1GeneratorName, discoveryClient, cmdErr)
 | 
						|
		}
 | 
						|
	case CronJobV2Alpha1GeneratorName:
 | 
						|
		hasResource, err := HasResource(discoveryClient, batchv2alpha1.SchemeGroupVersion.WithResource("cronjobs"))
 | 
						|
		if err != nil {
 | 
						|
			return "", err
 | 
						|
		}
 | 
						|
		if !hasResource {
 | 
						|
			return JobV1GeneratorName, nil
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return generatorName, nil
 | 
						|
}
 | 
						|
 | 
						|
func HasResource(client discovery.DiscoveryInterface, resource schema.GroupVersionResource) (bool, error) {
 | 
						|
	resources, err := client.ServerResourcesForGroupVersion(resource.GroupVersion().String())
 | 
						|
	if apierrors.IsNotFound(err) {
 | 
						|
		// entire group is missing
 | 
						|
		return false, nil
 | 
						|
	}
 | 
						|
	if err != nil {
 | 
						|
		// other errors error
 | 
						|
		return false, fmt.Errorf("failed to discover supported resources: %v", err)
 | 
						|
	}
 | 
						|
	for _, serverResource := range resources.APIResources {
 | 
						|
		if serverResource.Name == resource.Resource {
 | 
						|
			return true, nil
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return false, nil
 | 
						|
}
 | 
						|
 | 
						|
func Warning(cmdErr io.Writer, newGeneratorName, oldGeneratorName string) {
 | 
						|
	fmt.Fprintf(cmdErr, "WARNING: New generator %q specified, "+
 | 
						|
		"but it isn't available. "+
 | 
						|
		"Falling back to %q.\n",
 | 
						|
		newGeneratorName,
 | 
						|
		oldGeneratorName,
 | 
						|
	)
 | 
						|
}
 |