mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-10-31 18:28:13 +00:00 
			
		
		
		
	Deployments under apps/v1beta1 with new defaults
This commit is contained in:
		| @@ -49,7 +49,9 @@ import ( | |||||||
| 	serverstorage "k8s.io/apiserver/pkg/server/storage" | 	serverstorage "k8s.io/apiserver/pkg/server/storage" | ||||||
| 	"k8s.io/kubernetes/cmd/kube-apiserver/app/options" | 	"k8s.io/kubernetes/cmd/kube-apiserver/app/options" | ||||||
| 	"k8s.io/kubernetes/pkg/api" | 	"k8s.io/kubernetes/pkg/api" | ||||||
|  | 	"k8s.io/kubernetes/pkg/apis/apps" | ||||||
| 	"k8s.io/kubernetes/pkg/apis/batch" | 	"k8s.io/kubernetes/pkg/apis/batch" | ||||||
|  | 	"k8s.io/kubernetes/pkg/apis/extensions" | ||||||
| 	"k8s.io/kubernetes/pkg/capabilities" | 	"k8s.io/kubernetes/pkg/capabilities" | ||||||
| 	"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" | 	"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" | ||||||
| 	informers "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalversion" | 	informers "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalversion" | ||||||
| @@ -243,6 +245,8 @@ func BuildMasterConfig(s *options.ServerRunOptions) (*master.Config, informers.S | |||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, nil, fmt.Errorf("error in initializing storage factory: %s", err) | 		return nil, nil, fmt.Errorf("error in initializing storage factory: %s", err) | ||||||
| 	} | 	} | ||||||
|  | 	// keep Deployments in extensions for backwards compatibility, we'll have to migrate at some point, eventually | ||||||
|  | 	storageFactory.AddCohabitatingResources(extensions.Resource("deployments"), apps.Resource("deployments")) | ||||||
| 	for _, override := range s.Etcd.EtcdServersOverrides { | 	for _, override := range s.Etcd.EtcdServersOverrides { | ||||||
| 		tokens := strings.Split(override, "#") | 		tokens := strings.Split(override, "#") | ||||||
| 		if len(tokens) != 2 { | 		if len(tokens) != 2 { | ||||||
|   | |||||||
| @@ -1133,7 +1133,7 @@ run_kubectl_get_tests() { | |||||||
|   kube::test::if_has_string "${output_message}" "/apis/apps/v1beta1/namespaces/default/statefulsets 200 OK" |   kube::test::if_has_string "${output_message}" "/apis/apps/v1beta1/namespaces/default/statefulsets 200 OK" | ||||||
|   kube::test::if_has_string "${output_message}" "/apis/autoscaling/v1/namespaces/default/horizontalpodautoscalers 200" |   kube::test::if_has_string "${output_message}" "/apis/autoscaling/v1/namespaces/default/horizontalpodautoscalers 200" | ||||||
|   kube::test::if_has_string "${output_message}" "/apis/batch/v1/namespaces/default/jobs 200 OK" |   kube::test::if_has_string "${output_message}" "/apis/batch/v1/namespaces/default/jobs 200 OK" | ||||||
|   kube::test::if_has_string "${output_message}" "/apis/extensions/v1beta1/namespaces/default/deployments 200 OK" |   kube::test::if_has_string "${output_message}" "/apis/apps/v1beta1/namespaces/default/deployments 200 OK" | ||||||
|   kube::test::if_has_string "${output_message}" "/apis/extensions/v1beta1/namespaces/default/replicasets 200 OK" |   kube::test::if_has_string "${output_message}" "/apis/extensions/v1beta1/namespaces/default/replicasets 200 OK" | ||||||
|  |  | ||||||
|   ### Test --allow-missing-template-keys |   ### Test --allow-missing-template-keys | ||||||
| @@ -2270,6 +2270,11 @@ run_deployment_tests() { | |||||||
|   kubectl create deployment test-nginx --image=gcr.io/google-containers/nginx:test-cmd |   kubectl create deployment test-nginx --image=gcr.io/google-containers/nginx:test-cmd | ||||||
|   # Post-Condition: Deployment has 2 replicas defined in its spec. |   # Post-Condition: Deployment has 2 replicas defined in its spec. | ||||||
|   kube::test::get_object_assert 'deploy test-nginx' "{{$container_name_field}}" 'nginx' |   kube::test::get_object_assert 'deploy test-nginx' "{{$container_name_field}}" 'nginx' | ||||||
|  |   # Ensure we can interact with deployments through extensions and apps endpoints | ||||||
|  |   output_message=$(kubectl get deployment.extensions -o=jsonpath='{.items[0].apiVersion}' 2>&1 "${kube_flags[@]}") | ||||||
|  |   kube::test::if_has_string "${output_message}" 'extensions/v1beta1' | ||||||
|  |   output_message=$(kubectl get deployment.apps -o=jsonpath='{.items[0].apiVersion}' 2>&1 "${kube_flags[@]}") | ||||||
|  |   kube::test::if_has_string "${output_message}" 'apps/v1beta1' | ||||||
|   # Clean up |   # Clean up | ||||||
|   kubectl delete deployment test-nginx "${kube_flags[@]}" |   kubectl delete deployment test-nginx "${kube_flags[@]}" | ||||||
|  |  | ||||||
| @@ -2882,7 +2887,7 @@ runTests() { | |||||||
|     kube::test::get_object_assert rolebinding/sarole "{{range.subjects}}{{.namespace}}:{{end}}" 'otherns:' |     kube::test::get_object_assert rolebinding/sarole "{{range.subjects}}{{.namespace}}:{{end}}" 'otherns:' | ||||||
|     kube::test::get_object_assert rolebinding/sarole "{{range.subjects}}{{.name}}:{{end}}" 'sa-name:' |     kube::test::get_object_assert rolebinding/sarole "{{range.subjects}}{{.name}}:{{end}}" 'sa-name:' | ||||||
|   fi |   fi | ||||||
|    |  | ||||||
|   if kube::test::if_supports_resource "${roles}" ; then |   if kube::test::if_supports_resource "${roles}" ; then | ||||||
|     kubectl create "${kube_flags[@]}" role pod-admin --verb=* --resource=pods |     kubectl create "${kube_flags[@]}" role pod-admin --verb=* --resource=pods | ||||||
|     kube::test::get_object_assert role/pod-admin "{{range.rules}}{{range.verbs}}{{.}}:{{end}}{{end}}" '\*:' |     kube::test::get_object_assert role/pod-admin "{{range.rules}}{{range.verbs}}{{.}}:{{end}}{{end}}" '\*:' | ||||||
|   | |||||||
| @@ -105,6 +105,8 @@ func TestDefaulting(t *testing.T) { | |||||||
| 		{Group: "extensions", Version: "v1beta1", Kind: "DaemonSetList"}:                          {}, | 		{Group: "extensions", Version: "v1beta1", Kind: "DaemonSetList"}:                          {}, | ||||||
| 		{Group: "extensions", Version: "v1beta1", Kind: "Deployment"}:                             {}, | 		{Group: "extensions", Version: "v1beta1", Kind: "Deployment"}:                             {}, | ||||||
| 		{Group: "extensions", Version: "v1beta1", Kind: "DeploymentList"}:                         {}, | 		{Group: "extensions", Version: "v1beta1", Kind: "DeploymentList"}:                         {}, | ||||||
|  | 		{Group: "apps", Version: "v1beta1", Kind: "Deployment"}:                                   {}, | ||||||
|  | 		{Group: "apps", Version: "v1beta1", Kind: "DeploymentList"}:                               {}, | ||||||
| 		{Group: "extensions", Version: "v1beta1", Kind: "ReplicaSet"}:                             {}, | 		{Group: "extensions", Version: "v1beta1", Kind: "ReplicaSet"}:                             {}, | ||||||
| 		{Group: "extensions", Version: "v1beta1", Kind: "ReplicaSetList"}:                         {}, | 		{Group: "extensions", Version: "v1beta1", Kind: "ReplicaSetList"}:                         {}, | ||||||
| 		{Group: "rbac.authorization.k8s.io", Version: "v1alpha1", Kind: "ClusterRoleBinding"}:     {}, | 		{Group: "rbac.authorization.k8s.io", Version: "v1alpha1", Kind: "ClusterRoleBinding"}:     {}, | ||||||
|   | |||||||
| @@ -474,6 +474,13 @@ func coreFuncs(t apitesting.TestingCommon) []interface{} { | |||||||
|  |  | ||||||
| func extensionFuncs(t apitesting.TestingCommon) []interface{} { | func extensionFuncs(t apitesting.TestingCommon) []interface{} { | ||||||
| 	return []interface{}{ | 	return []interface{}{ | ||||||
|  | 		func(j *extensions.DeploymentSpec, c fuzz.Continue) { | ||||||
|  | 			c.FuzzNoCustom(j) // fuzz self without calling this function again | ||||||
|  | 			rhl := int32(c.Rand.Int31()) | ||||||
|  | 			pds := int32(c.Rand.Int31()) | ||||||
|  | 			j.RevisionHistoryLimit = &rhl | ||||||
|  | 			j.ProgressDeadlineSeconds = &pds | ||||||
|  | 		}, | ||||||
| 		func(j *extensions.DeploymentStrategy, c fuzz.Continue) { | 		func(j *extensions.DeploymentStrategy, c fuzz.Continue) { | ||||||
| 			c.FuzzNoCustom(j) // fuzz self without calling this function again | 			c.FuzzNoCustom(j) // fuzz self without calling this function again | ||||||
| 			// Ensure that strategyType is one of valid values. | 			// Ensure that strategyType is one of valid values. | ||||||
|   | |||||||
| @@ -19,6 +19,7 @@ package apps | |||||||
| import ( | import ( | ||||||
| 	"k8s.io/apimachinery/pkg/runtime" | 	"k8s.io/apimachinery/pkg/runtime" | ||||||
| 	"k8s.io/apimachinery/pkg/runtime/schema" | 	"k8s.io/apimachinery/pkg/runtime/schema" | ||||||
|  | 	"k8s.io/kubernetes/pkg/apis/extensions" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| var ( | var ( | ||||||
| @@ -46,6 +47,10 @@ func Resource(resource string) schema.GroupResource { | |||||||
| func addKnownTypes(scheme *runtime.Scheme) error { | func addKnownTypes(scheme *runtime.Scheme) error { | ||||||
| 	// TODO this will get cleaned up with the scheme types are fixed | 	// TODO this will get cleaned up with the scheme types are fixed | ||||||
| 	scheme.AddKnownTypes(SchemeGroupVersion, | 	scheme.AddKnownTypes(SchemeGroupVersion, | ||||||
|  | 		&extensions.Deployment{}, | ||||||
|  | 		&extensions.DeploymentList{}, | ||||||
|  | 		&extensions.DeploymentRollback{}, | ||||||
|  | 		&extensions.Scale{}, | ||||||
| 		&StatefulSet{}, | 		&StatefulSet{}, | ||||||
| 		&StatefulSetList{}, | 		&StatefulSetList{}, | ||||||
| 	) | 	) | ||||||
|   | |||||||
| @@ -22,9 +22,11 @@ import ( | |||||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||||
| 	"k8s.io/apimachinery/pkg/conversion" | 	"k8s.io/apimachinery/pkg/conversion" | ||||||
| 	"k8s.io/apimachinery/pkg/runtime" | 	"k8s.io/apimachinery/pkg/runtime" | ||||||
|  | 	"k8s.io/apimachinery/pkg/util/intstr" | ||||||
| 	"k8s.io/kubernetes/pkg/api" | 	"k8s.io/kubernetes/pkg/api" | ||||||
| 	v1 "k8s.io/kubernetes/pkg/api/v1" | 	"k8s.io/kubernetes/pkg/api/v1" | ||||||
| 	"k8s.io/kubernetes/pkg/apis/apps" | 	"k8s.io/kubernetes/pkg/apis/apps" | ||||||
|  | 	"k8s.io/kubernetes/pkg/apis/extensions" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func addConversionFuncs(scheme *runtime.Scheme) error { | func addConversionFuncs(scheme *runtime.Scheme) error { | ||||||
| @@ -35,21 +37,49 @@ func addConversionFuncs(scheme *runtime.Scheme) error { | |||||||
| 	err := scheme.AddConversionFuncs( | 	err := scheme.AddConversionFuncs( | ||||||
| 		Convert_v1beta1_StatefulSetSpec_To_apps_StatefulSetSpec, | 		Convert_v1beta1_StatefulSetSpec_To_apps_StatefulSetSpec, | ||||||
| 		Convert_apps_StatefulSetSpec_To_v1beta1_StatefulSetSpec, | 		Convert_apps_StatefulSetSpec_To_v1beta1_StatefulSetSpec, | ||||||
|  | 		// extensions | ||||||
|  | 		// TODO: below conversions should be dropped in favor of auto-generated | ||||||
|  | 		// ones, see https://github.com/kubernetes/kubernetextensionsssues/39865 | ||||||
|  | 		Convert_v1beta1_ScaleStatus_To_extensions_ScaleStatus, | ||||||
|  | 		Convert_extensions_ScaleStatus_To_v1beta1_ScaleStatus, | ||||||
|  | 		Convert_v1beta1_DeploymentSpec_To_extensions_DeploymentSpec, | ||||||
|  | 		Convert_extensions_DeploymentSpec_To_v1beta1_DeploymentSpec, | ||||||
|  | 		Convert_v1beta1_DeploymentStrategy_To_extensions_DeploymentStrategy, | ||||||
|  | 		Convert_extensions_DeploymentStrategy_To_v1beta1_DeploymentStrategy, | ||||||
|  | 		Convert_v1beta1_RollingUpdateDeployment_To_extensions_RollingUpdateDeployment, | ||||||
|  | 		Convert_extensions_RollingUpdateDeployment_To_v1beta1_RollingUpdateDeployment, | ||||||
| 	) | 	) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return scheme.AddFieldLabelConversionFunc("apps/v1beta1", "StatefulSet", | 	// Add field label conversions for kinds having selectable nothing but ObjectMeta fields. | ||||||
|  | 	err = scheme.AddFieldLabelConversionFunc("apps/v1beta1", "StatefulSet", | ||||||
| 		func(label, value string) (string, string, error) { | 		func(label, value string) (string, string, error) { | ||||||
| 			switch label { | 			switch label { | ||||||
| 			case "metadata.name", "metadata.namespace", "status.successful": | 			case "metadata.name", "metadata.namespace", "status.successful": | ||||||
| 				return label, value, nil | 				return label, value, nil | ||||||
| 			default: | 			default: | ||||||
| 				return "", "", fmt.Errorf("field label not supported: %s", label) | 				return "", "", fmt.Errorf("field label not supported for StatefulSet: %s", label) | ||||||
| 			} | 			} | ||||||
| 		}, | 		}) | ||||||
| 	) | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	err = api.Scheme.AddFieldLabelConversionFunc("apps/v1beta1", "Deployment", | ||||||
|  | 		func(label, value string) (string, string, error) { | ||||||
|  | 			switch label { | ||||||
|  | 			case "metadata.name", "metadata.namespace": | ||||||
|  | 				return label, value, nil | ||||||
|  | 			default: | ||||||
|  | 				return "", "", fmt.Errorf("field label %q not supported for Deployment", label) | ||||||
|  | 			} | ||||||
|  | 		}) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func Convert_v1beta1_StatefulSetSpec_To_apps_StatefulSetSpec(in *StatefulSetSpec, out *apps.StatefulSetSpec, s conversion.Scope) error { | func Convert_v1beta1_StatefulSetSpec_To_apps_StatefulSetSpec(in *StatefulSetSpec, out *apps.StatefulSetSpec, s conversion.Scope) error { | ||||||
| @@ -112,3 +142,156 @@ func Convert_apps_StatefulSetSpec_To_v1beta1_StatefulSetSpec(in *apps.StatefulSe | |||||||
| 	out.ServiceName = in.ServiceName | 	out.ServiceName = in.ServiceName | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func Convert_extensions_ScaleStatus_To_v1beta1_ScaleStatus(in *extensions.ScaleStatus, out *ScaleStatus, s conversion.Scope) error { | ||||||
|  | 	out.Replicas = int32(in.Replicas) | ||||||
|  |  | ||||||
|  | 	out.Selector = nil | ||||||
|  | 	out.TargetSelector = "" | ||||||
|  | 	if in.Selector != nil { | ||||||
|  | 		if in.Selector.MatchExpressions == nil || len(in.Selector.MatchExpressions) == 0 { | ||||||
|  | 			out.Selector = in.Selector.MatchLabels | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		selector, err := metav1.LabelSelectorAsSelector(in.Selector) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return fmt.Errorf("invalid label selector: %v", err) | ||||||
|  | 		} | ||||||
|  | 		out.TargetSelector = selector.String() | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func Convert_v1beta1_ScaleStatus_To_extensions_ScaleStatus(in *ScaleStatus, out *extensions.ScaleStatus, s conversion.Scope) error { | ||||||
|  | 	out.Replicas = in.Replicas | ||||||
|  |  | ||||||
|  | 	// Normally when 2 fields map to the same internal value we favor the old field, since | ||||||
|  | 	// old clients can't be expected to know about new fields but clients that know about the | ||||||
|  | 	// new field can be expected to know about the old field (though that's not quite true, due | ||||||
|  | 	// to kubectl apply). However, these fields are readonly, so any non-nil value should work. | ||||||
|  | 	if in.TargetSelector != "" { | ||||||
|  | 		labelSelector, err := metav1.ParseToLabelSelector(in.TargetSelector) | ||||||
|  | 		if err != nil { | ||||||
|  | 			out.Selector = nil | ||||||
|  | 			return fmt.Errorf("failed to parse target selector: %v", err) | ||||||
|  | 		} | ||||||
|  | 		out.Selector = labelSelector | ||||||
|  | 	} else if in.Selector != nil { | ||||||
|  | 		out.Selector = new(metav1.LabelSelector) | ||||||
|  | 		selector := make(map[string]string) | ||||||
|  | 		for key, val := range in.Selector { | ||||||
|  | 			selector[key] = val | ||||||
|  | 		} | ||||||
|  | 		out.Selector.MatchLabels = selector | ||||||
|  | 	} else { | ||||||
|  | 		out.Selector = nil | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func Convert_v1beta1_DeploymentSpec_To_extensions_DeploymentSpec(in *DeploymentSpec, out *extensions.DeploymentSpec, s conversion.Scope) error { | ||||||
|  | 	if in.Replicas != nil { | ||||||
|  | 		out.Replicas = *in.Replicas | ||||||
|  | 	} | ||||||
|  | 	out.Selector = in.Selector | ||||||
|  | 	if err := v1.Convert_v1_PodTemplateSpec_To_api_PodTemplateSpec(&in.Template, &out.Template, s); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	if err := Convert_v1beta1_DeploymentStrategy_To_extensions_DeploymentStrategy(&in.Strategy, &out.Strategy, s); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	out.RevisionHistoryLimit = in.RevisionHistoryLimit | ||||||
|  | 	out.MinReadySeconds = in.MinReadySeconds | ||||||
|  | 	out.Paused = in.Paused | ||||||
|  | 	if in.RollbackTo != nil { | ||||||
|  | 		out.RollbackTo = new(extensions.RollbackConfig) | ||||||
|  | 		out.RollbackTo.Revision = in.RollbackTo.Revision | ||||||
|  | 	} else { | ||||||
|  | 		out.RollbackTo = nil | ||||||
|  | 	} | ||||||
|  | 	if in.ProgressDeadlineSeconds != nil { | ||||||
|  | 		out.ProgressDeadlineSeconds = new(int32) | ||||||
|  | 		*out.ProgressDeadlineSeconds = *in.ProgressDeadlineSeconds | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func Convert_extensions_DeploymentSpec_To_v1beta1_DeploymentSpec(in *extensions.DeploymentSpec, out *DeploymentSpec, s conversion.Scope) error { | ||||||
|  | 	out.Replicas = &in.Replicas | ||||||
|  | 	out.Selector = in.Selector | ||||||
|  | 	if err := v1.Convert_api_PodTemplateSpec_To_v1_PodTemplateSpec(&in.Template, &out.Template, s); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	if err := Convert_extensions_DeploymentStrategy_To_v1beta1_DeploymentStrategy(&in.Strategy, &out.Strategy, s); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	if in.RevisionHistoryLimit != nil { | ||||||
|  | 		out.RevisionHistoryLimit = new(int32) | ||||||
|  | 		*out.RevisionHistoryLimit = int32(*in.RevisionHistoryLimit) | ||||||
|  | 	} | ||||||
|  | 	out.MinReadySeconds = int32(in.MinReadySeconds) | ||||||
|  | 	out.Paused = in.Paused | ||||||
|  | 	if in.RollbackTo != nil { | ||||||
|  | 		out.RollbackTo = new(RollbackConfig) | ||||||
|  | 		out.RollbackTo.Revision = int64(in.RollbackTo.Revision) | ||||||
|  | 	} else { | ||||||
|  | 		out.RollbackTo = nil | ||||||
|  | 	} | ||||||
|  | 	if in.ProgressDeadlineSeconds != nil { | ||||||
|  | 		out.ProgressDeadlineSeconds = new(int32) | ||||||
|  | 		*out.ProgressDeadlineSeconds = *in.ProgressDeadlineSeconds | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func Convert_extensions_DeploymentStrategy_To_v1beta1_DeploymentStrategy(in *extensions.DeploymentStrategy, out *DeploymentStrategy, s conversion.Scope) error { | ||||||
|  | 	out.Type = DeploymentStrategyType(in.Type) | ||||||
|  | 	if in.RollingUpdate != nil { | ||||||
|  | 		out.RollingUpdate = new(RollingUpdateDeployment) | ||||||
|  | 		if err := Convert_extensions_RollingUpdateDeployment_To_v1beta1_RollingUpdateDeployment(in.RollingUpdate, out.RollingUpdate, s); err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		out.RollingUpdate = nil | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func Convert_v1beta1_DeploymentStrategy_To_extensions_DeploymentStrategy(in *DeploymentStrategy, out *extensions.DeploymentStrategy, s conversion.Scope) error { | ||||||
|  | 	out.Type = extensions.DeploymentStrategyType(in.Type) | ||||||
|  | 	if in.RollingUpdate != nil { | ||||||
|  | 		out.RollingUpdate = new(extensions.RollingUpdateDeployment) | ||||||
|  | 		if err := Convert_v1beta1_RollingUpdateDeployment_To_extensions_RollingUpdateDeployment(in.RollingUpdate, out.RollingUpdate, s); err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		out.RollingUpdate = nil | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func Convert_v1beta1_RollingUpdateDeployment_To_extensions_RollingUpdateDeployment(in *RollingUpdateDeployment, out *extensions.RollingUpdateDeployment, s conversion.Scope) error { | ||||||
|  | 	if err := s.Convert(in.MaxUnavailable, &out.MaxUnavailable, 0); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	if err := s.Convert(in.MaxSurge, &out.MaxSurge, 0); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func Convert_extensions_RollingUpdateDeployment_To_v1beta1_RollingUpdateDeployment(in *extensions.RollingUpdateDeployment, out *RollingUpdateDeployment, s conversion.Scope) error { | ||||||
|  | 	if out.MaxUnavailable == nil { | ||||||
|  | 		out.MaxUnavailable = &intstr.IntOrString{} | ||||||
|  | 	} | ||||||
|  | 	if err := s.Convert(&in.MaxUnavailable, out.MaxUnavailable, 0); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	if out.MaxSurge == nil { | ||||||
|  | 		out.MaxSurge = &intstr.IntOrString{} | ||||||
|  | 	} | ||||||
|  | 	if err := s.Convert(&in.MaxSurge, out.MaxSurge, 0); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|   | |||||||
| @@ -19,12 +19,14 @@ package v1beta1 | |||||||
| import ( | import ( | ||||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||||
| 	"k8s.io/apimachinery/pkg/runtime" | 	"k8s.io/apimachinery/pkg/runtime" | ||||||
|  | 	"k8s.io/apimachinery/pkg/util/intstr" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func addDefaultingFuncs(scheme *runtime.Scheme) error { | func addDefaultingFuncs(scheme *runtime.Scheme) error { | ||||||
| 	RegisterDefaults(scheme) | 	RegisterDefaults(scheme) | ||||||
| 	return scheme.AddDefaultingFuncs( | 	return scheme.AddDefaultingFuncs( | ||||||
| 		SetDefaults_StatefulSet, | 		SetDefaults_StatefulSet, | ||||||
|  | 		SetDefaults_Deployment, | ||||||
| 	) | 	) | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -45,3 +47,57 @@ func SetDefaults_StatefulSet(obj *StatefulSet) { | |||||||
| 		*obj.Spec.Replicas = 1 | 		*obj.Spec.Replicas = 1 | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // SetDefaults_Deployment sets additional defaults compared to its counterpart | ||||||
|  | // in extensions. These addons are: | ||||||
|  | // - MaxUnavailable during rolling update set to 25% (1 in extensions) | ||||||
|  | // - MaxSurge value during rolling update set to 25% (1 in extensions) | ||||||
|  | // - RevisionHistoryLimit set to 2 (not set in extensions) | ||||||
|  | // - ProgressDeadlineSeconds set to 600s (not set in extensions) | ||||||
|  | func SetDefaults_Deployment(obj *Deployment) { | ||||||
|  | 	// Default labels and selector to labels from pod template spec. | ||||||
|  | 	labels := obj.Spec.Template.Labels | ||||||
|  |  | ||||||
|  | 	if labels != nil { | ||||||
|  | 		if obj.Spec.Selector == nil { | ||||||
|  | 			obj.Spec.Selector = &metav1.LabelSelector{MatchLabels: labels} | ||||||
|  | 		} | ||||||
|  | 		if len(obj.Labels) == 0 { | ||||||
|  | 			obj.Labels = labels | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	// Set DeploymentSpec.Replicas to 1 if it is not set. | ||||||
|  | 	if obj.Spec.Replicas == nil { | ||||||
|  | 		obj.Spec.Replicas = new(int32) | ||||||
|  | 		*obj.Spec.Replicas = 1 | ||||||
|  | 	} | ||||||
|  | 	strategy := &obj.Spec.Strategy | ||||||
|  | 	// Set default DeploymentStrategyType as RollingUpdate. | ||||||
|  | 	if strategy.Type == "" { | ||||||
|  | 		strategy.Type = RollingUpdateDeploymentStrategyType | ||||||
|  | 	} | ||||||
|  | 	if strategy.Type == RollingUpdateDeploymentStrategyType { | ||||||
|  | 		if strategy.RollingUpdate == nil { | ||||||
|  | 			rollingUpdate := RollingUpdateDeployment{} | ||||||
|  | 			strategy.RollingUpdate = &rollingUpdate | ||||||
|  | 		} | ||||||
|  | 		if strategy.RollingUpdate.MaxUnavailable == nil { | ||||||
|  | 			// Set default MaxUnavailable as 25% by default. | ||||||
|  | 			maxUnavailable := intstr.FromString("25%") | ||||||
|  | 			strategy.RollingUpdate.MaxUnavailable = &maxUnavailable | ||||||
|  | 		} | ||||||
|  | 		if strategy.RollingUpdate.MaxSurge == nil { | ||||||
|  | 			// Set default MaxSurge as 25% by default. | ||||||
|  | 			maxSurge := intstr.FromString("25%") | ||||||
|  | 			strategy.RollingUpdate.MaxSurge = &maxSurge | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if obj.Spec.RevisionHistoryLimit == nil { | ||||||
|  | 		obj.Spec.RevisionHistoryLimit = new(int32) | ||||||
|  | 		*obj.Spec.RevisionHistoryLimit = 2 | ||||||
|  | 	} | ||||||
|  | 	if obj.Spec.ProgressDeadlineSeconds == nil { | ||||||
|  | 		obj.Spec.ProgressDeadlineSeconds = new(int32) | ||||||
|  | 		*obj.Spec.ProgressDeadlineSeconds = 600 | ||||||
|  | 	} | ||||||
|  | } | ||||||
|   | |||||||
							
								
								
									
										219
									
								
								pkg/apis/apps/v1beta1/defaults_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										219
									
								
								pkg/apis/apps/v1beta1/defaults_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,219 @@ | |||||||
|  | /* | ||||||
|  | Copyright 2017 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 v1beta1_test | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"reflect" | ||||||
|  | 	"testing" | ||||||
|  |  | ||||||
|  | 	"k8s.io/apimachinery/pkg/runtime" | ||||||
|  | 	"k8s.io/apimachinery/pkg/util/intstr" | ||||||
|  | 	"k8s.io/kubernetes/pkg/api" | ||||||
|  | 	_ "k8s.io/kubernetes/pkg/api/install" | ||||||
|  | 	"k8s.io/kubernetes/pkg/api/v1" | ||||||
|  | 	_ "k8s.io/kubernetes/pkg/apis/apps/install" | ||||||
|  | 	. "k8s.io/kubernetes/pkg/apis/apps/v1beta1" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestSetDefaultDeployment(t *testing.T) { | ||||||
|  | 	defaultIntOrString := intstr.FromString("25%") | ||||||
|  | 	differentIntOrString := intstr.FromInt(5) | ||||||
|  | 	period := int64(v1.DefaultTerminationGracePeriodSeconds) | ||||||
|  | 	defaultTemplate := v1.PodTemplateSpec{ | ||||||
|  | 		Spec: v1.PodSpec{ | ||||||
|  | 			DNSPolicy:                     v1.DNSClusterFirst, | ||||||
|  | 			RestartPolicy:                 v1.RestartPolicyAlways, | ||||||
|  | 			SecurityContext:               &v1.PodSecurityContext{}, | ||||||
|  | 			TerminationGracePeriodSeconds: &period, | ||||||
|  | 			SchedulerName:                 api.DefaultSchedulerName, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 	tests := []struct { | ||||||
|  | 		original *Deployment | ||||||
|  | 		expected *Deployment | ||||||
|  | 	}{ | ||||||
|  | 		{ | ||||||
|  | 			original: &Deployment{}, | ||||||
|  | 			expected: &Deployment{ | ||||||
|  | 				Spec: DeploymentSpec{ | ||||||
|  | 					Replicas: newInt32(1), | ||||||
|  | 					Strategy: DeploymentStrategy{ | ||||||
|  | 						Type: RollingUpdateDeploymentStrategyType, | ||||||
|  | 						RollingUpdate: &RollingUpdateDeployment{ | ||||||
|  | 							MaxSurge:       &defaultIntOrString, | ||||||
|  | 							MaxUnavailable: &defaultIntOrString, | ||||||
|  | 						}, | ||||||
|  | 					}, | ||||||
|  | 					RevisionHistoryLimit:    newInt32(2), | ||||||
|  | 					ProgressDeadlineSeconds: newInt32(600), | ||||||
|  | 					Template:                defaultTemplate, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			original: &Deployment{ | ||||||
|  | 				Spec: DeploymentSpec{ | ||||||
|  | 					Replicas: newInt32(5), | ||||||
|  | 					Strategy: DeploymentStrategy{ | ||||||
|  | 						RollingUpdate: &RollingUpdateDeployment{ | ||||||
|  | 							MaxSurge: &differentIntOrString, | ||||||
|  | 						}, | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 			expected: &Deployment{ | ||||||
|  | 				Spec: DeploymentSpec{ | ||||||
|  | 					Replicas: newInt32(5), | ||||||
|  | 					Strategy: DeploymentStrategy{ | ||||||
|  | 						Type: RollingUpdateDeploymentStrategyType, | ||||||
|  | 						RollingUpdate: &RollingUpdateDeployment{ | ||||||
|  | 							MaxSurge:       &differentIntOrString, | ||||||
|  | 							MaxUnavailable: &defaultIntOrString, | ||||||
|  | 						}, | ||||||
|  | 					}, | ||||||
|  | 					RevisionHistoryLimit:    newInt32(2), | ||||||
|  | 					ProgressDeadlineSeconds: newInt32(600), | ||||||
|  | 					Template:                defaultTemplate, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			original: &Deployment{ | ||||||
|  | 				Spec: DeploymentSpec{ | ||||||
|  | 					Replicas: newInt32(3), | ||||||
|  | 					Strategy: DeploymentStrategy{ | ||||||
|  | 						Type:          RollingUpdateDeploymentStrategyType, | ||||||
|  | 						RollingUpdate: nil, | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 			expected: &Deployment{ | ||||||
|  | 				Spec: DeploymentSpec{ | ||||||
|  | 					Replicas: newInt32(3), | ||||||
|  | 					Strategy: DeploymentStrategy{ | ||||||
|  | 						Type: RollingUpdateDeploymentStrategyType, | ||||||
|  | 						RollingUpdate: &RollingUpdateDeployment{ | ||||||
|  | 							MaxSurge:       &defaultIntOrString, | ||||||
|  | 							MaxUnavailable: &defaultIntOrString, | ||||||
|  | 						}, | ||||||
|  | 					}, | ||||||
|  | 					RevisionHistoryLimit:    newInt32(2), | ||||||
|  | 					ProgressDeadlineSeconds: newInt32(600), | ||||||
|  | 					Template:                defaultTemplate, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			original: &Deployment{ | ||||||
|  | 				Spec: DeploymentSpec{ | ||||||
|  | 					Replicas: newInt32(5), | ||||||
|  | 					Strategy: DeploymentStrategy{ | ||||||
|  | 						Type: RecreateDeploymentStrategyType, | ||||||
|  | 					}, | ||||||
|  | 					RevisionHistoryLimit: newInt32(0), | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 			expected: &Deployment{ | ||||||
|  | 				Spec: DeploymentSpec{ | ||||||
|  | 					Replicas: newInt32(5), | ||||||
|  | 					Strategy: DeploymentStrategy{ | ||||||
|  | 						Type: RecreateDeploymentStrategyType, | ||||||
|  | 					}, | ||||||
|  | 					RevisionHistoryLimit:    newInt32(0), | ||||||
|  | 					ProgressDeadlineSeconds: newInt32(600), | ||||||
|  | 					Template:                defaultTemplate, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			original: &Deployment{ | ||||||
|  | 				Spec: DeploymentSpec{ | ||||||
|  | 					Replicas: newInt32(5), | ||||||
|  | 					Strategy: DeploymentStrategy{ | ||||||
|  | 						Type: RecreateDeploymentStrategyType, | ||||||
|  | 					}, | ||||||
|  | 					ProgressDeadlineSeconds: newInt32(30), | ||||||
|  | 					RevisionHistoryLimit:    newInt32(2), | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 			expected: &Deployment{ | ||||||
|  | 				Spec: DeploymentSpec{ | ||||||
|  | 					Replicas: newInt32(5), | ||||||
|  | 					Strategy: DeploymentStrategy{ | ||||||
|  | 						Type: RecreateDeploymentStrategyType, | ||||||
|  | 					}, | ||||||
|  | 					ProgressDeadlineSeconds: newInt32(30), | ||||||
|  | 					RevisionHistoryLimit:    newInt32(2), | ||||||
|  | 					Template:                defaultTemplate, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, test := range tests { | ||||||
|  | 		original := test.original | ||||||
|  | 		expected := test.expected | ||||||
|  | 		obj2 := roundTrip(t, runtime.Object(original)) | ||||||
|  | 		got, ok := obj2.(*Deployment) | ||||||
|  | 		if !ok { | ||||||
|  | 			t.Errorf("unexpected object: %v", got) | ||||||
|  | 			t.FailNow() | ||||||
|  | 		} | ||||||
|  | 		if !reflect.DeepEqual(got.Spec, expected.Spec) { | ||||||
|  | 			t.Errorf("object mismatch!\nexpected:\n\t%+v\ngot:\n\t%+v", got.Spec, expected.Spec) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestDefaultDeploymentAvailability(t *testing.T) { | ||||||
|  | 	d := roundTrip(t, runtime.Object(&Deployment{})).(*Deployment) | ||||||
|  |  | ||||||
|  | 	maxUnavailable, err := intstr.GetValueFromIntOrPercent(d.Spec.Strategy.RollingUpdate.MaxUnavailable, int(*(d.Spec.Replicas)), false) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatalf("unexpected error: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if *(d.Spec.Replicas)-int32(maxUnavailable) <= 0 { | ||||||
|  | 		t.Fatalf("the default value of maxUnavailable can lead to no active replicas during rolling update") | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func roundTrip(t *testing.T, obj runtime.Object) runtime.Object { | ||||||
|  | 	data, err := runtime.Encode(api.Codecs.LegacyCodec(SchemeGroupVersion), obj) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("%v\n %#v", err, obj) | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	obj2, err := runtime.Decode(api.Codecs.UniversalDecoder(), data) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("%v\nData: %s\nSource: %#v", err, string(data), obj) | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	obj3 := reflect.New(reflect.TypeOf(obj).Elem()).Interface().(runtime.Object) | ||||||
|  | 	err = api.Scheme.Convert(obj2, obj3, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("%v\nSource: %#v", err, obj2) | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	return obj3 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func newInt32(val int32) *int32 { | ||||||
|  | 	p := new(int32) | ||||||
|  | 	*p = val | ||||||
|  | 	return p | ||||||
|  | } | ||||||
| @@ -41,12 +41,13 @@ var ( | |||||||
| // Adds the list of known types to api.Scheme. | // Adds the list of known types to api.Scheme. | ||||||
| func addKnownTypes(scheme *runtime.Scheme) error { | func addKnownTypes(scheme *runtime.Scheme) error { | ||||||
| 	scheme.AddKnownTypes(SchemeGroupVersion, | 	scheme.AddKnownTypes(SchemeGroupVersion, | ||||||
|  | 		&Deployment{}, | ||||||
|  | 		&DeploymentList{}, | ||||||
|  | 		&DeploymentRollback{}, | ||||||
|  | 		&Scale{}, | ||||||
| 		&StatefulSet{}, | 		&StatefulSet{}, | ||||||
| 		&StatefulSetList{}, | 		&StatefulSetList{}, | ||||||
| 	) | 	) | ||||||
| 	metav1.AddToGroupVersion(scheme, SchemeGroupVersion) | 	metav1.AddToGroupVersion(scheme, SchemeGroupVersion) | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func (obj *StatefulSet) GetObjectKind() schema.ObjectKind     { return &obj.TypeMeta } |  | ||||||
| func (obj *StatefulSetList) GetObjectKind() schema.ObjectKind { return &obj.TypeMeta } |  | ||||||
|   | |||||||
| @@ -18,6 +18,7 @@ package v1beta1 | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||||
|  | 	"k8s.io/apimachinery/pkg/util/intstr" | ||||||
| 	"k8s.io/kubernetes/pkg/api/v1" | 	"k8s.io/kubernetes/pkg/api/v1" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -26,6 +27,51 @@ const ( | |||||||
| 	StatefulSetInitAnnotation = "pod.alpha.kubernetes.io/initialized" | 	StatefulSetInitAnnotation = "pod.alpha.kubernetes.io/initialized" | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | // ScaleSpec describes the attributes of a scale subresource | ||||||
|  | type ScaleSpec struct { | ||||||
|  | 	// desired number of instances for the scaled object. | ||||||
|  | 	// +optional | ||||||
|  | 	Replicas int32 `json:"replicas,omitempty" protobuf:"varint,1,opt,name=replicas"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ScaleStatus represents the current status of a scale subresource. | ||||||
|  | type ScaleStatus struct { | ||||||
|  | 	// actual number of observed instances of the scaled object. | ||||||
|  | 	Replicas int32 `json:"replicas" protobuf:"varint,1,opt,name=replicas"` | ||||||
|  |  | ||||||
|  | 	// label query over pods that should match the replicas count. More info: http://kubernetes.io/docs/user-guide/labels#label-selectors | ||||||
|  | 	// +optional | ||||||
|  | 	Selector map[string]string `json:"selector,omitempty" protobuf:"bytes,2,rep,name=selector"` | ||||||
|  |  | ||||||
|  | 	// label selector for pods that should match the replicas count. This is a serializated | ||||||
|  | 	// version of both map-based and more expressive set-based selectors. This is done to | ||||||
|  | 	// avoid introspection in the clients. The string will be in the same format as the | ||||||
|  | 	// query-param syntax. If the target type only supports map-based selectors, both this | ||||||
|  | 	// field and map-based selector field are populated. | ||||||
|  | 	// More info: http://kubernetes.io/docs/user-guide/labels#label-selectors | ||||||
|  | 	// +optional | ||||||
|  | 	TargetSelector string `json:"targetSelector,omitempty" protobuf:"bytes,3,opt,name=targetSelector"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // +genclient=true | ||||||
|  | // +noMethods=true | ||||||
|  |  | ||||||
|  | // Scale represents a scaling request for a resource. | ||||||
|  | type Scale struct { | ||||||
|  | 	metav1.TypeMeta `json:",inline"` | ||||||
|  | 	// Standard object metadata; More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata. | ||||||
|  | 	// +optional | ||||||
|  | 	metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` | ||||||
|  |  | ||||||
|  | 	// defines the behavior of the scale. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status. | ||||||
|  | 	// +optional | ||||||
|  | 	Spec ScaleSpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"` | ||||||
|  |  | ||||||
|  | 	// current status of the scale. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status. Read-only. | ||||||
|  | 	// +optional | ||||||
|  | 	Status ScaleStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"` | ||||||
|  | } | ||||||
|  |  | ||||||
| // +genclient=true | // +genclient=true | ||||||
|  |  | ||||||
| // StatefulSet represents a set of pods with consistent identities. | // StatefulSet represents a set of pods with consistent identities. | ||||||
| @@ -106,3 +152,224 @@ type StatefulSetList struct { | |||||||
| 	metav1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` | 	metav1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` | ||||||
| 	Items           []StatefulSet `json:"items" protobuf:"bytes,2,rep,name=items"` | 	Items           []StatefulSet `json:"items" protobuf:"bytes,2,rep,name=items"` | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // +genclient=true | ||||||
|  |  | ||||||
|  | // Deployment enables declarative updates for Pods and ReplicaSets. | ||||||
|  | type Deployment struct { | ||||||
|  | 	metav1.TypeMeta `json:",inline"` | ||||||
|  | 	// Standard object metadata. | ||||||
|  | 	// +optional | ||||||
|  | 	metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` | ||||||
|  |  | ||||||
|  | 	// Specification of the desired behavior of the Deployment. | ||||||
|  | 	// +optional | ||||||
|  | 	Spec DeploymentSpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"` | ||||||
|  |  | ||||||
|  | 	// Most recently observed status of the Deployment. | ||||||
|  | 	// +optional | ||||||
|  | 	Status DeploymentStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // DeploymentSpec is the specification of the desired behavior of the Deployment. | ||||||
|  | type DeploymentSpec struct { | ||||||
|  | 	// Number of desired pods. This is a pointer to distinguish between explicit | ||||||
|  | 	// zero and not specified. Defaults to 1. | ||||||
|  | 	// +optional | ||||||
|  | 	Replicas *int32 `json:"replicas,omitempty" protobuf:"varint,1,opt,name=replicas"` | ||||||
|  |  | ||||||
|  | 	// Label selector for pods. Existing ReplicaSets whose pods are | ||||||
|  | 	// selected by this will be the ones affected by this deployment. | ||||||
|  | 	// +optional | ||||||
|  | 	Selector *metav1.LabelSelector `json:"selector,omitempty" protobuf:"bytes,2,opt,name=selector"` | ||||||
|  |  | ||||||
|  | 	// Template describes the pods that will be created. | ||||||
|  | 	Template v1.PodTemplateSpec `json:"template" protobuf:"bytes,3,opt,name=template"` | ||||||
|  |  | ||||||
|  | 	// The deployment strategy to use to replace existing pods with new ones. | ||||||
|  | 	// +optional | ||||||
|  | 	Strategy DeploymentStrategy `json:"strategy,omitempty" protobuf:"bytes,4,opt,name=strategy"` | ||||||
|  |  | ||||||
|  | 	// Minimum number of seconds for which a newly created pod should be ready | ||||||
|  | 	// without any of its container crashing, for it to be considered available. | ||||||
|  | 	// Defaults to 0 (pod will be considered available as soon as it is ready) | ||||||
|  | 	// +optional | ||||||
|  | 	MinReadySeconds int32 `json:"minReadySeconds,omitempty" protobuf:"varint,5,opt,name=minReadySeconds"` | ||||||
|  |  | ||||||
|  | 	// The number of old ReplicaSets to retain to allow rollback. | ||||||
|  | 	// This is a pointer to distinguish between explicit zero and not specified. | ||||||
|  | 	// Defaults to 2. | ||||||
|  | 	// +optional | ||||||
|  | 	RevisionHistoryLimit *int32 `json:"revisionHistoryLimit,omitempty" protobuf:"varint,6,opt,name=revisionHistoryLimit"` | ||||||
|  |  | ||||||
|  | 	// Indicates that the deployment is paused. | ||||||
|  | 	// +optional | ||||||
|  | 	Paused bool `json:"paused,omitempty" protobuf:"varint,7,opt,name=paused"` | ||||||
|  |  | ||||||
|  | 	// The config this deployment is rolling back to. Will be cleared after rollback is done. | ||||||
|  | 	// +optional | ||||||
|  | 	RollbackTo *RollbackConfig `json:"rollbackTo,omitempty" protobuf:"bytes,8,opt,name=rollbackTo"` | ||||||
|  |  | ||||||
|  | 	// The maximum time in seconds for a deployment to make progress before it | ||||||
|  | 	// is considered to be failed. The deployment controller will continue to | ||||||
|  | 	// process failed deployments and a condition with a ProgressDeadlineExceeded | ||||||
|  | 	// reason will be surfaced in the deployment status. Once autoRollback is | ||||||
|  | 	// implemented, the deployment controller will automatically rollback failed | ||||||
|  | 	// deployments. Note that progress will not be estimated during the time a | ||||||
|  | 	// deployment is paused. Defaults to 600s. | ||||||
|  | 	ProgressDeadlineSeconds *int32 `json:"progressDeadlineSeconds,omitempty" protobuf:"varint,9,opt,name=progressDeadlineSeconds"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // DeploymentRollback stores the information required to rollback a deployment. | ||||||
|  | type DeploymentRollback struct { | ||||||
|  | 	metav1.TypeMeta `json:",inline"` | ||||||
|  | 	// Required: This must match the Name of a deployment. | ||||||
|  | 	Name string `json:"name" protobuf:"bytes,1,opt,name=name"` | ||||||
|  | 	// The annotations to be updated to a deployment | ||||||
|  | 	// +optional | ||||||
|  | 	UpdatedAnnotations map[string]string `json:"updatedAnnotations,omitempty" protobuf:"bytes,2,rep,name=updatedAnnotations"` | ||||||
|  | 	// The config of this deployment rollback. | ||||||
|  | 	RollbackTo RollbackConfig `json:"rollbackTo" protobuf:"bytes,3,opt,name=rollbackTo"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type RollbackConfig struct { | ||||||
|  | 	// The revision to rollback to. If set to 0, rollbck to the last revision. | ||||||
|  | 	// +optional | ||||||
|  | 	Revision int64 `json:"revision,omitempty" protobuf:"varint,1,opt,name=revision"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	// DefaultDeploymentUniqueLabelKey is the default key of the selector that is added | ||||||
|  | 	// to existing RCs (and label key that is added to its pods) to prevent the existing RCs | ||||||
|  | 	// to select new pods (and old pods being select by new RC). | ||||||
|  | 	DefaultDeploymentUniqueLabelKey string = "pod-template-hash" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // DeploymentStrategy describes how to replace existing pods with new ones. | ||||||
|  | type DeploymentStrategy struct { | ||||||
|  | 	// Type of deployment. Can be "Recreate" or "RollingUpdate". Default is RollingUpdate. | ||||||
|  | 	// +optional | ||||||
|  | 	Type DeploymentStrategyType `json:"type,omitempty" protobuf:"bytes,1,opt,name=type,casttype=DeploymentStrategyType"` | ||||||
|  |  | ||||||
|  | 	// Rolling update config params. Present only if DeploymentStrategyType = | ||||||
|  | 	// RollingUpdate. | ||||||
|  | 	//--- | ||||||
|  | 	// TODO: Update this to follow our convention for oneOf, whatever we decide it | ||||||
|  | 	// to be. | ||||||
|  | 	// +optional | ||||||
|  | 	RollingUpdate *RollingUpdateDeployment `json:"rollingUpdate,omitempty" protobuf:"bytes,2,opt,name=rollingUpdate"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type DeploymentStrategyType string | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	// Kill all existing pods before creating new ones. | ||||||
|  | 	RecreateDeploymentStrategyType DeploymentStrategyType = "Recreate" | ||||||
|  |  | ||||||
|  | 	// Replace the old RCs by new one using rolling update i.e gradually scale down the old RCs and scale up the new one. | ||||||
|  | 	RollingUpdateDeploymentStrategyType DeploymentStrategyType = "RollingUpdate" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Spec to control the desired behavior of rolling update. | ||||||
|  | type RollingUpdateDeployment struct { | ||||||
|  | 	// The maximum number of pods that can be unavailable during the update. | ||||||
|  | 	// Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%). | ||||||
|  | 	// Absolute number is calculated from percentage by rounding down. | ||||||
|  | 	// This can not be 0 if MaxSurge is 0. | ||||||
|  | 	// Defaults to 25%. | ||||||
|  | 	// Example: when this is set to 30%, the old RC can be scaled down to 70% of desired pods | ||||||
|  | 	// immediately when the rolling update starts. Once new pods are ready, old RC | ||||||
|  | 	// can be scaled down further, followed by scaling up the new RC, ensuring | ||||||
|  | 	// that the total number of pods available at all times during the update is at | ||||||
|  | 	// least 70% of desired pods. | ||||||
|  | 	// +optional | ||||||
|  | 	MaxUnavailable *intstr.IntOrString `json:"maxUnavailable,omitempty" protobuf:"bytes,1,opt,name=maxUnavailable"` | ||||||
|  |  | ||||||
|  | 	// The maximum number of pods that can be scheduled above the desired number of | ||||||
|  | 	// pods. | ||||||
|  | 	// Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%). | ||||||
|  | 	// This can not be 0 if MaxUnavailable is 0. | ||||||
|  | 	// Absolute number is calculated from percentage by rounding up. | ||||||
|  | 	// Defaults to 25%. | ||||||
|  | 	// Example: when this is set to 30%, the new RC can be scaled up immediately when | ||||||
|  | 	// the rolling update starts, such that the total number of old and new pods do not exceed | ||||||
|  | 	// 130% of desired pods. Once old pods have been killed, | ||||||
|  | 	// new RC can be scaled up further, ensuring that total number of pods running | ||||||
|  | 	// at any time during the update is atmost 130% of desired pods. | ||||||
|  | 	// +optional | ||||||
|  | 	MaxSurge *intstr.IntOrString `json:"maxSurge,omitempty" protobuf:"bytes,2,opt,name=maxSurge"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // DeploymentStatus is the most recently observed status of the Deployment. | ||||||
|  | type DeploymentStatus struct { | ||||||
|  | 	// The generation observed by the deployment controller. | ||||||
|  | 	// +optional | ||||||
|  | 	ObservedGeneration int64 `json:"observedGeneration,omitempty" protobuf:"varint,1,opt,name=observedGeneration"` | ||||||
|  |  | ||||||
|  | 	// Total number of non-terminated pods targeted by this deployment (their labels match the selector). | ||||||
|  | 	// +optional | ||||||
|  | 	Replicas int32 `json:"replicas,omitempty" protobuf:"varint,2,opt,name=replicas"` | ||||||
|  |  | ||||||
|  | 	// Total number of non-terminated pods targeted by this deployment that have the desired template spec. | ||||||
|  | 	// +optional | ||||||
|  | 	UpdatedReplicas int32 `json:"updatedReplicas,omitempty" protobuf:"varint,3,opt,name=updatedReplicas"` | ||||||
|  |  | ||||||
|  | 	// Total number of ready pods targeted by this deployment. | ||||||
|  | 	// +optional | ||||||
|  | 	ReadyReplicas int32 `json:"readyReplicas,omitempty" protobuf:"varint,7,opt,name=readyReplicas"` | ||||||
|  |  | ||||||
|  | 	// Total number of available pods (ready for at least minReadySeconds) targeted by this deployment. | ||||||
|  | 	// +optional | ||||||
|  | 	AvailableReplicas int32 `json:"availableReplicas,omitempty" protobuf:"varint,4,opt,name=availableReplicas"` | ||||||
|  |  | ||||||
|  | 	// Total number of unavailable pods targeted by this deployment. | ||||||
|  | 	// +optional | ||||||
|  | 	UnavailableReplicas int32 `json:"unavailableReplicas,omitempty" protobuf:"varint,5,opt,name=unavailableReplicas"` | ||||||
|  |  | ||||||
|  | 	// Represents the latest available observations of a deployment's current state. | ||||||
|  | 	Conditions []DeploymentCondition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,6,rep,name=conditions"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type DeploymentConditionType string | ||||||
|  |  | ||||||
|  | // These are valid conditions of a deployment. | ||||||
|  | const ( | ||||||
|  | 	// Available means the deployment is available, ie. at least the minimum available | ||||||
|  | 	// replicas required are up and running for at least minReadySeconds. | ||||||
|  | 	DeploymentAvailable DeploymentConditionType = "Available" | ||||||
|  | 	// Progressing means the deployment is progressing. Progress for a deployment is | ||||||
|  | 	// considered when a new replica set is created or adopted, and when new pods scale | ||||||
|  | 	// up or old pods scale down. Progress is not estimated for paused deployments or | ||||||
|  | 	// when progressDeadlineSeconds is not specified. | ||||||
|  | 	DeploymentProgressing DeploymentConditionType = "Progressing" | ||||||
|  | 	// ReplicaFailure is added in a deployment when one of its pods fails to be created | ||||||
|  | 	// or deleted. | ||||||
|  | 	DeploymentReplicaFailure DeploymentConditionType = "ReplicaFailure" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // DeploymentCondition describes the state of a deployment at a certain point. | ||||||
|  | type DeploymentCondition struct { | ||||||
|  | 	// Type of deployment condition. | ||||||
|  | 	Type DeploymentConditionType `json:"type" protobuf:"bytes,1,opt,name=type,casttype=DeploymentConditionType"` | ||||||
|  | 	// Status of the condition, one of True, False, Unknown. | ||||||
|  | 	Status v1.ConditionStatus `json:"status" protobuf:"bytes,2,opt,name=status,casttype=k8s.io/kubernetes/pkg/api/v1.ConditionStatus"` | ||||||
|  | 	// The last time this condition was updated. | ||||||
|  | 	LastUpdateTime metav1.Time `json:"lastUpdateTime,omitempty" protobuf:"bytes,6,opt,name=lastUpdateTime"` | ||||||
|  | 	// Last time the condition transitioned from one status to another. | ||||||
|  | 	LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty" protobuf:"bytes,7,opt,name=lastTransitionTime"` | ||||||
|  | 	// The reason for the condition's last transition. | ||||||
|  | 	Reason string `json:"reason,omitempty" protobuf:"bytes,4,opt,name=reason"` | ||||||
|  | 	// A human readable message indicating details about the transition. | ||||||
|  | 	Message string `json:"message,omitempty" protobuf:"bytes,5,opt,name=message"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // DeploymentList is a list of Deployments. | ||||||
|  | type DeploymentList struct { | ||||||
|  | 	metav1.TypeMeta `json:",inline"` | ||||||
|  | 	// Standard list metadata. | ||||||
|  | 	// +optional | ||||||
|  | 	metav1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` | ||||||
|  |  | ||||||
|  | 	// Items is the list of Deployments. | ||||||
|  | 	Items []Deployment `json:"items" protobuf:"bytes,2,rep,name=items"` | ||||||
|  | } | ||||||
|   | |||||||
| @@ -539,15 +539,3 @@ func newInt32(val int32) *int32 { | |||||||
| 	*p = val | 	*p = val | ||||||
| 	return p | 	return p | ||||||
| } | } | ||||||
|  |  | ||||||
| func newString(val string) *string { |  | ||||||
| 	p := new(string) |  | ||||||
| 	*p = val |  | ||||||
| 	return p |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func newBool(val bool) *bool { |  | ||||||
| 	b := new(bool) |  | ||||||
| 	*b = val |  | ||||||
| 	return b |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -2808,9 +2808,3 @@ func TestIsValidSysctlPattern(t *testing.T) { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func newBool(val bool) *bool { |  | ||||||
| 	p := new(bool) |  | ||||||
| 	*p = val |  | ||||||
| 	return p |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -421,6 +421,7 @@ func getRESTMappings(mapper meta.RESTMapper, pruneResources *[]pruneResource) (n | |||||||
| 			{"extensions", "v1beta1", "Ingress", true}, | 			{"extensions", "v1beta1", "Ingress", true}, | ||||||
| 			{"extensions", "v1beta1", "ReplicaSet", true}, | 			{"extensions", "v1beta1", "ReplicaSet", true}, | ||||||
| 			{"apps", "v1beta1", "StatefulSet", true}, | 			{"apps", "v1beta1", "StatefulSet", true}, | ||||||
|  | 			{"apps", "v1beta1", "Deployment", true}, | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -552,7 +552,8 @@ func (f *ring0Factory) Generators(cmdName string) map[string]kubectl.Generator { | |||||||
|  |  | ||||||
| func (f *ring0Factory) CanBeExposed(kind schema.GroupKind) error { | func (f *ring0Factory) CanBeExposed(kind schema.GroupKind) error { | ||||||
| 	switch kind { | 	switch kind { | ||||||
| 	case api.Kind("ReplicationController"), api.Kind("Service"), api.Kind("Pod"), extensions.Kind("Deployment"), extensions.Kind("ReplicaSet"): | 	case api.Kind("ReplicationController"), api.Kind("Service"), api.Kind("Pod"), | ||||||
|  | 		extensions.Kind("Deployment"), apps.Kind("Deployment"), extensions.Kind("ReplicaSet"): | ||||||
| 		// nothing to do here | 		// nothing to do here | ||||||
| 	default: | 	default: | ||||||
| 		return fmt.Errorf("cannot expose a %s", kind) | 		return fmt.Errorf("cannot expose a %s", kind) | ||||||
| @@ -562,7 +563,8 @@ func (f *ring0Factory) CanBeExposed(kind schema.GroupKind) error { | |||||||
|  |  | ||||||
| func (f *ring0Factory) CanBeAutoscaled(kind schema.GroupKind) error { | func (f *ring0Factory) CanBeAutoscaled(kind schema.GroupKind) error { | ||||||
| 	switch kind { | 	switch kind { | ||||||
| 	case api.Kind("ReplicationController"), extensions.Kind("Deployment"), extensions.Kind("ReplicaSet"): | 	case api.Kind("ReplicationController"), extensions.Kind("ReplicaSet"), | ||||||
|  | 		extensions.Kind("Deployment"), apps.Kind("Deployment"): | ||||||
| 		// nothing to do here | 		// nothing to do here | ||||||
| 	default: | 	default: | ||||||
| 		return fmt.Errorf("cannot autoscale a %v", kind) | 		return fmt.Errorf("cannot autoscale a %v", kind) | ||||||
|   | |||||||
| @@ -28,6 +28,7 @@ import ( | |||||||
| 	"k8s.io/apimachinery/pkg/runtime/schema" | 	"k8s.io/apimachinery/pkg/runtime/schema" | ||||||
| 	"k8s.io/kubernetes/pkg/api" | 	"k8s.io/kubernetes/pkg/api" | ||||||
| 	"k8s.io/kubernetes/pkg/api/v1" | 	"k8s.io/kubernetes/pkg/api/v1" | ||||||
|  | 	"k8s.io/kubernetes/pkg/apis/apps" | ||||||
| 	"k8s.io/kubernetes/pkg/apis/extensions" | 	"k8s.io/kubernetes/pkg/apis/extensions" | ||||||
| 	clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" | 	clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" | ||||||
| 	deploymentutil "k8s.io/kubernetes/pkg/controller/deployment/util" | 	deploymentutil "k8s.io/kubernetes/pkg/controller/deployment/util" | ||||||
| @@ -46,7 +47,7 @@ type HistoryViewer interface { | |||||||
|  |  | ||||||
| func HistoryViewerFor(kind schema.GroupKind, c clientset.Interface) (HistoryViewer, error) { | func HistoryViewerFor(kind schema.GroupKind, c clientset.Interface) (HistoryViewer, error) { | ||||||
| 	switch kind { | 	switch kind { | ||||||
| 	case extensions.Kind("Deployment"): | 	case extensions.Kind("Deployment"), apps.Kind("Deployment"): | ||||||
| 		return &DeploymentHistoryViewer{c}, nil | 		return &DeploymentHistoryViewer{c}, nil | ||||||
| 	} | 	} | ||||||
| 	return nil, fmt.Errorf("no history viewer has been implemented for %q", kind) | 	return nil, fmt.Errorf("no history viewer has been implemented for %q", kind) | ||||||
|   | |||||||
| @@ -29,6 +29,7 @@ import ( | |||||||
| 	"k8s.io/apimachinery/pkg/watch" | 	"k8s.io/apimachinery/pkg/watch" | ||||||
| 	"k8s.io/kubernetes/pkg/api" | 	"k8s.io/kubernetes/pkg/api" | ||||||
| 	"k8s.io/kubernetes/pkg/api/v1" | 	"k8s.io/kubernetes/pkg/api/v1" | ||||||
|  | 	"k8s.io/kubernetes/pkg/apis/apps" | ||||||
| 	"k8s.io/kubernetes/pkg/apis/extensions" | 	"k8s.io/kubernetes/pkg/apis/extensions" | ||||||
| 	externalextensions "k8s.io/kubernetes/pkg/apis/extensions/v1beta1" | 	externalextensions "k8s.io/kubernetes/pkg/apis/extensions/v1beta1" | ||||||
| 	clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" | 	clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" | ||||||
| @@ -44,7 +45,7 @@ type Rollbacker interface { | |||||||
|  |  | ||||||
| func RollbackerFor(kind schema.GroupKind, c clientset.Interface) (Rollbacker, error) { | func RollbackerFor(kind schema.GroupKind, c clientset.Interface) (Rollbacker, error) { | ||||||
| 	switch kind { | 	switch kind { | ||||||
| 	case extensions.Kind("Deployment"): | 	case extensions.Kind("Deployment"), apps.Kind("Deployment"): | ||||||
| 		return &DeploymentRollbacker{c}, nil | 		return &DeploymentRollbacker{c}, nil | ||||||
| 	} | 	} | ||||||
| 	return nil, fmt.Errorf("no rollbacker has been implemented for %q", kind) | 	return nil, fmt.Errorf("no rollbacker has been implemented for %q", kind) | ||||||
|   | |||||||
| @@ -21,6 +21,7 @@ import ( | |||||||
|  |  | ||||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||||
| 	"k8s.io/apimachinery/pkg/runtime/schema" | 	"k8s.io/apimachinery/pkg/runtime/schema" | ||||||
|  | 	"k8s.io/kubernetes/pkg/apis/apps" | ||||||
| 	"k8s.io/kubernetes/pkg/apis/extensions" | 	"k8s.io/kubernetes/pkg/apis/extensions" | ||||||
| 	"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" | 	"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" | ||||||
| 	extensionsclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion" | 	extensionsclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion" | ||||||
| @@ -34,7 +35,7 @@ type StatusViewer interface { | |||||||
|  |  | ||||||
| func StatusViewerFor(kind schema.GroupKind, c internalclientset.Interface) (StatusViewer, error) { | func StatusViewerFor(kind schema.GroupKind, c internalclientset.Interface) (StatusViewer, error) { | ||||||
| 	switch kind { | 	switch kind { | ||||||
| 	case extensions.Kind("Deployment"): | 	case extensions.Kind("Deployment"), apps.Kind("Deployment"): | ||||||
| 		return &DeploymentStatusViewer{c.Extensions()}, nil | 		return &DeploymentStatusViewer{c.Extensions()}, nil | ||||||
| 	case extensions.Kind("DaemonSet"): | 	case extensions.Kind("DaemonSet"): | ||||||
| 		return &DaemonSetStatusViewer{c.Extensions()}, nil | 		return &DaemonSetStatusViewer{c.Extensions()}, nil | ||||||
|   | |||||||
| @@ -26,7 +26,7 @@ import ( | |||||||
| 	"k8s.io/apimachinery/pkg/runtime" | 	"k8s.io/apimachinery/pkg/runtime" | ||||||
| 	"k8s.io/apimachinery/pkg/util/validation" | 	"k8s.io/apimachinery/pkg/util/validation" | ||||||
| 	"k8s.io/kubernetes/pkg/api" | 	"k8s.io/kubernetes/pkg/api" | ||||||
| 	batch "k8s.io/kubernetes/pkg/apis/batch" | 	"k8s.io/kubernetes/pkg/apis/batch" | ||||||
| 	"k8s.io/kubernetes/pkg/apis/extensions" | 	"k8s.io/kubernetes/pkg/apis/extensions" | ||||||
| ) | ) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -60,7 +60,7 @@ func ScalerFor(kind schema.GroupKind, c internalclientset.Interface) (Scaler, er | |||||||
| 		return &JobScaler{c.Batch()}, nil // Either kind of job can be scaled with Batch interface. | 		return &JobScaler{c.Batch()}, nil // Either kind of job can be scaled with Batch interface. | ||||||
| 	case apps.Kind("StatefulSet"): | 	case apps.Kind("StatefulSet"): | ||||||
| 		return &StatefulSetScaler{c.Apps()}, nil | 		return &StatefulSetScaler{c.Apps()}, nil | ||||||
| 	case extensions.Kind("Deployment"): | 	case extensions.Kind("Deployment"), apps.Kind("Deployment"): | ||||||
| 		return &DeploymentScaler{c.Extensions()}, nil | 		return &DeploymentScaler{c.Extensions()}, nil | ||||||
| 	} | 	} | ||||||
| 	return nil, fmt.Errorf("no scaler has been implemented for %q", kind) | 	return nil, fmt.Errorf("no scaler has been implemented for %q", kind) | ||||||
|   | |||||||
| @@ -90,7 +90,7 @@ func ReaperFor(kind schema.GroupKind, c internalclientset.Interface) (Reaper, er | |||||||
| 	case apps.Kind("StatefulSet"): | 	case apps.Kind("StatefulSet"): | ||||||
| 		return &StatefulSetReaper{c.Apps(), c.Core(), Interval, Timeout}, nil | 		return &StatefulSetReaper{c.Apps(), c.Core(), Interval, Timeout}, nil | ||||||
|  |  | ||||||
| 	case extensions.Kind("Deployment"): | 	case extensions.Kind("Deployment"), apps.Kind("Deployment"): | ||||||
| 		return &DeploymentReaper{c.Extensions(), c.Extensions(), Interval, Timeout}, nil | 		return &DeploymentReaper{c.Extensions(), c.Extensions(), Interval, Timeout}, nil | ||||||
|  |  | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -33,7 +33,7 @@ import ( | |||||||
| 	"k8s.io/kubernetes/cmd/kube-apiserver/app/options" | 	"k8s.io/kubernetes/cmd/kube-apiserver/app/options" | ||||||
| 	"k8s.io/kubernetes/pkg/api" | 	"k8s.io/kubernetes/pkg/api" | ||||||
| 	apiv1 "k8s.io/kubernetes/pkg/api/v1" | 	apiv1 "k8s.io/kubernetes/pkg/api/v1" | ||||||
| 	appsapi "k8s.io/kubernetes/pkg/apis/apps/v1beta1" | 	appsv1beta1 "k8s.io/kubernetes/pkg/apis/apps/v1beta1" | ||||||
| 	authenticationv1 "k8s.io/kubernetes/pkg/apis/authentication/v1" | 	authenticationv1 "k8s.io/kubernetes/pkg/apis/authentication/v1" | ||||||
| 	authenticationv1beta1 "k8s.io/kubernetes/pkg/apis/authentication/v1beta1" | 	authenticationv1beta1 "k8s.io/kubernetes/pkg/apis/authentication/v1beta1" | ||||||
| 	authorizationapiv1 "k8s.io/kubernetes/pkg/apis/authorization/v1" | 	authorizationapiv1 "k8s.io/kubernetes/pkg/apis/authorization/v1" | ||||||
| @@ -367,7 +367,7 @@ func DefaultAPIResourceConfigSource() *serverstorage.ResourceConfig { | |||||||
| 		authenticationv1.SchemeGroupVersion, | 		authenticationv1.SchemeGroupVersion, | ||||||
| 		authenticationv1beta1.SchemeGroupVersion, | 		authenticationv1beta1.SchemeGroupVersion, | ||||||
| 		autoscalingapiv1.SchemeGroupVersion, | 		autoscalingapiv1.SchemeGroupVersion, | ||||||
| 		appsapi.SchemeGroupVersion, | 		appsv1beta1.SchemeGroupVersion, | ||||||
| 		policyapiv1beta1.SchemeGroupVersion, | 		policyapiv1beta1.SchemeGroupVersion, | ||||||
| 		rbacv1beta1.SchemeGroupVersion, | 		rbacv1beta1.SchemeGroupVersion, | ||||||
| 		rbacapi.SchemeGroupVersion, | 		rbacapi.SchemeGroupVersion, | ||||||
|   | |||||||
| @@ -118,6 +118,7 @@ func describerMap(c clientset.Interface) map[schema.GroupKind]printers.Describer | |||||||
| 		batch.Kind("Job"):                              &JobDescriber{c}, | 		batch.Kind("Job"):                              &JobDescriber{c}, | ||||||
| 		batch.Kind("CronJob"):                          &CronJobDescriber{c}, | 		batch.Kind("CronJob"):                          &CronJobDescriber{c}, | ||||||
| 		apps.Kind("StatefulSet"):                       &StatefulSetDescriber{c}, | 		apps.Kind("StatefulSet"):                       &StatefulSetDescriber{c}, | ||||||
|  | 		apps.Kind("Deployment"):                        &DeploymentDescriber{c, versionedClientsetForDeployment(c)}, | ||||||
| 		certificates.Kind("CertificateSigningRequest"): &CertificateSigningRequestDescriber{c}, | 		certificates.Kind("CertificateSigningRequest"): &CertificateSigningRequestDescriber{c}, | ||||||
| 		storage.Kind("StorageClass"):                   &StorageClassDescriber{c}, | 		storage.Kind("StorageClass"):                   &StorageClassDescriber{c}, | ||||||
| 		policy.Kind("PodDisruptionBudget"):             &PodDisruptionBudgetDescriber{c}, | 		policy.Kind("PodDisruptionBudget"):             &PodDisruptionBudgetDescriber{c}, | ||||||
|   | |||||||
| @@ -25,6 +25,7 @@ import ( | |||||||
| 	"k8s.io/kubernetes/pkg/apis/apps" | 	"k8s.io/kubernetes/pkg/apis/apps" | ||||||
| 	appsapiv1beta1 "k8s.io/kubernetes/pkg/apis/apps/v1beta1" | 	appsapiv1beta1 "k8s.io/kubernetes/pkg/apis/apps/v1beta1" | ||||||
| 	statefulsetstore "k8s.io/kubernetes/pkg/registry/apps/petset/storage" | 	statefulsetstore "k8s.io/kubernetes/pkg/registry/apps/petset/storage" | ||||||
|  | 	deploymentstore "k8s.io/kubernetes/pkg/registry/extensions/deployment/storage" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type RESTStorageProvider struct{} | type RESTStorageProvider struct{} | ||||||
| @@ -44,6 +45,13 @@ func (p RESTStorageProvider) v1beta1Storage(apiResourceConfigSource serverstorag | |||||||
| 	version := appsapiv1beta1.SchemeGroupVersion | 	version := appsapiv1beta1.SchemeGroupVersion | ||||||
|  |  | ||||||
| 	storage := map[string]rest.Storage{} | 	storage := map[string]rest.Storage{} | ||||||
|  | 	if apiResourceConfigSource.ResourceEnabled(version.WithResource("deployments")) { | ||||||
|  | 		deploymentStorage := deploymentstore.NewStorage(restOptionsGetter) | ||||||
|  | 		storage["deployments"] = deploymentStorage.Deployment | ||||||
|  | 		storage["deployments/status"] = deploymentStorage.Status | ||||||
|  | 		storage["deployments/rollback"] = deploymentStorage.Rollback | ||||||
|  | 		storage["deployments/scale"] = deploymentStorage.Scale | ||||||
|  | 	} | ||||||
| 	if apiResourceConfigSource.ResourceEnabled(version.WithResource("statefulsets")) { | 	if apiResourceConfigSource.ResourceEnabled(version.WithResource("statefulsets")) { | ||||||
| 		statefulsetStorage, statefulsetStatusStorage := statefulsetstore.NewREST(restOptionsGetter) | 		statefulsetStorage, statefulsetStatusStorage := statefulsetstore.NewREST(restOptionsGetter) | ||||||
| 		storage["statefulsets"] = statefulsetStorage | 		storage["statefulsets"] = statefulsetStorage | ||||||
|   | |||||||
| @@ -92,8 +92,8 @@ func init() { | |||||||
| 	addControllerRole(rbac.ClusterRole{ | 	addControllerRole(rbac.ClusterRole{ | ||||||
| 		ObjectMeta: metav1.ObjectMeta{Name: saRolePrefix + "deployment-controller"}, | 		ObjectMeta: metav1.ObjectMeta{Name: saRolePrefix + "deployment-controller"}, | ||||||
| 		Rules: []rbac.PolicyRule{ | 		Rules: []rbac.PolicyRule{ | ||||||
| 			rbac.NewRule("get", "list", "watch", "update").Groups(extensionsGroup).Resources("deployments").RuleOrDie(), | 			rbac.NewRule("get", "list", "watch", "update").Groups(extensionsGroup, appsGroup).Resources("deployments").RuleOrDie(), | ||||||
| 			rbac.NewRule("update").Groups(extensionsGroup).Resources("deployments/status").RuleOrDie(), | 			rbac.NewRule("update").Groups(extensionsGroup, appsGroup).Resources("deployments/status").RuleOrDie(), | ||||||
| 			rbac.NewRule("get", "list", "watch", "create", "update", "patch", "delete").Groups(extensionsGroup).Resources("replicasets").RuleOrDie(), | 			rbac.NewRule("get", "list", "watch", "create", "update", "patch", "delete").Groups(extensionsGroup).Resources("replicasets").RuleOrDie(), | ||||||
| 			// TODO: remove "update" once | 			// TODO: remove "update" once | ||||||
| 			// https://github.com/kubernetes/kubernetes/issues/36897 is resolved. | 			// https://github.com/kubernetes/kubernetes/issues/36897 is resolved. | ||||||
| @@ -104,7 +104,7 @@ func init() { | |||||||
| 	addControllerRole(rbac.ClusterRole{ | 	addControllerRole(rbac.ClusterRole{ | ||||||
| 		ObjectMeta: metav1.ObjectMeta{Name: saRolePrefix + "disruption-controller"}, | 		ObjectMeta: metav1.ObjectMeta{Name: saRolePrefix + "disruption-controller"}, | ||||||
| 		Rules: []rbac.PolicyRule{ | 		Rules: []rbac.PolicyRule{ | ||||||
| 			rbac.NewRule("get", "list", "watch").Groups(extensionsGroup).Resources("deployments").RuleOrDie(), | 			rbac.NewRule("get", "list", "watch").Groups(extensionsGroup, appsGroup).Resources("deployments").RuleOrDie(), | ||||||
| 			rbac.NewRule("get", "list", "watch").Groups(extensionsGroup).Resources("replicasets").RuleOrDie(), | 			rbac.NewRule("get", "list", "watch").Groups(extensionsGroup).Resources("replicasets").RuleOrDie(), | ||||||
| 			rbac.NewRule("get", "list", "watch").Groups(legacyGroup).Resources("replicationcontrollers").RuleOrDie(), | 			rbac.NewRule("get", "list", "watch").Groups(legacyGroup).Resources("replicationcontrollers").RuleOrDie(), | ||||||
| 			rbac.NewRule("get", "list", "watch").Groups(policyGroup).Resources("poddisruptionbudgets").RuleOrDie(), | 			rbac.NewRule("get", "list", "watch").Groups(policyGroup).Resources("poddisruptionbudgets").RuleOrDie(), | ||||||
| @@ -138,7 +138,7 @@ func init() { | |||||||
| 			rbac.NewRule("get", "update").Groups(legacyGroup).Resources("replicationcontrollers/scale").RuleOrDie(), | 			rbac.NewRule("get", "update").Groups(legacyGroup).Resources("replicationcontrollers/scale").RuleOrDie(), | ||||||
| 			// TODO this should be removable when the HPA contoller is fixed | 			// TODO this should be removable when the HPA contoller is fixed | ||||||
| 			rbac.NewRule("get", "update").Groups(extensionsGroup).Resources("replicationcontrollers/scale").RuleOrDie(), | 			rbac.NewRule("get", "update").Groups(extensionsGroup).Resources("replicationcontrollers/scale").RuleOrDie(), | ||||||
| 			rbac.NewRule("get", "update").Groups(extensionsGroup).Resources("deployments/scale", "replicasets/scale").RuleOrDie(), | 			rbac.NewRule("get", "update").Groups(extensionsGroup, appsGroup).Resources("deployments/scale", "replicasets/scale").RuleOrDie(), | ||||||
| 			rbac.NewRule("list").Groups(legacyGroup).Resources("pods").RuleOrDie(), | 			rbac.NewRule("list").Groups(legacyGroup).Resources("pods").RuleOrDie(), | ||||||
| 			// TODO: Remove the root /proxy permission in 1.7; MetricsClient no longer requires root proxy access as of 1.6 (fixed in https://github.com/kubernetes/kubernetes/pull/39636) | 			// TODO: Remove the root /proxy permission in 1.7; MetricsClient no longer requires root proxy access as of 1.6 (fixed in https://github.com/kubernetes/kubernetes/pull/39636) | ||||||
| 			rbac.NewRule("proxy").Groups(legacyGroup).Resources("services").Names("https:heapster:", "http:heapster:").RuleOrDie(), | 			rbac.NewRule("proxy").Groups(legacyGroup).Resources("services").Names("https:heapster:", "http:heapster:").RuleOrDie(), | ||||||
|   | |||||||
| @@ -127,14 +127,16 @@ func ClusterRoles() []rbac.ClusterRole { | |||||||
| 				rbac.NewRule(Read...).Groups(legacyGroup).Resources("namespaces").RuleOrDie(), | 				rbac.NewRule(Read...).Groups(legacyGroup).Resources("namespaces").RuleOrDie(), | ||||||
| 				rbac.NewRule("impersonate").Groups(legacyGroup).Resources("serviceaccounts").RuleOrDie(), | 				rbac.NewRule("impersonate").Groups(legacyGroup).Resources("serviceaccounts").RuleOrDie(), | ||||||
|  |  | ||||||
| 				rbac.NewRule(ReadWrite...).Groups(appsGroup).Resources("statefulsets").RuleOrDie(), | 				rbac.NewRule(ReadWrite...).Groups(appsGroup).Resources("statefulsets", | ||||||
|  | 					"deployments", "deployments/scale", "deployments/rollback").RuleOrDie(), | ||||||
|  |  | ||||||
| 				rbac.NewRule(ReadWrite...).Groups(autoscalingGroup).Resources("horizontalpodautoscalers").RuleOrDie(), | 				rbac.NewRule(ReadWrite...).Groups(autoscalingGroup).Resources("horizontalpodautoscalers").RuleOrDie(), | ||||||
|  |  | ||||||
| 				rbac.NewRule(ReadWrite...).Groups(batchGroup).Resources("jobs", "cronjobs", "scheduledjobs").RuleOrDie(), | 				rbac.NewRule(ReadWrite...).Groups(batchGroup).Resources("jobs", "cronjobs", "scheduledjobs").RuleOrDie(), | ||||||
|  |  | ||||||
| 				rbac.NewRule(ReadWrite...).Groups(extensionsGroup).Resources("daemonsets", "deployments", "deployments/scale", | 				rbac.NewRule(ReadWrite...).Groups(extensionsGroup).Resources("daemonsets", | ||||||
| 					"ingresses", "replicasets", "replicasets/scale", "replicationcontrollers/scale").RuleOrDie(), | 					"deployments", "deployments/scale", "deployments/rollback", "ingresses", | ||||||
|  | 					"replicasets", "replicasets/scale", "replicationcontrollers/scale").RuleOrDie(), | ||||||
|  |  | ||||||
| 				// additional admin powers | 				// additional admin powers | ||||||
| 				rbac.NewRule("create").Groups(authorizationGroup).Resources("localsubjectaccessreviews").RuleOrDie(), | 				rbac.NewRule("create").Groups(authorizationGroup).Resources("localsubjectaccessreviews").RuleOrDie(), | ||||||
| @@ -157,14 +159,16 @@ func ClusterRoles() []rbac.ClusterRole { | |||||||
| 				rbac.NewRule(Read...).Groups(legacyGroup).Resources("namespaces").RuleOrDie(), | 				rbac.NewRule(Read...).Groups(legacyGroup).Resources("namespaces").RuleOrDie(), | ||||||
| 				rbac.NewRule("impersonate").Groups(legacyGroup).Resources("serviceaccounts").RuleOrDie(), | 				rbac.NewRule("impersonate").Groups(legacyGroup).Resources("serviceaccounts").RuleOrDie(), | ||||||
|  |  | ||||||
| 				rbac.NewRule(ReadWrite...).Groups(appsGroup).Resources("statefulsets").RuleOrDie(), | 				rbac.NewRule(ReadWrite...).Groups(appsGroup).Resources("statefulsets", | ||||||
|  | 					"deployments", "deployments/scale", "deployments/rollback").RuleOrDie(), | ||||||
|  |  | ||||||
| 				rbac.NewRule(ReadWrite...).Groups(autoscalingGroup).Resources("horizontalpodautoscalers").RuleOrDie(), | 				rbac.NewRule(ReadWrite...).Groups(autoscalingGroup).Resources("horizontalpodautoscalers").RuleOrDie(), | ||||||
|  |  | ||||||
| 				rbac.NewRule(ReadWrite...).Groups(batchGroup).Resources("jobs", "cronjobs", "scheduledjobs").RuleOrDie(), | 				rbac.NewRule(ReadWrite...).Groups(batchGroup).Resources("jobs", "cronjobs", "scheduledjobs").RuleOrDie(), | ||||||
|  |  | ||||||
| 				rbac.NewRule(ReadWrite...).Groups(extensionsGroup).Resources("daemonsets", "deployments", "deployments/scale", | 				rbac.NewRule(ReadWrite...).Groups(extensionsGroup).Resources("daemonsets", | ||||||
| 					"ingresses", "replicasets", "replicasets/scale", "replicationcontrollers/scale").RuleOrDie(), | 					"deployments", "deployments/scale", "deployments/rollback", "ingresses", | ||||||
|  | 					"replicasets", "replicasets/scale", "replicationcontrollers/scale").RuleOrDie(), | ||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| @@ -180,7 +184,7 @@ func ClusterRoles() []rbac.ClusterRole { | |||||||
| 				// indicator of which namespaces you have access to. | 				// indicator of which namespaces you have access to. | ||||||
| 				rbac.NewRule(Read...).Groups(legacyGroup).Resources("namespaces").RuleOrDie(), | 				rbac.NewRule(Read...).Groups(legacyGroup).Resources("namespaces").RuleOrDie(), | ||||||
|  |  | ||||||
| 				rbac.NewRule(Read...).Groups(appsGroup).Resources("statefulsets").RuleOrDie(), | 				rbac.NewRule(Read...).Groups(appsGroup).Resources("statefulsets", "deployments", "deployments/scale").RuleOrDie(), | ||||||
|  |  | ||||||
| 				rbac.NewRule(Read...).Groups(autoscalingGroup).Resources("horizontalpodautoscalers").RuleOrDie(), | 				rbac.NewRule(Read...).Groups(autoscalingGroup).Resources("horizontalpodautoscalers").RuleOrDie(), | ||||||
|  |  | ||||||
| @@ -320,6 +324,7 @@ func ClusterRoles() []rbac.ClusterRole { | |||||||
| 					"podsecuritypolicies", | 					"podsecuritypolicies", | ||||||
| 					"replicasets", | 					"replicasets", | ||||||
| 				).RuleOrDie(), | 				).RuleOrDie(), | ||||||
|  | 				rbac.NewRule("list", "watch").Groups(appsGroup).Resources("deployments").RuleOrDie(), | ||||||
| 				rbac.NewRule("list", "watch").Groups(batchGroup).Resources("jobs", "cronjobs").RuleOrDie(), | 				rbac.NewRule("list", "watch").Groups(batchGroup).Resources("jobs", "cronjobs").RuleOrDie(), | ||||||
| 				rbac.NewRule("list", "watch").Groups(appsGroup).Resources("statefulsets").RuleOrDie(), | 				rbac.NewRule("list", "watch").Groups(appsGroup).Resources("statefulsets").RuleOrDie(), | ||||||
| 				rbac.NewRule("list", "watch").Groups(policyGroup).Resources("poddisruptionbudgets").RuleOrDie(), | 				rbac.NewRule("list", "watch").Groups(policyGroup).Resources("poddisruptionbudgets").RuleOrDie(), | ||||||
|   | |||||||
| @@ -117,6 +117,12 @@ var viewEscalatingNamespaceResources = []rbac.PolicyRule{ | |||||||
| 	rbac.NewRule(bootstrappolicy.Read...).Groups("").Resources("services/proxy").RuleOrDie(), | 	rbac.NewRule(bootstrappolicy.Read...).Groups("").Resources("services/proxy").RuleOrDie(), | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // ungettableResources is the list of rules that don't allow to view (GET) them | ||||||
|  | // this is purposefully separate list to distinguish from escalating privs | ||||||
|  | var ungettableResources = []rbac.PolicyRule{ | ||||||
|  | 	rbac.NewRule(bootstrappolicy.Read...).Groups("apps", "extensions").Resources("deployments/rollback").RuleOrDie(), | ||||||
|  | } | ||||||
|  |  | ||||||
| func TestEditViewRelationship(t *testing.T) { | func TestEditViewRelationship(t *testing.T) { | ||||||
| 	readVerbs := sets.NewString(bootstrappolicy.Read...) | 	readVerbs := sets.NewString(bootstrappolicy.Read...) | ||||||
| 	semanticRoles := getSemanticRoles(bootstrappolicy.ClusterRoles()) | 	semanticRoles := getSemanticRoles(bootstrappolicy.ClusterRoles()) | ||||||
| @@ -142,6 +148,14 @@ func TestEditViewRelationship(t *testing.T) { | |||||||
| 	} | 	} | ||||||
| 	semanticRoles.view.Rules = append(semanticRoles.view.Rules, viewEscalatingNamespaceResources...) | 	semanticRoles.view.Rules = append(semanticRoles.view.Rules, viewEscalatingNamespaceResources...) | ||||||
|  |  | ||||||
|  | 	// confirm that the view role doesn't have ungettable resources | ||||||
|  | 	for _, rule := range ungettableResources { | ||||||
|  | 		if covers, _ := rbacregistryvalidation.Covers(semanticRoles.view.Rules, []rbac.PolicyRule{rule}); covers { | ||||||
|  | 			t.Errorf("view has ungettable resource: %#v", rule) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	semanticRoles.view.Rules = append(semanticRoles.view.Rules, ungettableResources...) | ||||||
|  |  | ||||||
| 	// at this point, we should have a two way covers relationship | 	// at this point, we should have a two way covers relationship | ||||||
| 	if covers, miss := rbacregistryvalidation.Covers(semanticRoles.edit.Rules, semanticRoles.view.Rules); !covers { | 	if covers, miss := rbacregistryvalidation.Covers(semanticRoles.edit.Rules, semanticRoles.view.Rules); !covers { | ||||||
| 		t.Errorf("edit has lost rules for: %#v", miss) | 		t.Errorf("edit has lost rules for: %#v", miss) | ||||||
|   | |||||||
| @@ -193,7 +193,7 @@ func waitForDeployment(c *fedclientset.Clientset, namespace string, deploymentNa | |||||||
| 		} | 		} | ||||||
| 		specReplicas, statusReplicas := int32(0), int32(0) | 		specReplicas, statusReplicas := int32(0), int32(0) | ||||||
| 		for _, cluster := range clusters { | 		for _, cluster := range clusters { | ||||||
| 			dep, err := cluster.Deployments(namespace).Get(deploymentName, metav1.GetOptions{}) | 			dep, err := cluster.Extensions().Deployments(namespace).Get(deploymentName, metav1.GetOptions{}) | ||||||
| 			if err != nil && !errors.IsNotFound(err) { | 			if err != nil && !errors.IsNotFound(err) { | ||||||
| 				By(fmt.Sprintf("Failed getting deployment: %q/%q/%q, err: %v", cluster.name, namespace, deploymentName, err)) | 				By(fmt.Sprintf("Failed getting deployment: %q/%q/%q, err: %v", cluster.name, namespace, deploymentName, err)) | ||||||
| 				return false, err | 				return false, err | ||||||
|   | |||||||
| @@ -138,6 +138,60 @@ var hpaV1 string = ` | |||||||
| } | } | ||||||
| ` | ` | ||||||
|  |  | ||||||
|  | var deploymentExtensions string = ` | ||||||
|  | { | ||||||
|  |   "apiVersion": "extensions/v1beta1", | ||||||
|  |   "kind": "Deployment", | ||||||
|  |   "metadata": { | ||||||
|  |      "name": "test-deployment1", | ||||||
|  |      "namespace": "default" | ||||||
|  |   }, | ||||||
|  |   "spec": { | ||||||
|  |     "replicas": 1, | ||||||
|  |     "template": { | ||||||
|  |       "metadata": { | ||||||
|  |         "labels": { | ||||||
|  |           "app": "nginx0" | ||||||
|  |         } | ||||||
|  |       }, | ||||||
|  |       "spec": { | ||||||
|  |         "containers": [{ | ||||||
|  |           "name": "nginx", | ||||||
|  |           "image": "gcr.io/google-containers/nginx:1.7.9" | ||||||
|  |         }] | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | ` | ||||||
|  |  | ||||||
|  | var deploymentApps string = ` | ||||||
|  | { | ||||||
|  |   "apiVersion": "apps/v1beta1", | ||||||
|  |   "kind": "Deployment", | ||||||
|  |   "metadata": { | ||||||
|  |      "name": "test-deployment2", | ||||||
|  |      "namespace": "default" | ||||||
|  |   }, | ||||||
|  |   "spec": { | ||||||
|  |     "replicas": 1, | ||||||
|  |     "template": { | ||||||
|  |       "metadata": { | ||||||
|  |         "labels": { | ||||||
|  |           "app": "nginx0" | ||||||
|  |         } | ||||||
|  |       }, | ||||||
|  |       "spec": { | ||||||
|  |         "containers": [{ | ||||||
|  |           "name": "nginx", | ||||||
|  |           "image": "gcr.io/google-containers/nginx:1.7.9" | ||||||
|  |         }] | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | ` | ||||||
|  |  | ||||||
| func autoscalingPath(resource, namespace, name string) string { | func autoscalingPath(resource, namespace, name string) string { | ||||||
| 	return testapi.Autoscaling.ResourcePath(resource, namespace, name) | 	return testapi.Autoscaling.ResourcePath(resource, namespace, name) | ||||||
| } | } | ||||||
| @@ -150,6 +204,10 @@ func extensionsPath(resource, namespace, name string) string { | |||||||
| 	return testapi.Extensions.ResourcePath(resource, namespace, name) | 	return testapi.Extensions.ResourcePath(resource, namespace, name) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func appsPath(resource, namespace, name string) string { | ||||||
|  | 	return testapi.Apps.ResourcePath(resource, namespace, name) | ||||||
|  | } | ||||||
|  |  | ||||||
| func TestAutoscalingGroupBackwardCompatibility(t *testing.T) { | func TestAutoscalingGroupBackwardCompatibility(t *testing.T) { | ||||||
| 	_, s := framework.RunAMaster(nil) | 	_, s := framework.RunAMaster(nil) | ||||||
| 	defer s.Close() | 	defer s.Close() | ||||||
| @@ -195,6 +253,59 @@ func TestAutoscalingGroupBackwardCompatibility(t *testing.T) { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func TestAppsGroupBackwardCompatibility(t *testing.T) { | ||||||
|  | 	_, s := framework.RunAMaster(nil) | ||||||
|  | 	defer s.Close() | ||||||
|  | 	transport := http.DefaultTransport | ||||||
|  |  | ||||||
|  | 	requests := []struct { | ||||||
|  | 		verb                string | ||||||
|  | 		URL                 string | ||||||
|  | 		body                string | ||||||
|  | 		expectedStatusCodes map[int]bool | ||||||
|  | 		expectedVersion     string | ||||||
|  | 	}{ | ||||||
|  | 		// Post to extensions endpoint and get back from both: extensions and apps | ||||||
|  | 		{"POST", extensionsPath("deployments", metav1.NamespaceDefault, ""), deploymentExtensions, integration.Code201, ""}, | ||||||
|  | 		{"GET", extensionsPath("deployments", metav1.NamespaceDefault, "test-deployment1"), "", integration.Code200, testapi.Extensions.GroupVersion().String()}, | ||||||
|  | 		{"GET", appsPath("deployments", metav1.NamespaceDefault, "test-deployment1"), "", integration.Code200, testapi.Apps.GroupVersion().String()}, | ||||||
|  | 		{"DELETE", extensionsPath("deployments", metav1.NamespaceDefault, "test-deployment1"), "", integration.Code200, testapi.Extensions.GroupVersion().String()}, | ||||||
|  | 		// Post to apps endpoint and get back from both: apps and extensions | ||||||
|  | 		{"POST", appsPath("deployments", metav1.NamespaceDefault, ""), deploymentApps, integration.Code201, ""}, | ||||||
|  | 		{"GET", appsPath("deployments", metav1.NamespaceDefault, "test-deployment2"), "", integration.Code200, testapi.Apps.GroupVersion().String()}, | ||||||
|  | 		{"GET", extensionsPath("deployments", metav1.NamespaceDefault, "test-deployment2"), "", integration.Code200, testapi.Extensions.GroupVersion().String()}, | ||||||
|  | 		{"DELETE", appsPath("deployments", metav1.NamespaceDefault, "test-deployment2"), "", integration.Code200, testapi.Apps.GroupVersion().String()}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, r := range requests { | ||||||
|  | 		bodyBytes := bytes.NewReader([]byte(r.body)) | ||||||
|  | 		req, err := http.NewRequest(r.verb, s.URL+r.URL, bodyBytes) | ||||||
|  | 		if err != nil { | ||||||
|  | 			t.Logf("case %v", r) | ||||||
|  | 			t.Fatalf("unexpected error: %v", err) | ||||||
|  | 		} | ||||||
|  | 		func() { | ||||||
|  | 			resp, err := transport.RoundTrip(req) | ||||||
|  | 			defer resp.Body.Close() | ||||||
|  | 			if err != nil { | ||||||
|  | 				t.Logf("case %v", r) | ||||||
|  | 				t.Fatalf("unexpected error: %v", err) | ||||||
|  | 			} | ||||||
|  | 			b, _ := ioutil.ReadAll(resp.Body) | ||||||
|  | 			body := string(b) | ||||||
|  | 			if _, ok := r.expectedStatusCodes[resp.StatusCode]; !ok { | ||||||
|  | 				t.Logf("case %v", r) | ||||||
|  | 				t.Errorf("Expected status one of %v, but got %v", r.expectedStatusCodes, resp.StatusCode) | ||||||
|  | 				t.Errorf("Body: %v", body) | ||||||
|  | 			} | ||||||
|  | 			if !strings.Contains(body, "\"apiVersion\":\""+r.expectedVersion) { | ||||||
|  | 				t.Logf("case %v", r) | ||||||
|  | 				t.Errorf("Expected version %v, got body %v", r.expectedVersion, body) | ||||||
|  | 			} | ||||||
|  | 		}() | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| func TestAccept(t *testing.T) { | func TestAccept(t *testing.T) { | ||||||
| 	_, s := framework.RunAMaster(nil) | 	_, s := framework.RunAMaster(nil) | ||||||
| 	defer s.Close() | 	defer s.Close() | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Maciej Szulik
					Maciej Szulik