mirror of https://github.com/k3s-io/k3s
Deployments under apps/v1beta1 with new defaults
parent
ac05be73ac
commit
c272630b1b
|
@ -49,7 +49,9 @@ import (
|
|||
serverstorage "k8s.io/apiserver/pkg/server/storage"
|
||||
"k8s.io/kubernetes/cmd/kube-apiserver/app/options"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/apis/apps"
|
||||
"k8s.io/kubernetes/pkg/apis/batch"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
"k8s.io/kubernetes/pkg/capabilities"
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
||||
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 {
|
||||
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 {
|
||||
tokens := strings.Split(override, "#")
|
||||
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/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/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"
|
||||
|
||||
### 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
|
||||
# Post-Condition: Deployment has 2 replicas defined in its spec.
|
||||
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
|
||||
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}}{{.name}}:{{end}}" 'sa-name:'
|
||||
fi
|
||||
|
||||
|
||||
if kube::test::if_supports_resource "${roles}" ; then
|
||||
kubectl create "${kube_flags[@]}" role pod-admin --verb=* --resource=pods
|
||||
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: "Deployment"}: {},
|
||||
{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: "ReplicaSetList"}: {},
|
||||
{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{} {
|
||||
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) {
|
||||
c.FuzzNoCustom(j) // fuzz self without calling this function again
|
||||
// Ensure that strategyType is one of valid values.
|
||||
|
|
|
@ -19,6 +19,7 @@ package apps
|
|||
import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -46,6 +47,10 @@ func Resource(resource string) schema.GroupResource {
|
|||
func addKnownTypes(scheme *runtime.Scheme) error {
|
||||
// TODO this will get cleaned up with the scheme types are fixed
|
||||
scheme.AddKnownTypes(SchemeGroupVersion,
|
||||
&extensions.Deployment{},
|
||||
&extensions.DeploymentList{},
|
||||
&extensions.DeploymentRollback{},
|
||||
&extensions.Scale{},
|
||||
&StatefulSet{},
|
||||
&StatefulSetList{},
|
||||
)
|
||||
|
|
|
@ -22,9 +22,11 @@ import (
|
|||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/conversion"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
"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/extensions"
|
||||
)
|
||||
|
||||
func addConversionFuncs(scheme *runtime.Scheme) error {
|
||||
|
@ -35,21 +37,49 @@ func addConversionFuncs(scheme *runtime.Scheme) error {
|
|||
err := scheme.AddConversionFuncs(
|
||||
Convert_v1beta1_StatefulSetSpec_To_apps_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 {
|
||||
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) {
|
||||
switch label {
|
||||
case "metadata.name", "metadata.namespace", "status.successful":
|
||||
return label, value, nil
|
||||
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 {
|
||||
|
@ -112,3 +142,156 @@ func Convert_apps_StatefulSetSpec_To_v1beta1_StatefulSetSpec(in *apps.StatefulSe
|
|||
out.ServiceName = in.ServiceName
|
||||
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 (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
)
|
||||
|
||||
func addDefaultingFuncs(scheme *runtime.Scheme) error {
|
||||
RegisterDefaults(scheme)
|
||||
return scheme.AddDefaultingFuncs(
|
||||
SetDefaults_StatefulSet,
|
||||
SetDefaults_Deployment,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -45,3 +47,57 @@ func SetDefaults_StatefulSet(obj *StatefulSet) {
|
|||
*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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
func addKnownTypes(scheme *runtime.Scheme) error {
|
||||
scheme.AddKnownTypes(SchemeGroupVersion,
|
||||
&Deployment{},
|
||||
&DeploymentList{},
|
||||
&DeploymentRollback{},
|
||||
&Scale{},
|
||||
&StatefulSet{},
|
||||
&StatefulSetList{},
|
||||
)
|
||||
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
|
||||
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 (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
)
|
||||
|
||||
|
@ -26,6 +27,51 @@ const (
|
|||
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
|
||||
|
||||
// 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"`
|
||||
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
|
||||
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", "ReplicaSet", 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 {
|
||||
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
|
||||
default:
|
||||
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 {
|
||||
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
|
||||
default:
|
||||
return fmt.Errorf("cannot autoscale a %v", kind)
|
||||
|
|
|
@ -28,6 +28,7 @@ import (
|
|||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
"k8s.io/kubernetes/pkg/apis/apps"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
||||
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) {
|
||||
switch kind {
|
||||
case extensions.Kind("Deployment"):
|
||||
case extensions.Kind("Deployment"), apps.Kind("Deployment"):
|
||||
return &DeploymentHistoryViewer{c}, nil
|
||||
}
|
||||
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/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
"k8s.io/kubernetes/pkg/apis/apps"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
externalextensions "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
|
||||
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) {
|
||||
switch kind {
|
||||
case extensions.Kind("Deployment"):
|
||||
case extensions.Kind("Deployment"), apps.Kind("Deployment"):
|
||||
return &DeploymentRollbacker{c}, nil
|
||||
}
|
||||
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"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/kubernetes/pkg/apis/apps"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
||||
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) {
|
||||
switch kind {
|
||||
case extensions.Kind("Deployment"):
|
||||
case extensions.Kind("Deployment"), apps.Kind("Deployment"):
|
||||
return &DeploymentStatusViewer{c.Extensions()}, nil
|
||||
case extensions.Kind("DaemonSet"):
|
||||
return &DaemonSetStatusViewer{c.Extensions()}, nil
|
||||
|
|
|
@ -26,7 +26,7 @@ import (
|
|||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/validation"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
batch "k8s.io/kubernetes/pkg/apis/batch"
|
||||
"k8s.io/kubernetes/pkg/apis/batch"
|
||||
"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.
|
||||
case apps.Kind("StatefulSet"):
|
||||
return &StatefulSetScaler{c.Apps()}, nil
|
||||
case extensions.Kind("Deployment"):
|
||||
case extensions.Kind("Deployment"), apps.Kind("Deployment"):
|
||||
return &DeploymentScaler{c.Extensions()}, nil
|
||||
}
|
||||
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"):
|
||||
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
|
||||
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ import (
|
|||
"k8s.io/kubernetes/cmd/kube-apiserver/app/options"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
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"
|
||||
authenticationv1beta1 "k8s.io/kubernetes/pkg/apis/authentication/v1beta1"
|
||||
authorizationapiv1 "k8s.io/kubernetes/pkg/apis/authorization/v1"
|
||||
|
@ -367,7 +367,7 @@ func DefaultAPIResourceConfigSource() *serverstorage.ResourceConfig {
|
|||
authenticationv1.SchemeGroupVersion,
|
||||
authenticationv1beta1.SchemeGroupVersion,
|
||||
autoscalingapiv1.SchemeGroupVersion,
|
||||
appsapi.SchemeGroupVersion,
|
||||
appsv1beta1.SchemeGroupVersion,
|
||||
policyapiv1beta1.SchemeGroupVersion,
|
||||
rbacv1beta1.SchemeGroupVersion,
|
||||
rbacapi.SchemeGroupVersion,
|
||||
|
|
|
@ -118,6 +118,7 @@ func describerMap(c clientset.Interface) map[schema.GroupKind]printers.Describer
|
|||
batch.Kind("Job"): &JobDescriber{c},
|
||||
batch.Kind("CronJob"): &CronJobDescriber{c},
|
||||
apps.Kind("StatefulSet"): &StatefulSetDescriber{c},
|
||||
apps.Kind("Deployment"): &DeploymentDescriber{c, versionedClientsetForDeployment(c)},
|
||||
certificates.Kind("CertificateSigningRequest"): &CertificateSigningRequestDescriber{c},
|
||||
storage.Kind("StorageClass"): &StorageClassDescriber{c},
|
||||
policy.Kind("PodDisruptionBudget"): &PodDisruptionBudgetDescriber{c},
|
||||
|
|
|
@ -25,6 +25,7 @@ import (
|
|||
"k8s.io/kubernetes/pkg/apis/apps"
|
||||
appsapiv1beta1 "k8s.io/kubernetes/pkg/apis/apps/v1beta1"
|
||||
statefulsetstore "k8s.io/kubernetes/pkg/registry/apps/petset/storage"
|
||||
deploymentstore "k8s.io/kubernetes/pkg/registry/extensions/deployment/storage"
|
||||
)
|
||||
|
||||
type RESTStorageProvider struct{}
|
||||
|
@ -44,6 +45,13 @@ func (p RESTStorageProvider) v1beta1Storage(apiResourceConfigSource serverstorag
|
|||
version := appsapiv1beta1.SchemeGroupVersion
|
||||
|
||||
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")) {
|
||||
statefulsetStorage, statefulsetStatusStorage := statefulsetstore.NewREST(restOptionsGetter)
|
||||
storage["statefulsets"] = statefulsetStorage
|
||||
|
|
|
@ -92,8 +92,8 @@ func init() {
|
|||
addControllerRole(rbac.ClusterRole{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: saRolePrefix + "deployment-controller"},
|
||||
Rules: []rbac.PolicyRule{
|
||||
rbac.NewRule("get", "list", "watch", "update").Groups(extensionsGroup).Resources("deployments").RuleOrDie(),
|
||||
rbac.NewRule("update").Groups(extensionsGroup).Resources("deployments/status").RuleOrDie(),
|
||||
rbac.NewRule("get", "list", "watch", "update").Groups(extensionsGroup, appsGroup).Resources("deployments").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(),
|
||||
// TODO: remove "update" once
|
||||
// https://github.com/kubernetes/kubernetes/issues/36897 is resolved.
|
||||
|
@ -104,7 +104,7 @@ func init() {
|
|||
addControllerRole(rbac.ClusterRole{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: saRolePrefix + "disruption-controller"},
|
||||
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(legacyGroup).Resources("replicationcontrollers").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(),
|
||||
// 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("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(),
|
||||
// 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(),
|
||||
|
|
|
@ -127,14 +127,16 @@ func ClusterRoles() []rbac.ClusterRole {
|
|||
rbac.NewRule(Read...).Groups(legacyGroup).Resources("namespaces").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(batchGroup).Resources("jobs", "cronjobs", "scheduledjobs").RuleOrDie(),
|
||||
|
||||
rbac.NewRule(ReadWrite...).Groups(extensionsGroup).Resources("daemonsets", "deployments", "deployments/scale",
|
||||
"ingresses", "replicasets", "replicasets/scale", "replicationcontrollers/scale").RuleOrDie(),
|
||||
rbac.NewRule(ReadWrite...).Groups(extensionsGroup).Resources("daemonsets",
|
||||
"deployments", "deployments/scale", "deployments/rollback", "ingresses",
|
||||
"replicasets", "replicasets/scale", "replicationcontrollers/scale").RuleOrDie(),
|
||||
|
||||
// additional admin powers
|
||||
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("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(batchGroup).Resources("jobs", "cronjobs", "scheduledjobs").RuleOrDie(),
|
||||
|
||||
rbac.NewRule(ReadWrite...).Groups(extensionsGroup).Resources("daemonsets", "deployments", "deployments/scale",
|
||||
"ingresses", "replicasets", "replicasets/scale", "replicationcontrollers/scale").RuleOrDie(),
|
||||
rbac.NewRule(ReadWrite...).Groups(extensionsGroup).Resources("daemonsets",
|
||||
"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.
|
||||
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(),
|
||||
|
||||
|
@ -320,6 +324,7 @@ func ClusterRoles() []rbac.ClusterRole {
|
|||
"podsecuritypolicies",
|
||||
"replicasets",
|
||||
).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(appsGroup).Resources("statefulsets").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(),
|
||||
}
|
||||
|
||||
// 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) {
|
||||
readVerbs := sets.NewString(bootstrappolicy.Read...)
|
||||
semanticRoles := getSemanticRoles(bootstrappolicy.ClusterRoles())
|
||||
|
@ -142,6 +148,14 @@ func TestEditViewRelationship(t *testing.T) {
|
|||
}
|
||||
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
|
||||
if covers, miss := rbacregistryvalidation.Covers(semanticRoles.edit.Rules, semanticRoles.view.Rules); !covers {
|
||||
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)
|
||||
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) {
|
||||
By(fmt.Sprintf("Failed getting deployment: %q/%q/%q, err: %v", cluster.name, namespace, deploymentName, 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 {
|
||||
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)
|
||||
}
|
||||
|
||||
func appsPath(resource, namespace, name string) string {
|
||||
return testapi.Apps.ResourcePath(resource, namespace, name)
|
||||
}
|
||||
|
||||
func TestAutoscalingGroupBackwardCompatibility(t *testing.T) {
|
||||
_, s := framework.RunAMaster(nil)
|
||||
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) {
|
||||
_, s := framework.RunAMaster(nil)
|
||||
defer s.Close()
|
||||
|
|
Loading…
Reference in New Issue