Commit 10aee82a authored by Janet Kuo's avatar Janet Kuo

Rename PetSet API to StatefulSet

parent a266f72b
apiVersion: "apps/v1alpha1"
kind: PetSet
kind: StatefulSet
metadata:
name: calico-etcd
namespace: kube-system
......
......@@ -440,15 +440,15 @@ func StartControllers(s *options.CMServer, kubeconfig *restclient.Config, rootCl
groupVersion = "apps/v1alpha1"
resources, found = resourceMap[groupVersion]
glog.Infof("Attempting to start petset, full resource map %+v", resourceMap)
glog.Infof("Attempting to start statefulset, full resource map %+v", resourceMap)
if containsVersion(versions, groupVersion) && found {
glog.Infof("Starting %s apis", groupVersion)
if containsResource(resources, "petsets") {
glog.Infof("Starting PetSet controller")
if containsResource(resources, "statefulsets") {
glog.Infof("Starting StatefulSet controller")
resyncPeriod := ResyncPeriod(s)()
go petset.NewPetSetController(
go petset.NewStatefulSetController(
sharedInformers.Pods().Informer(),
client("petset-controller"),
client("statefulset-controller"),
resyncPeriod,
).Run(1, wait.NeverStop)
time.Sleep(wait.Jitter(s.ControllerStartInterval.Duration, ControllerStartJitter))
......
......@@ -130,11 +130,11 @@ func validateObject(obj runtime.Object) (errors field.ErrorList) {
t.Namespace = api.NamespaceDefault
}
errors = expvalidation.ValidateDaemonSet(t)
case *apps.PetSet:
case *apps.StatefulSet:
if t.Namespace == "" {
t.Namespace = api.NamespaceDefault
}
errors = appsvalidation.ValidatePetSet(t)
errors = appsvalidation.ValidateStatefulSet(t)
default:
errors = field.ErrorList{}
errors = append(errors, field.InternalError(field.NewPath(""), fmt.Errorf("no validation defined for %#v", obj)))
......@@ -221,7 +221,7 @@ func TestExampleObjectSchemas(t *testing.T) {
"cassandra-daemonset": &extensions.DaemonSet{},
"cassandra-controller": &api.ReplicationController{},
"cassandra-service": &api.Service{},
"cassandra-petset": &apps.PetSet{},
"cassandra-petset": &apps.StatefulSet{},
},
"../examples/cluster-dns": {
"dns-backend-rc": &api.ReplicationController{},
......
apiVersion: "apps/v1alpha1"
kind: PetSet
kind: StatefulSet
metadata:
name: cassandra
spec:
......
......@@ -334,7 +334,7 @@ runTests() {
hpa_min_field=".spec.minReplicas"
hpa_max_field=".spec.maxReplicas"
hpa_cpu_field=".spec.targetCPUUtilizationPercentage"
petset_replicas_field=".spec.replicas"
statefulset_replicas_field=".spec.replicas"
job_parallelism_field=".spec.parallelism"
deployment_replicas=".spec.replicas"
secret_data=".data"
......@@ -1185,7 +1185,7 @@ __EOF__
kube::test::if_has_string "${output_message}" "/api/v1/namespaces/default/pods 200 OK"
kube::test::if_has_string "${output_message}" "/api/v1/namespaces/default/replicationcontrollers 200 OK"
kube::test::if_has_string "${output_message}" "/api/v1/namespaces/default/services 200 OK"
kube::test::if_has_string "${output_message}" "/apis/apps/v1alpha1/namespaces/default/petsets 200 OK"
kube::test::if_has_string "${output_message}" "/apis/apps/v1alpha1/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"
......@@ -2396,25 +2396,25 @@ __EOF__
############
# Pet Sets #
############
#################
# Stateful Sets #
#################
kube::log::status "Testing kubectl(${version}:petsets)"
kube::log::status "Testing kubectl(${version}:statefulsets)"
### Create and stop petset, make sure it doesn't leak pods
# Pre-condition: no petset exists
kube::test::get_object_assert petset "{{range.items}}{{$id_field}}:{{end}}" ''
# Command: create petset
### Create and stop statefulset, make sure it doesn't leak pods
# Pre-condition: no statefulset exists
kube::test::get_object_assert statefulset "{{range.items}}{{$id_field}}:{{end}}" ''
# Command: create statefulset
kubectl create -f hack/testdata/nginx-petset.yaml "${kube_flags[@]}"
### Scale petset test with current-replicas and replicas
### Scale statefulset test with current-replicas and replicas
# Pre-condition: 0 replicas
kube::test::get_object_assert 'petset nginx' "{{$petset_replicas_field}}" '0'
kube::test::get_object_assert 'statefulset nginx' "{{$statefulset_replicas_field}}" '0'
# Command: Scale up
kubectl scale --current-replicas=0 --replicas=1 petset nginx "${kube_flags[@]}"
kubectl scale --current-replicas=0 --replicas=1 statefulset nginx "${kube_flags[@]}"
# Post-condition: 1 replica, named nginx-0
kube::test::get_object_assert 'petset nginx' "{{$petset_replicas_field}}" '1'
kube::test::get_object_assert 'statefulset nginx' "{{$statefulset_replicas_field}}" '1'
# Typically we'd wait and confirm that N>1 replicas are up, but this framework
# doesn't start the scheduler, so pet-0 will block all others.
# TODO: test robust scaling in an e2e.
......@@ -2422,7 +2422,7 @@ __EOF__
### Clean up
kubectl delete -f hack/testdata/nginx-petset.yaml "${kube_flags[@]}"
# Post-condition: no pods from petset controller
# Post-condition: no pods from statefulset controller
wait-for-pods-with-label "app=nginx-petset" ""
......
......@@ -17,7 +17,7 @@ spec:
app: nginx-petset
---
apiVersion: apps/v1alpha1
kind: PetSet
kind: StatefulSet
metadata:
name: nginx
spec:
......
......@@ -77,8 +77,8 @@ func TestDefaulting(t *testing.T) {
{Group: "", Version: "v1", Kind: "SecretList"}: {},
{Group: "", Version: "v1", Kind: "Service"}: {},
{Group: "", Version: "v1", Kind: "ServiceList"}: {},
{Group: "apps", Version: "v1alpha1", Kind: "PetSet"}: {},
{Group: "apps", Version: "v1alpha1", Kind: "PetSetList"}: {},
{Group: "apps", Version: "v1alpha1", Kind: "StatefulSet"}: {},
{Group: "apps", Version: "v1alpha1", Kind: "StatefulSetList"}: {},
{Group: "autoscaling", Version: "v1", Kind: "HorizontalPodAutoscaler"}: {},
{Group: "autoscaling", Version: "v1", Kind: "HorizontalPodAutoscalerList"}: {},
{Group: "batch", Version: "v1", Kind: "Job"}: {},
......
......@@ -175,7 +175,7 @@ func TestKindForGroupVersionKinds(t *testing.T) {
ok: true,
},
{
input: []GroupVersionKind{{Group: "apps", Version: "v1alpha1", Kind: "PetSet"}},
input: []GroupVersionKind{{Group: "apps", Version: "v1alpha1", Kind: "StatefulSet"}},
target: GroupVersionKind{},
ok: false,
},
......
......@@ -47,8 +47,8 @@ func Resource(resource string) unversioned.GroupResource {
func addKnownTypes(scheme *runtime.Scheme) error {
// TODO this will get cleaned up with the scheme types are fixed
scheme.AddKnownTypes(SchemeGroupVersion,
&PetSet{},
&PetSetList{},
&StatefulSet{},
&StatefulSetList{},
&api.ListOptions{},
&api.DeleteOptions{},
)
......
......@@ -23,30 +23,30 @@ import (
// +genclient=true
// PetSet represents a set of pods with consistent identities.
// StatefulSet represents a set of pods with consistent identities.
// Identities are defined as:
// - Network: A single stable DNS and hostname.
// - Storage: As many VolumeClaims as requested.
// The PetSet guarantees that a given network identity will always
// map to the same storage identity. PetSet is currently in alpha and
// The StatefulSet guarantees that a given network identity will always
// map to the same storage identity. StatefulSet is currently in alpha and
// and subject to change without notice.
type PetSet struct {
type StatefulSet struct {
unversioned.TypeMeta `json:",inline"`
// +optional
api.ObjectMeta `json:"metadata,omitempty"`
// Spec defines the desired identities of pets in this set.
// Spec defines the desired identities of pods in this set.
// +optional
Spec PetSetSpec `json:"spec,omitempty"`
Spec StatefulSetSpec `json:"spec,omitempty"`
// Status is the current status of Pets in this PetSet. This data
// Status is the current status of Pods in this StatefulSet. This data
// may be out of date by some window of time.
// +optional
Status PetSetStatus `json:"status,omitempty"`
Status StatefulSetStatus `json:"status,omitempty"`
}
// A PetSetSpec is the specification of a PetSet.
type PetSetSpec struct {
// A StatefulSetSpec is the specification of a StatefulSet.
type StatefulSetSpec struct {
// Replicas is the desired number of replicas of the given Template.
// These are replicas in the sense that they are instantiations of the
// same Template, but individual replicas also have a consistent identity.
......@@ -62,14 +62,14 @@ type PetSetSpec struct {
Selector *unversioned.LabelSelector `json:"selector,omitempty"`
// Template is the object that describes the pod that will be created if
// insufficient replicas are detected. Each pod stamped out by the PetSet
// insufficient replicas are detected. Each pod stamped out by the StatefulSet
// will fulfill this Template, but have a unique identity from the rest
// of the PetSet.
// of the StatefulSet.
Template api.PodTemplateSpec `json:"template"`
// VolumeClaimTemplates is a list of claims that pets are allowed to reference.
// The PetSet controller is responsible for mapping network identities to
// claims in a way that maintains the identity of a pet. Every claim in
// VolumeClaimTemplates is a list of claims that pods are allowed to reference.
// The StatefulSet controller is responsible for mapping network identities to
// claims in a way that maintains the identity of a pod. Every claim in
// this list must have at least one matching (by name) volumeMount in one
// container in the template. A claim in this list takes precedence over
// any volumes in the template, with the same name.
......@@ -77,16 +77,16 @@ type PetSetSpec struct {
// +optional
VolumeClaimTemplates []api.PersistentVolumeClaim `json:"volumeClaimTemplates,omitempty"`
// ServiceName is the name of the service that governs this PetSet.
// This service must exist before the PetSet, and is responsible for
// the network identity of the set. Pets get DNS/hostnames that follow the
// pattern: pet-specific-string.serviceName.default.svc.cluster.local
// where "pet-specific-string" is managed by the PetSet controller.
// ServiceName is the name of the service that governs this StatefulSet.
// This service must exist before the StatefulSet, and is responsible for
// the network identity of the set. Pods get DNS/hostnames that follow the
// pattern: pod-specific-string.serviceName.default.svc.cluster.local
// where "pod-specific-string" is managed by the StatefulSet controller.
ServiceName string `json:"serviceName"`
}
// PetSetStatus represents the current state of a PetSet.
type PetSetStatus struct {
// StatefulSetStatus represents the current state of a StatefulSet.
type StatefulSetStatus struct {
// most recent generation observed by this autoscaler.
// +optional
ObservedGeneration *int64 `json:"observedGeneration,omitempty"`
......@@ -95,10 +95,10 @@ type PetSetStatus struct {
Replicas int32 `json:"replicas"`
}
// PetSetList is a collection of PetSets.
type PetSetList struct {
// StatefulSetList is a collection of StatefulSets.
type StatefulSetList struct {
unversioned.TypeMeta `json:",inline"`
// +optional
unversioned.ListMeta `json:"metadata,omitempty"`
Items []PetSet `json:"items"`
Items []StatefulSet `json:"items"`
}
......@@ -33,14 +33,14 @@ func addConversionFuncs(scheme *runtime.Scheme) error {
// it, but a plain int32 is more convenient in the internal type. These
// functions are the same as the autogenerated ones in every other way.
err := scheme.AddConversionFuncs(
Convert_v1alpha1_PetSetSpec_To_apps_PetSetSpec,
Convert_apps_PetSetSpec_To_v1alpha1_PetSetSpec,
Convert_v1alpha1_StatefulSetSpec_To_apps_StatefulSetSpec,
Convert_apps_StatefulSetSpec_To_v1alpha1_StatefulSetSpec,
)
if err != nil {
return err
}
return api.Scheme.AddFieldLabelConversionFunc("apps/v1alpha1", "PetSet",
return api.Scheme.AddFieldLabelConversionFunc("apps/v1alpha1", "StatefulSet",
func(label, value string) (string, string, error) {
switch label {
case "metadata.name", "metadata.namespace", "status.successful":
......@@ -52,7 +52,7 @@ func addConversionFuncs(scheme *runtime.Scheme) error {
)
}
func Convert_v1alpha1_PetSetSpec_To_apps_PetSetSpec(in *PetSetSpec, out *apps.PetSetSpec, s conversion.Scope) error {
func Convert_v1alpha1_StatefulSetSpec_To_apps_StatefulSetSpec(in *StatefulSetSpec, out *apps.StatefulSetSpec, s conversion.Scope) error {
if in.Replicas != nil {
out.Replicas = *in.Replicas
}
......@@ -83,7 +83,7 @@ func Convert_v1alpha1_PetSetSpec_To_apps_PetSetSpec(in *PetSetSpec, out *apps.Pe
return nil
}
func Convert_apps_PetSetSpec_To_v1alpha1_PetSetSpec(in *apps.PetSetSpec, out *PetSetSpec, s conversion.Scope) error {
func Convert_apps_StatefulSetSpec_To_v1alpha1_StatefulSetSpec(in *apps.StatefulSetSpec, out *StatefulSetSpec, s conversion.Scope) error {
out.Replicas = new(int32)
*out.Replicas = in.Replicas
if in.Selector != nil {
......
......@@ -24,11 +24,11 @@ import (
func addDefaultingFuncs(scheme *runtime.Scheme) error {
RegisterDefaults(scheme)
return scheme.AddDefaultingFuncs(
SetDefaults_PetSet,
SetDefaults_StatefulSet,
)
}
func SetDefaults_PetSet(obj *PetSet) {
func SetDefaults_StatefulSet(obj *StatefulSet) {
labels := obj.Spec.Template.Labels
if labels != nil {
if obj.Spec.Selector == nil {
......
......@@ -37,8 +37,8 @@ var (
// Adds the list of known types to api.Scheme.
func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion,
&PetSet{},
&PetSetList{},
&StatefulSet{},
&StatefulSetList{},
&v1.ListOptions{},
&v1.DeleteOptions{},
)
......@@ -46,5 +46,5 @@ func addKnownTypes(scheme *runtime.Scheme) error {
return nil
}
func (obj *PetSet) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
func (obj *PetSetList) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
func (obj *StatefulSet) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
func (obj *StatefulSetList) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
......@@ -23,30 +23,30 @@ import (
// +genclient=true
// PetSet represents a set of pods with consistent identities.
// StatefulSet represents a set of pods with consistent identities.
// Identities are defined as:
// - Network: A single stable DNS and hostname.
// - Storage: As many VolumeClaims as requested.
// The PetSet guarantees that a given network identity will always
// map to the same storage identity. PetSet is currently in alpha
// The StatefulSet guarantees that a given network identity will always
// map to the same storage identity. StatefulSet is currently in alpha
// and subject to change without notice.
type PetSet struct {
type StatefulSet struct {
unversioned.TypeMeta `json:",inline"`
// +optional
v1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
// Spec defines the desired identities of pets in this set.
// Spec defines the desired identities of pods in this set.
// +optional
Spec PetSetSpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"`
Spec StatefulSetSpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"`
// Status is the current status of Pets in this PetSet. This data
// Status is the current status of Pods in this StatefulSet. This data
// may be out of date by some window of time.
// +optional
Status PetSetStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"`
Status StatefulSetStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"`
}
// A PetSetSpec is the specification of a PetSet.
type PetSetSpec struct {
// A StatefulSetSpec is the specification of a StatefulSet.
type StatefulSetSpec struct {
// Replicas is the desired number of replicas of the given Template.
// These are replicas in the sense that they are instantiations of the
// same Template, but individual replicas also have a consistent identity.
......@@ -62,14 +62,14 @@ type PetSetSpec struct {
Selector *unversioned.LabelSelector `json:"selector,omitempty" protobuf:"bytes,2,opt,name=selector"`
// Template is the object that describes the pod that will be created if
// insufficient replicas are detected. Each pod stamped out by the PetSet
// insufficient replicas are detected. Each pod stamped out by the StatefulSet
// will fulfill this Template, but have a unique identity from the rest
// of the PetSet.
// of the StatefulSet.
Template v1.PodTemplateSpec `json:"template" protobuf:"bytes,3,opt,name=template"`
// VolumeClaimTemplates is a list of claims that pets are allowed to reference.
// The PetSet controller is responsible for mapping network identities to
// claims in a way that maintains the identity of a pet. Every claim in
// VolumeClaimTemplates is a list of claims that pods are allowed to reference.
// The StatefulSet controller is responsible for mapping network identities to
// claims in a way that maintains the identity of a pod. Every claim in
// this list must have at least one matching (by name) volumeMount in one
// container in the template. A claim in this list takes precedence over
// any volumes in the template, with the same name.
......@@ -77,16 +77,16 @@ type PetSetSpec struct {
// +optional
VolumeClaimTemplates []v1.PersistentVolumeClaim `json:"volumeClaimTemplates,omitempty" protobuf:"bytes,4,rep,name=volumeClaimTemplates"`
// ServiceName is the name of the service that governs this PetSet.
// This service must exist before the PetSet, and is responsible for
// the network identity of the set. Pets get DNS/hostnames that follow the
// pattern: pet-specific-string.serviceName.default.svc.cluster.local
// where "pet-specific-string" is managed by the PetSet controller.
// ServiceName is the name of the service that governs this StatefulSet.
// This service must exist before the StatefulSet, and is responsible for
// the network identity of the set. Pods get DNS/hostnames that follow the
// pattern: pod-specific-string.serviceName.default.svc.cluster.local
// where "pod-specific-string" is managed by the StatefulSet controller.
ServiceName string `json:"serviceName" protobuf:"bytes,5,opt,name=serviceName"`
}
// PetSetStatus represents the current state of a PetSet.
type PetSetStatus struct {
// StatefulSetStatus represents the current state of a StatefulSet.
type StatefulSetStatus struct {
// most recent generation observed by this autoscaler.
// +optional
ObservedGeneration *int64 `json:"observedGeneration,omitempty" protobuf:"varint,1,opt,name=observedGeneration"`
......@@ -95,10 +95,10 @@ type PetSetStatus struct {
Replicas int32 `json:"replicas" protobuf:"varint,2,opt,name=replicas"`
}
// PetSetList is a collection of PetSets.
type PetSetList struct {
// StatefulSetList is a collection of StatefulSets.
type StatefulSetList struct {
unversioned.TypeMeta `json:",inline"`
// +optional
unversioned.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
Items []PetSet `json:"items" protobuf:"bytes,2,rep,name=items"`
Items []StatefulSet `json:"items" protobuf:"bytes,2,rep,name=items"`
}
......@@ -28,24 +28,24 @@ import (
"k8s.io/kubernetes/pkg/util/validation/field"
)
// ValidatePetSetName can be used to check whether the given PetSet name is valid.
// ValidateStatefulSetName can be used to check whether the given StatefulSet name is valid.
// Prefix indicates this name will be used as part of generation, in which case
// trailing dashes are allowed.
func ValidatePetSetName(name string, prefix bool) []string {
// TODO: Validate that there's name for the suffix inserted by the pets.
func ValidateStatefulSetName(name string, prefix bool) []string {
// TODO: Validate that there's name for the suffix inserted by the pods.
// Currently this is just "-index". In the future we may allow a user
// specified list of suffixes and we need to validate the longest one.
return apivalidation.NameIsDNSSubdomain(name, prefix)
}
// Validates the given template and ensures that it is in accordance with the desired selector.
func ValidatePodTemplateSpecForPetSet(template *api.PodTemplateSpec, selector labels.Selector, fldPath *field.Path) field.ErrorList {
func ValidatePodTemplateSpecForStatefulSet(template *api.PodTemplateSpec, selector labels.Selector, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if template == nil {
allErrs = append(allErrs, field.Required(fldPath, ""))
} else {
if !selector.Empty() {
// Verify that the PetSet selector matches the labels in template.
// Verify that the StatefulSet selector matches the labels in template.
labels := labels.Set(template.Labels)
if !selector.Matches(labels) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("metadata", "labels"), template.Labels, "`selector` does not match template `labels`"))
......@@ -62,8 +62,8 @@ func ValidatePodTemplateSpecForPetSet(template *api.PodTemplateSpec, selector la
return allErrs
}
// ValidatePetSetSpec tests if required fields in the PetSet spec are set.
func ValidatePetSetSpec(spec *apps.PetSetSpec, fldPath *field.Path) field.ErrorList {
// ValidateStatefulSetSpec tests if required fields in the StatefulSet spec are set.
func ValidateStatefulSetSpec(spec *apps.StatefulSetSpec, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(spec.Replicas), fldPath.Child("replicas"))...)
......@@ -72,7 +72,7 @@ func ValidatePetSetSpec(spec *apps.PetSetSpec, fldPath *field.Path) field.ErrorL
} else {
allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(spec.Selector, fldPath.Child("selector"))...)
if len(spec.Selector.MatchLabels)+len(spec.Selector.MatchExpressions) == 0 {
allErrs = append(allErrs, field.Invalid(fldPath.Child("selector"), spec.Selector, "empty selector is not valid for petset."))
allErrs = append(allErrs, field.Invalid(fldPath.Child("selector"), spec.Selector, "empty selector is not valid for statefulset."))
}
}
......@@ -80,7 +80,7 @@ func ValidatePetSetSpec(spec *apps.PetSetSpec, fldPath *field.Path) field.ErrorL
if err != nil {
allErrs = append(allErrs, field.Invalid(fldPath.Child("selector"), spec.Selector, ""))
} else {
allErrs = append(allErrs, ValidatePodTemplateSpecForPetSet(&spec.Template, selector, fldPath.Child("template"))...)
allErrs = append(allErrs, ValidatePodTemplateSpecForStatefulSet(&spec.Template, selector, fldPath.Child("template"))...)
}
if spec.Template.Spec.RestartPolicy != api.RestartPolicyAlways {
......@@ -90,42 +90,42 @@ func ValidatePetSetSpec(spec *apps.PetSetSpec, fldPath *field.Path) field.ErrorL
return allErrs
}
// ValidatePetSet validates a PetSet.
func ValidatePetSet(petSet *apps.PetSet) field.ErrorList {
allErrs := apivalidation.ValidateObjectMeta(&petSet.ObjectMeta, true, ValidatePetSetName, field.NewPath("metadata"))
allErrs = append(allErrs, ValidatePetSetSpec(&petSet.Spec, field.NewPath("spec"))...)
// ValidateStatefulSet validates a StatefulSet.
func ValidateStatefulSet(statefulSet *apps.StatefulSet) field.ErrorList {
allErrs := apivalidation.ValidateObjectMeta(&statefulSet.ObjectMeta, true, ValidateStatefulSetName, field.NewPath("metadata"))
allErrs = append(allErrs, ValidateStatefulSetSpec(&statefulSet.Spec, field.NewPath("spec"))...)
return allErrs
}
// ValidatePetSetUpdate tests if required fields in the PetSet are set.
func ValidatePetSetUpdate(petSet, oldPetSet *apps.PetSet) field.ErrorList {
allErrs := apivalidation.ValidateObjectMetaUpdate(&petSet.ObjectMeta, &oldPetSet.ObjectMeta, field.NewPath("metadata"))
// ValidateStatefulSetUpdate tests if required fields in the StatefulSet are set.
func ValidateStatefulSetUpdate(statefulSet, oldStatefulSet *apps.StatefulSet) field.ErrorList {
allErrs := apivalidation.ValidateObjectMetaUpdate(&statefulSet.ObjectMeta, &oldStatefulSet.ObjectMeta, field.NewPath("metadata"))
// TODO: For now we're taking the safe route and disallowing all updates to
// spec except for Replicas, for scaling, and Template.Spec.containers.image
// for rolling-update. Enable others on a case by case basis.
restoreReplicas := petSet.Spec.Replicas
petSet.Spec.Replicas = oldPetSet.Spec.Replicas
restoreReplicas := statefulSet.Spec.Replicas
statefulSet.Spec.Replicas = oldStatefulSet.Spec.Replicas
restoreContainers := petSet.Spec.Template.Spec.Containers
petSet.Spec.Template.Spec.Containers = oldPetSet.Spec.Template.Spec.Containers
restoreContainers := statefulSet.Spec.Template.Spec.Containers
statefulSet.Spec.Template.Spec.Containers = oldStatefulSet.Spec.Template.Spec.Containers
if !reflect.DeepEqual(petSet.Spec, oldPetSet.Spec) {
allErrs = append(allErrs, field.Forbidden(field.NewPath("spec"), "updates to petset spec for fields other than 'replicas' are forbidden."))
if !reflect.DeepEqual(statefulSet.Spec, oldStatefulSet.Spec) {
allErrs = append(allErrs, field.Forbidden(field.NewPath("spec"), "updates to statefulset spec for fields other than 'replicas' are forbidden."))
}
petSet.Spec.Replicas = restoreReplicas
petSet.Spec.Template.Spec.Containers = restoreContainers
statefulSet.Spec.Replicas = restoreReplicas
statefulSet.Spec.Template.Spec.Containers = restoreContainers
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(petSet.Spec.Replicas), field.NewPath("spec", "replicas"))...)
containerErrs, _ := apivalidation.ValidateContainerUpdates(petSet.Spec.Template.Spec.Containers, oldPetSet.Spec.Template.Spec.Containers, field.NewPath("spec").Child("template").Child("containers"))
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(statefulSet.Spec.Replicas), field.NewPath("spec", "replicas"))...)
containerErrs, _ := apivalidation.ValidateContainerUpdates(statefulSet.Spec.Template.Spec.Containers, oldStatefulSet.Spec.Template.Spec.Containers, field.NewPath("spec").Child("template").Child("containers"))
allErrs = append(allErrs, containerErrs...)
return allErrs
}
// ValidatePetSetStatusUpdate tests if required fields in the PetSet are set.
func ValidatePetSetStatusUpdate(petSet, oldPetSet *apps.PetSet) field.ErrorList {
// ValidateStatefulSetStatusUpdate tests if required fields in the StatefulSet are set.
func ValidateStatefulSetStatusUpdate(statefulSet, oldStatefulSet *apps.StatefulSet) field.ErrorList {
allErrs := field.ErrorList{}
allErrs = append(allErrs, apivalidation.ValidateObjectMetaUpdate(&petSet.ObjectMeta, &oldPetSet.ObjectMeta, field.NewPath("metadata"))...)
allErrs = append(allErrs, apivalidation.ValidateObjectMetaUpdate(&statefulSet.ObjectMeta, &oldStatefulSet.ObjectMeta, field.NewPath("metadata"))...)
// TODO: Validate status.
return allErrs
}
......@@ -364,13 +364,13 @@ func (s *StoreToPVFetcher) GetPersistentVolumeInfo(id string) (*api.PersistentVo
return o.(*api.PersistentVolume), nil
}
// StoreToPetSetLister gives a store List and Exists methods. The store must contain only PetSets.
type StoreToPetSetLister struct {
// StoreToStatefulSetLister gives a store List and Exists methods. The store must contain only StatefulSets.
type StoreToStatefulSetLister struct {
Store
}
// Exists checks if the given PetSet exists in the store.
func (s *StoreToPetSetLister) Exists(ps *apps.PetSet) (bool, error) {
// Exists checks if the given StatefulSet exists in the store.
func (s *StoreToStatefulSetLister) Exists(ps *apps.StatefulSet) (bool, error) {
_, exists, err := s.Store.Get(ps)
if err != nil {
return false, err
......@@ -378,35 +378,35 @@ func (s *StoreToPetSetLister) Exists(ps *apps.PetSet) (bool, error) {
return exists, nil
}
// List lists all PetSets in the store.
func (s *StoreToPetSetLister) List() (psList []apps.PetSet, err error) {
// List lists all StatefulSets in the store.
func (s *StoreToStatefulSetLister) List() (psList []apps.StatefulSet, err error) {
for _, ps := range s.Store.List() {
psList = append(psList, *(ps.(*apps.PetSet)))
psList = append(psList, *(ps.(*apps.StatefulSet)))
}
return psList, nil
}
type storePetSetsNamespacer struct {
type storeStatefulSetsNamespacer struct {
store Store
namespace string
}
func (s *StoreToPetSetLister) PetSets(namespace string) storePetSetsNamespacer {
return storePetSetsNamespacer{s.Store, namespace}
func (s *StoreToStatefulSetLister) StatefulSets(namespace string) storeStatefulSetsNamespacer {
return storeStatefulSetsNamespacer{s.Store, namespace}
}
// GetPodPetSets returns a list of PetSets managing a pod. Returns an error only if no matching PetSets are found.
func (s *StoreToPetSetLister) GetPodPetSets(pod *api.Pod) (psList []apps.PetSet, err error) {
// GetPodStatefulSets returns a list of StatefulSets managing a pod. Returns an error only if no matching StatefulSets are found.
func (s *StoreToStatefulSetLister) GetPodStatefulSets(pod *api.Pod) (psList []apps.StatefulSet, err error) {
var selector labels.Selector
var ps apps.PetSet
var ps apps.StatefulSet
if len(pod.Labels) == 0 {
err = fmt.Errorf("no PetSets found for pod %v because it has no labels", pod.Name)
err = fmt.Errorf("no StatefulSets found for pod %v because it has no labels", pod.Name)
return
}
for _, m := range s.Store.List() {
ps = *m.(*apps.PetSet)
ps = *m.(*apps.StatefulSet)
if ps.Namespace != pod.Namespace {
continue
}
......@@ -416,14 +416,14 @@ func (s *StoreToPetSetLister) GetPodPetSets(pod *api.Pod) (psList []apps.PetSet,
return
}
// If a PetSet with a nil or empty selector creeps in, it should match nothing, not everything.
// If a StatefulSet with a nil or empty selector creeps in, it should match nothing, not everything.
if selector.Empty() || !selector.Matches(labels.Set(pod.Labels)) {
continue
}
psList = append(psList, ps)
}
if len(psList) == 0 {
err = fmt.Errorf("could not find PetSet for pod %s in namespace %s with labels: %v", pod.Name, pod.Namespace, pod.Labels)
err = fmt.Errorf("could not find StatefulSet for pod %s in namespace %s with labels: %v", pod.Name, pod.Namespace, pod.Labels)
}
return
}
......
/*
Copyright 2015 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 unversioned
import (
"k8s.io/kubernetes/pkg/apis/apps"
"k8s.io/kubernetes/pkg/client/restclient"
)
type AppsInterface interface {
StatefulSetNamespacer
}
// AppsClient is used to interact with Kubernetes batch features.
type AppsClient struct {
*restclient.RESTClient
}
func (c *AppsClient) StatefulSets(namespace string) StatefulSetInterface {
return newStatefulSet(c, namespace)
}
func NewApps(c *restclient.Config) (*AppsClient, error) {
config := *c
if err := setGroupDefaults(apps.GroupName, &config); err != nil {
return nil, err
}
client, err := restclient.RESTClientFor(&config)
if err != nil {
return nil, err
}
return &AppsClient{client}, nil
}
func NewAppsOrDie(c *restclient.Config) *AppsClient {
client, err := NewApps(c)
if err != nil {
panic(err)
}
return client
}
......@@ -77,11 +77,11 @@ func ReplicaSetHasDesiredReplicas(rsClient extensionsclient.ReplicaSetsGetter, r
}
}
// PetSetHasDesiredPets returns a conditon that checks the number of petset replicas
func PetSetHasDesiredPets(psClient appsclient.PetSetsGetter, petset *apps.PetSet) wait.ConditionFunc {
// StatefulSetHasDesiredPets returns a conditon that checks the number of petset replicas
func StatefulSetHasDesiredPets(psClient appsclient.StatefulSetsGetter, petset *apps.StatefulSet) wait.ConditionFunc {
// TODO: Differentiate between 0 pets and a really quick scale down using generation.
return func() (bool, error) {
ps, err := psClient.PetSets(petset.Namespace).Get(petset.Name)
ps, err := psClient.StatefulSets(petset.Namespace).Get(petset.Name)
if err != nil {
return false, err
}
......
/*
Copyright 2015 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 unversioned
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/apps"
"k8s.io/kubernetes/pkg/watch"
)
// StatefulSetNamespacer has methods to work with StatefulSet resources in a namespace
type StatefulSetNamespacer interface {
StatefulSets(namespace string) StatefulSetInterface
}
// StatefulSetInterface exposes methods to work on StatefulSet resources.
type StatefulSetInterface interface {
List(opts api.ListOptions) (*apps.StatefulSetList, error)
Get(name string) (*apps.StatefulSet, error)
Create(statefulSet *apps.StatefulSet) (*apps.StatefulSet, error)
Update(statefulSet *apps.StatefulSet) (*apps.StatefulSet, error)
Delete(name string, options *api.DeleteOptions) error
Watch(opts api.ListOptions) (watch.Interface, error)
UpdateStatus(statefulSet *apps.StatefulSet) (*apps.StatefulSet, error)
}
// statefulSet implements StatefulSetNamespacer interface
type statefulSet struct {
r *AppsClient
ns string
}
// newStatefulSet returns a statefulSet
func newStatefulSet(c *AppsClient, namespace string) *statefulSet {
return &statefulSet{c, namespace}
}
// List returns a list of statefulSet that match the label and field selectors.
func (c *statefulSet) List(opts api.ListOptions) (result *apps.StatefulSetList, err error) {
result = &apps.StatefulSetList{}
err = c.r.Get().Namespace(c.ns).Resource("statefulsets").VersionedParams(&opts, api.ParameterCodec).Do().Into(result)
return
}
// Get returns information about a particular statefulSet.
func (c *statefulSet) Get(name string) (result *apps.StatefulSet, err error) {
result = &apps.StatefulSet{}
err = c.r.Get().Namespace(c.ns).Resource("statefulsets").Name(name).Do().Into(result)
return
}
// Create creates a new statefulSet.
func (c *statefulSet) Create(statefulSet *apps.StatefulSet) (result *apps.StatefulSet, err error) {
result = &apps.StatefulSet{}
err = c.r.Post().Namespace(c.ns).Resource("statefulsets").Body(statefulSet).Do().Into(result)
return
}
// Update updates an existing statefulSet.
func (c *statefulSet) Update(statefulSet *apps.StatefulSet) (result *apps.StatefulSet, err error) {
result = &apps.StatefulSet{}
err = c.r.Put().Namespace(c.ns).Resource("statefulsets").Name(statefulSet.Name).Body(statefulSet).Do().Into(result)
return
}
// Delete deletes a statefulSet, returns error if one occurs.
func (c *statefulSet) Delete(name string, options *api.DeleteOptions) (err error) {
return c.r.Delete().Namespace(c.ns).Resource("statefulsets").Name(name).Body(options).Do().Error()
}
// Watch returns a watch.Interface that watches the requested statefulSet.
func (c *statefulSet) Watch(opts api.ListOptions) (watch.Interface, error) {
return c.r.Get().
Prefix("watch").
Namespace(c.ns).
Resource("statefulsets").
VersionedParams(&opts, api.ParameterCodec).
Watch()
}
// UpdateStatus takes the name of the statefulSet and the new status. Returns the server's representation of the statefulSet, and an error, if it occurs.
func (c *statefulSet) UpdateStatus(statefulSet *apps.StatefulSet) (result *apps.StatefulSet, err error) {
result = &apps.StatefulSet{}
err = c.r.Put().Namespace(c.ns).Resource("statefulsets").Name(statefulSet.Name).SubResource("status").Body(statefulSet).Do().Into(result)
return
}
/*
Copyright 2016 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 unversioned_test
import (
"testing"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/testapi"
"k8s.io/kubernetes/pkg/apis/apps"
"k8s.io/kubernetes/pkg/client/unversioned/testclient/simple"
)
func getStatefulSetResourceName() string {
return "statefulsets"
}
func TestListStatefulSets(t *testing.T) {
ns := api.NamespaceAll
c := &simple.Client{
Request: simple.Request{
Method: "GET",
Path: testapi.Apps.ResourcePath(getStatefulSetResourceName(), ns, ""),
},
Response: simple.Response{StatusCode: 200,
Body: &apps.StatefulSetList{
Items: []apps.StatefulSet{
{
ObjectMeta: api.ObjectMeta{
Name: "foo",
Labels: map[string]string{
"foo": "bar",
"name": "baz",
},
},
Spec: apps.StatefulSetSpec{
Replicas: 2,
Template: api.PodTemplateSpec{},
},
},
},
},
},
}
receivedRSList, err := c.Setup(t).Apps().StatefulSets(ns).List(api.ListOptions{})
c.Validate(t, receivedRSList, err)
}
func TestGetStatefulSet(t *testing.T) {
ns := api.NamespaceDefault
c := &simple.Client{
Request: simple.Request{Method: "GET", Path: testapi.Apps.ResourcePath(getStatefulSetResourceName(), ns, "foo"), Query: simple.BuildQueryValues(nil)},
Response: simple.Response{
StatusCode: 200,
Body: &apps.StatefulSet{
ObjectMeta: api.ObjectMeta{
Name: "foo",
Labels: map[string]string{
"foo": "bar",
"name": "baz",
},
},
Spec: apps.StatefulSetSpec{
Replicas: 2,
Template: api.PodTemplateSpec{},
},
},
},
}
receivedRS, err := c.Setup(t).Apps().StatefulSets(ns).Get("foo")
c.Validate(t, receivedRS, err)
}
func TestGetStatefulSetWithNoName(t *testing.T) {
ns := api.NamespaceDefault
c := &simple.Client{Error: true}
receivedPod, err := c.Setup(t).Apps().StatefulSets(ns).Get("")
if (err != nil) && (err.Error() != simple.NameRequiredError) {
t.Errorf("Expected error: %v, but got %v", simple.NameRequiredError, err)
}
c.Validate(t, receivedPod, err)
}
func TestUpdateStatefulSet(t *testing.T) {
ns := api.NamespaceDefault
requestRS := &apps.StatefulSet{
ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "1"},
}
c := &simple.Client{
Request: simple.Request{Method: "PUT", Path: testapi.Apps.ResourcePath(getStatefulSetResourceName(), ns, "foo"), Query: simple.BuildQueryValues(nil)},
Response: simple.Response{
StatusCode: 200,
Body: &apps.StatefulSet{
ObjectMeta: api.ObjectMeta{
Name: "foo",
Labels: map[string]string{
"foo": "bar",
"name": "baz",
},
},
Spec: apps.StatefulSetSpec{
Replicas: 2,
Template: api.PodTemplateSpec{},
},
},
},
}
receivedRS, err := c.Setup(t).Apps().StatefulSets(ns).Update(requestRS)
c.Validate(t, receivedRS, err)
}
func TestDeleteStatefulSet(t *testing.T) {
ns := api.NamespaceDefault
c := &simple.Client{
Request: simple.Request{Method: "DELETE", Path: testapi.Apps.ResourcePath(getStatefulSetResourceName(), ns, "foo"), Query: simple.BuildQueryValues(nil)},
Response: simple.Response{StatusCode: 200},
}
err := c.Setup(t).Apps().StatefulSets(ns).Delete("foo", nil)
c.Validate(t, nil, err)
}
func TestCreateStatefulSet(t *testing.T) {
ns := api.NamespaceDefault
requestRS := &apps.StatefulSet{
ObjectMeta: api.ObjectMeta{Name: "foo"},
}
c := &simple.Client{
Request: simple.Request{Method: "POST", Path: testapi.Apps.ResourcePath(getStatefulSetResourceName(), ns, ""), Body: requestRS, Query: simple.BuildQueryValues(nil)},
Response: simple.Response{
StatusCode: 200,
Body: &apps.StatefulSet{
ObjectMeta: api.ObjectMeta{
Name: "foo",
Labels: map[string]string{
"foo": "bar",
"name": "baz",
},
},
Spec: apps.StatefulSetSpec{
Replicas: 2,
Template: api.PodTemplateSpec{},
},
},
},
}
receivedRS, err := c.Setup(t).Apps().StatefulSets(ns).Create(requestRS)
c.Validate(t, receivedRS, err)
}
// TODO: Test Status actions.
/*
Copyright 2016 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 testclient
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/apps"
"k8s.io/kubernetes/pkg/watch"
)
// FakeStatefulSets implements StatefulSetsInterface. Meant to be embedded into a struct to get a default
// implementation. This makes faking out just the method you want to test easier.
type FakeStatefulSets struct {
Fake *FakeApps
Namespace string
}
func (c *FakeStatefulSets) Get(name string) (*apps.StatefulSet, error) {
obj, err := c.Fake.Invokes(NewGetAction("statefulsets", c.Namespace, name), &apps.StatefulSet{})
if obj == nil {
return nil, err
}
return obj.(*apps.StatefulSet), err
}
func (c *FakeStatefulSets) List(opts api.ListOptions) (*apps.StatefulSetList, error) {
obj, err := c.Fake.Invokes(NewListAction("statefulsets", c.Namespace, opts), &apps.StatefulSetList{})
if obj == nil {
return nil, err
}
return obj.(*apps.StatefulSetList), err
}
func (c *FakeStatefulSets) Create(rs *apps.StatefulSet) (*apps.StatefulSet, error) {
obj, err := c.Fake.Invokes(NewCreateAction("statefulsets", c.Namespace, rs), rs)
if obj == nil {
return nil, err
}
return obj.(*apps.StatefulSet), err
}
func (c *FakeStatefulSets) Update(rs *apps.StatefulSet) (*apps.StatefulSet, error) {
obj, err := c.Fake.Invokes(NewUpdateAction("statefulsets", c.Namespace, rs), rs)
if obj == nil {
return nil, err
}
return obj.(*apps.StatefulSet), err
}
func (c *FakeStatefulSets) Delete(name string, options *api.DeleteOptions) error {
_, err := c.Fake.Invokes(NewDeleteAction("statefulsets", c.Namespace, name), &apps.StatefulSet{})
return err
}
func (c *FakeStatefulSets) Watch(opts api.ListOptions) (watch.Interface, error) {
return c.Fake.InvokesWatch(NewWatchAction("statefulsets", c.Namespace, opts))
}
func (c *FakeStatefulSets) UpdateStatus(rs *apps.StatefulSet) (result *apps.StatefulSet, err error) {
obj, err := c.Fake.Invokes(NewUpdateSubresourceAction("statefulsets", "status", c.Namespace, rs), rs)
if obj == nil {
return nil, err
}
return obj.(*apps.StatefulSet), err
}
......@@ -57,9 +57,9 @@ const (
// An annotation on the Service denoting if the endpoints controller should
// go ahead and create endpoints for unready pods. This annotation is
// currently only used by PetSets, where we need the pet to be DNS
// currently only used by StatefulSets, where we need the pod to be DNS
// resolvable during initialization. In this situation we create a headless
// service just for the PetSet, and clients shouldn't be using this Service
// service just for the StatefulSet, and clients shouldn't be using this Service
// for anything so unready endpoints don't matter.
TolerateUnreadyEndpointsAnnotation = "service.alpha.kubernetes.io/tolerate-unready-endpoints"
)
......
......@@ -51,7 +51,7 @@ func newPVC(name string) api.PersistentVolumeClaim {
}
}
func newPetSetWithVolumes(replicas int, name string, petMounts []api.VolumeMount, podMounts []api.VolumeMount) *apps.PetSet {
func newStatefulSetWithVolumes(replicas int, name string, petMounts []api.VolumeMount, podMounts []api.VolumeMount) *apps.StatefulSet {
mounts := append(petMounts, podMounts...)
claims := []api.PersistentVolumeClaim{}
for _, m := range petMounts {
......@@ -70,9 +70,9 @@ func newPetSetWithVolumes(replicas int, name string, petMounts []api.VolumeMount
})
}
return &apps.PetSet{
return &apps.StatefulSet{
TypeMeta: unversioned.TypeMeta{
Kind: "PetSet",
Kind: "StatefulSet",
APIVersion: "apps/v1beta1",
},
ObjectMeta: api.ObjectMeta{
......@@ -80,7 +80,7 @@ func newPetSetWithVolumes(replicas int, name string, petMounts []api.VolumeMount
Namespace: api.NamespaceDefault,
UID: types.UID("test"),
},
Spec: apps.PetSetSpec{
Spec: apps.StatefulSetSpec{
Selector: &unversioned.LabelSelector{
MatchLabels: map[string]string{"foo": "bar"},
},
......@@ -110,7 +110,7 @@ func runningPod(ns, name string) *api.Pod {
return p
}
func newPodList(ps *apps.PetSet, num int) []*api.Pod {
func newPodList(ps *apps.StatefulSet, num int) []*api.Pod {
// knownPods are pods in the system
knownPods := []*api.Pod{}
for i := 0; i < num; i++ {
......@@ -120,14 +120,14 @@ func newPodList(ps *apps.PetSet, num int) []*api.Pod {
return knownPods
}
func newPetSet(replicas int) *apps.PetSet {
func newStatefulSet(replicas int) *apps.StatefulSet {
petMounts := []api.VolumeMount{
{Name: "datadir", MountPath: "/tmp/zookeeper"},
}
podMounts := []api.VolumeMount{
{Name: "home", MountPath: "/home"},
}
return newPetSetWithVolumes(replicas, "foo", petMounts, podMounts)
return newStatefulSetWithVolumes(replicas, "foo", petMounts, podMounts)
}
func checkPodForMount(pod *api.Pod, mountName string) error {
......@@ -168,14 +168,14 @@ func (f *fakePetClient) Delete(p *pcb) error {
for i, pet := range f.pets {
if p.pod.Name == pet.pod.Name {
found = true
f.recorder.Eventf(pet.parent, api.EventTypeNormal, "SuccessfulDelete", "pet: %v", pet.pod.Name)
f.recorder.Eventf(pet.parent, api.EventTypeNormal, "SuccessfulDelete", "pod: %v", pet.pod.Name)
continue
}
pets = append(pets, f.pets[i])
}
if !found {
// TODO: Return proper not found error
return fmt.Errorf("Delete failed: pet %v doesn't exist", p.pod.Name)
return fmt.Errorf("Delete failed: pod %v doesn't exist", p.pod.Name)
}
f.pets = pets
f.petsDeleted++
......@@ -196,10 +196,10 @@ func (f *fakePetClient) Get(p *pcb) (*pcb, bool, error) {
func (f *fakePetClient) Create(p *pcb) error {
for _, pet := range f.pets {
if p.pod.Name == pet.pod.Name {
return fmt.Errorf("Create failed: pet %v already exists", p.pod.Name)
return fmt.Errorf("Create failed: pod %v already exists", p.pod.Name)
}
}
f.recorder.Eventf(p.parent, api.EventTypeNormal, "SuccessfulCreate", "pet: %v", p.pod.Name)
f.recorder.Eventf(p.parent, api.EventTypeNormal, "SuccessfulCreate", "pod: %v", p.pod.Name)
f.pets = append(f.pets, p)
f.petsCreated++
return nil
......@@ -220,7 +220,7 @@ func (f *fakePetClient) Update(expected, wanted *pcb) error {
}
f.pets = pets
if !found {
return fmt.Errorf("Cannot update pet %v not found", wanted.pod.Name)
return fmt.Errorf("Cannot update pod %v not found", wanted.pod.Name)
}
// TODO: Delete pvcs/volumes that are in wanted but not in expected.
return nil
......@@ -252,7 +252,7 @@ func (f *fakePetClient) setHealthy(index int) error {
return fmt.Errorf("Index out of range, len %v index %v", len(f.pets), index)
}
f.pets[index].pod.Status.Phase = api.PodRunning
f.pets[index].pod.Annotations[PetSetInitAnnotation] = "true"
f.pets[index].pod.Annotations[StatefulSetInitAnnotation] = "true"
f.pets[index].pod.Status.Conditions = []api.PodCondition{
{Type: api.PodReady, Status: api.ConditionTrue},
}
......
......@@ -30,7 +30,7 @@ import (
)
// identityMapper is an interface for assigning identities to a pet.
// All existing identity mappers just append "-(index)" to the petset name to
// All existing identity mappers just append "-(index)" to the statefulset name to
// generate a unique identity. This is used in claims/DNS/hostname/petname
// etc. There's a more elegant way to achieve this mapping, but we're
// taking the simplest route till we have data on whether users will need
......@@ -39,15 +39,15 @@ import (
// your pet a unique identity. You must run them all. Order doesn't matter.
type identityMapper interface {
// SetIdentity takes an id and assigns the given pet an identity based
// on the pet set spec. The is must be unique amongst members of the
// pet set.
// on the stateful set spec. The is must be unique amongst members of the
// stateful set.
SetIdentity(id string, pet *api.Pod)
// Identity returns the identity of the pet.
Identity(pod *api.Pod) string
}
func newIdentityMappers(ps *apps.PetSet) []identityMapper {
func newIdentityMappers(ps *apps.StatefulSet) []identityMapper {
return []identityMapper{
&NameIdentityMapper{ps},
&NetworkIdentityMapper{ps},
......@@ -57,7 +57,7 @@ func newIdentityMappers(ps *apps.PetSet) []identityMapper {
// NetworkIdentityMapper assigns network identity to pets.
type NetworkIdentityMapper struct {
ps *apps.PetSet
ps *apps.StatefulSet
}
// SetIdentity sets network identity on the pet.
......@@ -81,7 +81,7 @@ func (n *NetworkIdentityMapper) String(pet *api.Pod) string {
// VolumeIdentityMapper assigns storage identity to pets.
type VolumeIdentityMapper struct {
ps *apps.PetSet
ps *apps.StatefulSet
}
// SetIdentity sets storge identity on the pet.
......@@ -90,16 +90,16 @@ func (v *VolumeIdentityMapper) SetIdentity(id string, pet *api.Pod) {
petClaims := v.GetClaims(id)
// These volumes will all go down with the pod. If a name matches one of
// the claims in the pet set, it gets clobbered.
// the claims in the stateful set, it gets clobbered.
podVolumes := map[string]api.Volume{}
for _, podVol := range pet.Spec.Volumes {
podVolumes[podVol.Name] = podVol
}
// Insert claims for the idempotent petSet volumes
// Insert claims for the idempotent statefulset volumes
for name, claim := range petClaims {
// Volumes on a pet for which there are no associated claims on the
// petset are pod local, and die with the pod.
// statefulset are pod local, and die with the pod.
podVol, ok := podVolumes[name]
if ok {
// TODO: Validate and reject this.
......@@ -143,7 +143,7 @@ func (v *VolumeIdentityMapper) String(pet *api.Pod) string {
}
for _, podVol := range pet.Spec.Volumes {
// Volumes on a pet for which there are no associated claims on the
// petset are pod local, and die with the pod.
// statefulset are pod local, and die with the pod.
if !petVols.Has(podVol.Name) {
continue
}
......@@ -159,7 +159,7 @@ func (v *VolumeIdentityMapper) String(pet *api.Pod) string {
}
// GetClaims returns the volume claims associated with the given id.
// The claims belong to the petset. The id should be unique within a petset.
// The claims belong to the statefulset. The id should be unique within a statefulset.
func (v *VolumeIdentityMapper) GetClaims(id string) map[string]api.PersistentVolumeClaim {
petClaims := map[string]api.PersistentVolumeClaim{}
for _, pvc := range v.ps.Spec.VolumeClaimTemplates {
......@@ -192,7 +192,7 @@ func (v *VolumeIdentityMapper) GetClaimsForPet(pet *api.Pod) []api.PersistentVol
// NameIdentityMapper assigns names to pets.
// It also puts the pet in the same namespace as the parent.
type NameIdentityMapper struct {
ps *apps.PetSet
ps *apps.StatefulSet
}
// SetIdentity sets the pet namespace and name.
......@@ -214,7 +214,7 @@ func (n *NameIdentityMapper) String(pet *api.Pod) string {
// identityHash computes a hash of the pet by running all the above identity
// mappers.
func identityHash(ps *apps.PetSet, pet *api.Pod) string {
func identityHash(ps *apps.StatefulSet, pet *api.Pod) string {
id := ""
for _, idMapper := range newIdentityMappers(ps) {
id += idMapper.Identity(pet)
......
......@@ -29,7 +29,7 @@ import (
func TestPetIDName(t *testing.T) {
replicas := 3
ps := newPetSet(replicas)
ps := newStatefulSet(replicas)
for i := 0; i < replicas; i++ {
petName := fmt.Sprintf("%v-%d", ps.Name, i)
pcb, err := newPCB(fmt.Sprintf("%d", i), ps)
......@@ -45,7 +45,7 @@ func TestPetIDName(t *testing.T) {
func TestPetIDDNS(t *testing.T) {
replicas := 3
ps := newPetSet(replicas)
ps := newStatefulSet(replicas)
for i := 0; i < replicas; i++ {
petName := fmt.Sprintf("%v-%d", ps.Name, i)
petSubdomain := ps.Spec.ServiceName
......@@ -65,7 +65,7 @@ func TestPetIDDNS(t *testing.T) {
}
func TestPetIDVolume(t *testing.T) {
replicas := 3
ps := newPetSet(replicas)
ps := newStatefulSet(replicas)
for i := 0; i < replicas; i++ {
pcb, err := newPCB(fmt.Sprintf("%d", i), ps)
if err != nil {
......@@ -99,7 +99,7 @@ func TestPetIDVolume(t *testing.T) {
func TestPetIDVolumeClaims(t *testing.T) {
replicas := 3
ps := newPetSet(replicas)
ps := newStatefulSet(replicas)
for i := 0; i < replicas; i++ {
pcb, err := newPCB(fmt.Sprintf("%v", i), ps)
if err != nil {
......@@ -116,7 +116,7 @@ func TestPetIDVolumeClaims(t *testing.T) {
func TestPetIDCrossAssignment(t *testing.T) {
replicas := 3
ps := newPetSet(replicas)
ps := newStatefulSet(replicas)
nameMapper := &NameIdentityMapper{ps}
volumeMapper := &VolumeIdentityMapper{ps}
......@@ -144,7 +144,7 @@ func TestPetIDCrossAssignment(t *testing.T) {
func TestPetIDReset(t *testing.T) {
replicas := 2
ps := newPetSet(replicas)
ps := newStatefulSet(replicas)
firstPCB, err := newPCB("1", ps)
secondPCB, err := newPCB("2", ps)
if identityHash(ps, firstPCB.pod) == identityHash(ps, secondPCB.pod) {
......
......@@ -27,7 +27,7 @@ import (
)
// newPCB generates a new PCB using the id string as a unique qualifier
func newPCB(id string, ps *apps.PetSet) (*pcb, error) {
func newPCB(id string, ps *apps.StatefulSet) (*pcb, error) {
petPod, err := controller.GetPodFromTemplate(&ps.Spec.Template, ps, nil)
if err != nil {
return nil, err
......@@ -87,7 +87,7 @@ func (pt *petQueue) empty() bool {
}
// NewPetQueue returns a queue for tracking pets
func NewPetQueue(ps *apps.PetSet, podList []*api.Pod) *petQueue {
func NewPetQueue(ps *apps.StatefulSet, podList []*api.Pod) *petQueue {
pt := petQueue{pets: []*pcb{}, idMapper: &NameIdentityMapper{ps}}
// Seed the queue with existing pets. Assume all pets are scheduled for
// deletion, enqueuing a pet will "undelete" it. We always want to delete
......@@ -102,10 +102,10 @@ func NewPetQueue(ps *apps.PetSet, podList []*api.Pod) *petQueue {
return &pt
}
// petsetIterator implements a simple iterator over pets in the given petset.
type petSetIterator struct {
// ps is the petset for this iterator.
ps *apps.PetSet
// statefulsetIterator implements a simple iterator over pets in the given statefulset.
type statefulSetIterator struct {
// ps is the statefulset for this iterator.
ps *apps.StatefulSet
// queue contains the elements to iterate over.
queue *petQueue
// errs is a list because we always want the iterator to drain.
......@@ -115,7 +115,7 @@ type petSetIterator struct {
}
// Next returns true for as long as there are elements in the underlying queue.
func (pi *petSetIterator) Next() bool {
func (pi *statefulSetIterator) Next() bool {
var pet *pcb
var err error
if pi.petCount < pi.ps.Spec.Replicas {
......@@ -133,14 +133,14 @@ func (pi *petSetIterator) Next() bool {
}
// Value dequeues an element from the queue.
func (pi *petSetIterator) Value() *pcb {
func (pi *statefulSetIterator) Value() *pcb {
return pi.queue.dequeue()
}
// NewPetSetIterator returns a new iterator. All pods in the given podList
// NewStatefulSetIterator returns a new iterator. All pods in the given podList
// are used to seed the queue of the iterator.
func NewPetSetIterator(ps *apps.PetSet, podList []*api.Pod) *petSetIterator {
pi := &petSetIterator{
func NewStatefulSetIterator(ps *apps.StatefulSet, podList []*api.Pod) *statefulSetIterator {
pi := &statefulSetIterator{
ps: ps,
queue: NewPetQueue(ps, podList),
errs: []error{},
......
......@@ -27,7 +27,7 @@ import (
func TestPetQueueCreates(t *testing.T) {
replicas := 3
ps := newPetSet(replicas)
ps := newStatefulSet(replicas)
q := NewPetQueue(ps, []*api.Pod{})
for i := 0; i < replicas; i++ {
pet, _ := newPCB(fmt.Sprintf("%v", i), ps)
......@@ -38,13 +38,13 @@ func TestPetQueueCreates(t *testing.T) {
}
}
if q.dequeue() != nil {
t.Errorf("Expected no pets")
t.Errorf("Expected no pods")
}
}
func TestPetQueueScaleDown(t *testing.T) {
replicas := 1
ps := newPetSet(replicas)
ps := newStatefulSet(replicas)
// knownPods are the pods in the system
knownPods := newPodList(ps, 3)
......@@ -74,13 +74,13 @@ func TestPetQueueScaleDown(t *testing.T) {
}
}
if q.dequeue() != nil {
t.Errorf("Expected no pets")
t.Errorf("Expected no pods")
}
}
func TestPetQueueScaleUp(t *testing.T) {
replicas := 5
ps := newPetSet(replicas)
ps := newStatefulSet(replicas)
// knownPods are pods in the system
knownPods := newPodList(ps, 2)
......@@ -94,14 +94,14 @@ func TestPetQueueScaleUp(t *testing.T) {
pet := q.dequeue()
expectedName := fmt.Sprintf("%v-%d", ps.Name, i)
if pet.event != syncPet || pet.pod.Name != expectedName {
t.Errorf("Unexpected pet %+v, expected %v", pet.pod.Name, expectedName)
t.Errorf("Unexpected pod %+v, expected %v", pet.pod.Name, expectedName)
}
}
}
func TestPetSetIteratorRelist(t *testing.T) {
func TestStatefulSetIteratorRelist(t *testing.T) {
replicas := 5
ps := newPetSet(replicas)
ps := newStatefulSet(replicas)
// knownPods are pods in the system
knownPods := newPodList(ps, 5)
......@@ -109,7 +109,7 @@ func TestPetSetIteratorRelist(t *testing.T) {
knownPods[i].Spec.NodeName = fmt.Sprintf("foo-node-%v", i)
knownPods[i].Status.Phase = api.PodRunning
}
pi := NewPetSetIterator(ps, knownPods)
pi := NewStatefulSetIterator(ps, knownPods)
// A simple resync should not change identity of pods in the system
i := 0
......@@ -124,12 +124,12 @@ func TestPetSetIteratorRelist(t *testing.T) {
i++
}
if i != 5 {
t.Errorf("Unexpected iterations %v, this probably means too many/few pets", i)
t.Errorf("Unexpected iterations %v, this probably means too many/few pods", i)
}
// Scale to 0 should delete all pods in system
ps.Spec.Replicas = 0
pi = NewPetSetIterator(ps, knownPods)
pi = NewStatefulSetIterator(ps, knownPods)
i = 0
for pi.Next() {
p := pi.Value()
......@@ -139,11 +139,11 @@ func TestPetSetIteratorRelist(t *testing.T) {
i++
}
if i != 5 {
t.Errorf("Unexpected iterations %v, this probably means too many/few pets", i)
t.Errorf("Unexpected iterations %v, this probably means too many/few pods", i)
}
// Relist with 0 replicas should no-op
pi = NewPetSetIterator(ps, []*api.Pod{})
pi = NewStatefulSetIterator(ps, []*api.Pod{})
if pi.Next() != false {
t.Errorf("Unexpected iteration without any replicas or pods in system")
}
......
......@@ -41,10 +41,10 @@ const (
// updateRetries is the number of Get/Update cycles we perform when an
// update fails.
updateRetries = 3
// PetSetInitAnnotation is an annotation which when set, indicates that the
// StatefulSetInitAnnotation is an annotation which when set, indicates that the
// pet has finished initializing itself.
// TODO: Replace this with init container status.
PetSetInitAnnotation = "pod.alpha.kubernetes.io/initialized"
StatefulSetInitAnnotation = "pod.alpha.kubernetes.io/initialized"
)
// pcb is the control block used to transmit all updates about a single pet.
......@@ -59,8 +59,8 @@ type pcb struct {
event petLifeCycleEvent
// id is the identity index of this pet.
id string
// parent is a pointer to the parent petset.
parent *apps.PetSet
// parent is a pointer to the parent statefulset.
parent *apps.StatefulSet
}
// pvcClient is a client for managing persistent volume claims.
......@@ -113,12 +113,12 @@ func (p *petSyncer) Sync(pet *pcb) error {
}
} else if exists {
if !p.isHealthy(realPet.pod) {
glog.Infof("PetSet %v waiting on unhealthy pet %v", pet.parent.Name, realPet.pod.Name)
glog.Infof("StatefulSet %v waiting on unhealthy pet %v", pet.parent.Name, realPet.pod.Name)
}
return p.Update(realPet, pet)
}
if p.blockingPet != nil {
message := errUnhealthyPet(fmt.Sprintf("Create of %v in PetSet %v blocked by unhealthy pet %v", pet.pod.Name, pet.parent.Name, p.blockingPet.pod.Name))
message := errUnhealthyPet(fmt.Sprintf("Create of %v in StatefulSet %v blocked by unhealthy pet %v", pet.pod.Name, pet.parent.Name, p.blockingPet.pod.Name))
glog.Info(message)
return message
}
......@@ -135,7 +135,7 @@ func (p *petSyncer) Sync(pet *pcb) error {
return nil
}
// Delete deletes the given pet, if no other pet in the petset is blocking a
// Delete deletes the given pet, if no other pet in the statefulset is blocking a
// scale event.
func (p *petSyncer) Delete(pet *pcb) error {
if pet == nil {
......@@ -149,17 +149,17 @@ func (p *petSyncer) Delete(pet *pcb) error {
return nil
}
if p.blockingPet != nil {
glog.Infof("Delete of %v in PetSet %v blocked by unhealthy pet %v", realPet.pod.Name, pet.parent.Name, p.blockingPet.pod.Name)
glog.Infof("Delete of %v in StatefulSet %v blocked by unhealthy pet %v", realPet.pod.Name, pet.parent.Name, p.blockingPet.pod.Name)
return nil
}
// This is counted as a delete, even if it fails.
// The returned error will force a requeue.
p.blockingPet = realPet
if !p.isDying(realPet.pod) {
glog.Infof("PetSet %v deleting pet %v", pet.parent.Name, pet.pod.Name)
glog.Infof("StatefulSet %v deleting pet %v", pet.parent.Name, pet.pod.Name)
return p.petClient.Delete(pet)
}
glog.Infof("PetSet %v waiting on pet %v to die in %v", pet.parent.Name, realPet.pod.Name, realPet.pod.DeletionTimestamp)
glog.Infof("StatefulSet %v waiting on pet %v to die in %v", pet.parent.Name, realPet.pod.Name, realPet.pod.DeletionTimestamp)
return nil
}
......@@ -173,7 +173,7 @@ type petClient interface {
Update(*pcb, *pcb) error
}
// apiServerPetClient is a petset aware Kubernetes client.
// apiServerPetClient is a statefulset aware Kubernetes client.
type apiServerPetClient struct {
c internalclientset.Interface
recorder record.EventRecorder
......@@ -223,7 +223,7 @@ func (p *apiServerPetClient) Update(pet *pcb, expectedPet *pcb) (updateErr error
if err != nil || !needsUpdate {
return err
}
glog.Infof("Resetting pet %v/%v to match PetSet %v spec", pet.pod.Namespace, pet.pod.Name, pet.parent.Name)
glog.Infof("Resetting pet %v/%v to match StatefulSet %v spec", pet.pod.Namespace, pet.pod.Name, pet.parent.Name)
_, updateErr = pc.Update(&updatePod)
if updateErr == nil || i >= updateRetries {
return updateErr
......@@ -303,9 +303,9 @@ func (d *defaultPetHealthChecker) isHealthy(pod *api.Pod) bool {
if pod == nil || pod.Status.Phase != api.PodRunning {
return false
}
initialized, ok := pod.Annotations[PetSetInitAnnotation]
initialized, ok := pod.Annotations[StatefulSetInitAnnotation]
if !ok {
glog.Infof("PetSet pod %v in %v, waiting on annotation %v", api.PodRunning, pod.Name, PetSetInitAnnotation)
glog.Infof("StatefulSet pod %v in %v, waiting on annotation %v", api.PodRunning, pod.Name, StatefulSetInitAnnotation)
return false
}
b, err := strconv.ParseBool(initialized)
......
......@@ -29,55 +29,55 @@ import (
"github.com/golang/glog"
)
// overlappingPetSets sorts a list of PetSets by creation timestamp, using their names as a tie breaker.
// Generally used to tie break between PetSets that have overlapping selectors.
type overlappingPetSets []apps.PetSet
// overlappingStatefulSets sorts a list of StatefulSets by creation timestamp, using their names as a tie breaker.
// Generally used to tie break between StatefulSets that have overlapping selectors.
type overlappingStatefulSets []apps.StatefulSet
func (o overlappingPetSets) Len() int { return len(o) }
func (o overlappingPetSets) Swap(i, j int) { o[i], o[j] = o[j], o[i] }
func (o overlappingStatefulSets) Len() int { return len(o) }
func (o overlappingStatefulSets) Swap(i, j int) { o[i], o[j] = o[j], o[i] }
func (o overlappingPetSets) Less(i, j int) bool {
func (o overlappingStatefulSets) Less(i, j int) bool {
if o[i].CreationTimestamp.Equal(o[j].CreationTimestamp) {
return o[i].Name < o[j].Name
}
return o[i].CreationTimestamp.Before(o[j].CreationTimestamp)
}
// updatePetCount attempts to update the Status.Replicas of the given PetSet, with a single GET/PUT retry.
func updatePetCount(psClient appsclientset.PetSetsGetter, ps apps.PetSet, numPets int) (updateErr error) {
// updatePetCount attempts to update the Status.Replicas of the given StatefulSet, with a single GET/PUT retry.
func updatePetCount(psClient appsclientset.StatefulSetsGetter, ps apps.StatefulSet, numPets int) (updateErr error) {
if ps.Status.Replicas == int32(numPets) || psClient == nil {
return nil
}
var getErr error
for i, ps := 0, &ps; ; i++ {
glog.V(4).Infof(fmt.Sprintf("Updating replica count for PetSet: %s/%s, ", ps.Namespace, ps.Name) +
glog.V(4).Infof(fmt.Sprintf("Updating replica count for StatefulSet: %s/%s, ", ps.Namespace, ps.Name) +
fmt.Sprintf("replicas %d->%d (need %d), ", ps.Status.Replicas, numPets, ps.Spec.Replicas))
ps.Status = apps.PetSetStatus{Replicas: int32(numPets)}
_, updateErr = psClient.PetSets(ps.Namespace).UpdateStatus(ps)
ps.Status = apps.StatefulSetStatus{Replicas: int32(numPets)}
_, updateErr = psClient.StatefulSets(ps.Namespace).UpdateStatus(ps)
if updateErr == nil || i >= statusUpdateRetries {
return updateErr
}
if ps, getErr = psClient.PetSets(ps.Namespace).Get(ps.Name); getErr != nil {
if ps, getErr = psClient.StatefulSets(ps.Namespace).Get(ps.Name); getErr != nil {
return getErr
}
}
}
// unhealthyPetTracker tracks unhealthy pets for petsets.
// unhealthyPetTracker tracks unhealthy pets for statefulsets.
type unhealthyPetTracker struct {
pc petClient
store cache.Store
storeLock sync.Mutex
}
// Get returns a previously recorded blocking pet for the given petset.
func (u *unhealthyPetTracker) Get(ps *apps.PetSet, knownPets []*api.Pod) (*pcb, error) {
// Get returns a previously recorded blocking pet for the given statefulset.
func (u *unhealthyPetTracker) Get(ps *apps.StatefulSet, knownPets []*api.Pod) (*pcb, error) {
u.storeLock.Lock()
defer u.storeLock.Unlock()
// We "Get" by key but "Add" by object because the store interface doesn't
// allow us to Get/Add a related obj (eg petset: blocking pet).
// allow us to Get/Add a related obj (eg statefulset: blocking pet).
key, err := controller.KeyFunc(ps)
if err != nil {
return nil, err
......@@ -93,16 +93,16 @@ func (u *unhealthyPetTracker) Get(ps *apps.PetSet, knownPets []*api.Pod) (*pcb,
if !exists {
for _, p := range knownPets {
if hc.isHealthy(p) && !hc.isDying(p) {
glog.V(4).Infof("Ignoring healthy pet %v for PetSet %v", p.Name, ps.Name)
glog.V(4).Infof("Ignoring healthy pod %v for StatefulSet %v", p.Name, ps.Name)
continue
}
glog.Infof("No recorded blocking pet, but found unhealthy pet %v for PetSet %v", p.Name, ps.Name)
glog.Infof("No recorded blocking pod, but found unhealthy pod %v for StatefulSet %v", p.Name, ps.Name)
return &pcb{pod: p, parent: ps}, nil
}
return nil, nil
}
// This is a pet that's blocking further creates/deletes of a petset. If it
// This is a pet that's blocking further creates/deletes of a statefulset. If it
// disappears, it's no longer blocking. If it exists, it continues to block
// till it turns healthy or disappears.
bp := obj.(*pcb)
......@@ -111,12 +111,12 @@ func (u *unhealthyPetTracker) Get(ps *apps.PetSet, knownPets []*api.Pod) (*pcb,
return nil, err
}
if !exists {
glog.V(4).Infof("Clearing blocking pet %v for PetSet %v because it's been deleted", bp.pod.Name, ps.Name)
glog.V(4).Infof("Clearing blocking pod %v for StatefulSet %v because it's been deleted", bp.pod.Name, ps.Name)
return nil, nil
}
blockingPetPod := blockingPet.pod
if hc.isHealthy(blockingPetPod) && !hc.isDying(blockingPetPod) {
glog.V(4).Infof("Clearing blocking pet %v for PetSet %v because it's healthy", bp.pod.Name, ps.Name)
glog.V(4).Infof("Clearing blocking pod %v for StatefulSet %v because it's healthy", bp.pod.Name, ps.Name)
u.store.Delete(blockingPet)
blockingPet = nil
}
......@@ -131,11 +131,11 @@ func (u *unhealthyPetTracker) Add(blockingPet *pcb) error {
if blockingPet == nil {
return nil
}
glog.V(4).Infof("Adding blocking pet %v for PetSet %v", blockingPet.pod.Name, blockingPet.parent.Name)
glog.V(4).Infof("Adding blocking pod %v for StatefulSet %v", blockingPet.pod.Name, blockingPet.parent.Name)
return u.store.Add(blockingPet)
}
// newUnHealthyPetTracker tracks unhealthy pets that block progress of petsets.
// newUnHealthyPetTracker tracks unhealthy pets that block progress of statefulsets.
func newUnHealthyPetTracker(pc petClient) *unhealthyPetTracker {
return &unhealthyPetTracker{pc: pc, store: cache.NewStore(pcbKeyFunc)}
}
......@@ -148,10 +148,10 @@ func pcbKeyFunc(obj interface{}) (string, error) {
}
p, ok := obj.(*pcb)
if !ok {
return "", fmt.Errorf("not a valid pet control block %#v", p)
return "", fmt.Errorf("not a valid pod control block %#v", p)
}
if p.parent == nil {
return "", fmt.Errorf("cannot compute pet control block key without parent pointer %#v", p)
return "", fmt.Errorf("cannot compute pod control block key without parent pointer %#v", p)
}
return controller.KeyFunc(p.parent)
}
......@@ -183,7 +183,7 @@ __custom_func() {
* limitranges (aka 'limits')
* nodes (aka 'no')
* namespaces (aka 'ns')
* petsets (alpha feature, may be unstable)
* statefulsets (alpha feature, may be unstable)
* pods (aka 'po')
* persistentvolumes (aka 'pv')
* persistentvolumeclaims (aka 'pvc')
......
......@@ -863,7 +863,7 @@ func (f *factory) UpdatePodSpecForObject(obj runtime.Object, fn func(*api.PodSpe
return true, fn(&t.Spec.Template.Spec)
case *extensions.ReplicaSet:
return true, fn(&t.Spec.Template.Spec)
case *apps.PetSet:
case *apps.StatefulSet:
return true, fn(&t.Spec.Template.Spec)
case *batch.Job:
return true, fn(&t.Spec.Template.Spec)
......
......@@ -736,12 +736,12 @@ func TestDiscoveryReplaceAliases(t *testing.T) {
{
name: "all-replacement",
arg: "all",
expected: "pods,replicationcontrollers,services,petsets,horizontalpodautoscalers,jobs,deployments,replicasets",
expected: "pods,replicationcontrollers,services,statefulsets,horizontalpodautoscalers,jobs,deployments,replicasets",
},
{
name: "alias-in-comma-separated-arg",
arg: "all,secrets",
expected: "pods,replicationcontrollers,services,petsets,horizontalpodautoscalers,jobs,deployments,replicasets,secrets",
expected: "pods,replicationcontrollers,services,statefulsets,horizontalpodautoscalers,jobs,deployments,replicasets,secrets",
},
}
......
......@@ -111,7 +111,7 @@ var userResources = []unversioned.GroupResource{
{Group: "", Resource: "pods"},
{Group: "", Resource: "replicationcontrollers"},
{Group: "", Resource: "services"},
{Group: "apps", Resource: "petsets"},
{Group: "apps", Resource: "statefulsets"},
{Group: "autoscaling", Resource: "horizontalpodautoscalers"},
{Group: "extensions", Resource: "jobs"},
{Group: "extensions", Resource: "deployments"},
......
......@@ -37,12 +37,12 @@ func TestReplaceAliases(t *testing.T) {
{
name: "all-replacement",
arg: "all",
expected: "pods,replicationcontrollers,services,petsets,horizontalpodautoscalers,jobs,deployments,replicasets",
expected: "pods,replicationcontrollers,services,statefulsets,horizontalpodautoscalers,jobs,deployments,replicasets",
},
{
name: "alias-in-comma-separated-arg",
arg: "all,secrets",
expected: "pods,replicationcontrollers,services,petsets,horizontalpodautoscalers,jobs,deployments,replicasets,secrets",
expected: "pods,replicationcontrollers,services,statefulsets,horizontalpodautoscalers,jobs,deployments,replicasets,secrets",
},
}
......
......@@ -117,7 +117,7 @@ func describerMap(c clientset.Interface) map[unversioned.GroupKind]Describer {
extensions.Kind("Ingress"): &IngressDescriber{c},
batch.Kind("Job"): &JobDescriber{c},
batch.Kind("ScheduledJob"): &ScheduledJobDescriber{c},
apps.Kind("PetSet"): &PetSetDescriber{c},
apps.Kind("StatefulSet"): &StatefulSetDescriber{c},
certificates.Kind("CertificateSigningRequest"): &CertificateSigningRequestDescriber{c},
storage.Kind("StorageClass"): &StorageClassDescriber{c},
}
......@@ -1863,12 +1863,12 @@ func describeNode(node *api.Node, nodeNonTerminatedPodsList *api.PodList, events
})
}
type PetSetDescriber struct {
type StatefulSetDescriber struct {
client clientset.Interface
}
func (p *PetSetDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
ps, err := p.client.Apps().PetSets(namespace).Get(name)
func (p *StatefulSetDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
ps, err := p.client.Apps().StatefulSets(namespace).Get(name)
if err != nil {
return "", err
}
......
......@@ -470,7 +470,7 @@ var (
scheduledJobColumns = []string{"NAME", "SCHEDULE", "SUSPEND", "ACTIVE", "LAST-SCHEDULE"}
serviceColumns = []string{"NAME", "CLUSTER-IP", "EXTERNAL-IP", "PORT(S)", "AGE"}
ingressColumns = []string{"NAME", "HOSTS", "ADDRESS", "PORTS", "AGE"}
petSetColumns = []string{"NAME", "DESIRED", "CURRENT", "AGE"}
statefulSetColumns = []string{"NAME", "DESIRED", "CURRENT", "AGE"}
endpointColumns = []string{"NAME", "ENDPOINTS", "AGE"}
nodeColumns = []string{"NAME", "STATUS", "AGE"}
daemonSetColumns = []string{"NAME", "DESIRED", "CURRENT", "READY", "NODE-SELECTOR", "AGE"}
......@@ -539,8 +539,8 @@ func (h *HumanReadablePrinter) addDefaultHandlers() {
h.Handler(serviceColumns, printServiceList)
h.Handler(ingressColumns, printIngress)
h.Handler(ingressColumns, printIngressList)
h.Handler(petSetColumns, printPetSet)
h.Handler(petSetColumns, printPetSetList)
h.Handler(statefulSetColumns, printStatefulSet)
h.Handler(statefulSetColumns, printStatefulSetList)
h.Handler(endpointColumns, printEndpoints)
h.Handler(endpointColumns, printEndpointsList)
h.Handler(nodeColumns, printNode)
......@@ -1227,7 +1227,7 @@ func printIngressList(ingressList *extensions.IngressList, w io.Writer, options
return nil
}
func printPetSet(ps *apps.PetSet, w io.Writer, options PrintOptions) error {
func printStatefulSet(ps *apps.StatefulSet, w io.Writer, options PrintOptions) error {
name := formatResourceName(options.Kind, ps.Name, options.WithKind)
namespace := ps.Namespace
......@@ -1266,9 +1266,9 @@ func printPetSet(ps *apps.PetSet, w io.Writer, options PrintOptions) error {
return nil
}
func printPetSetList(petSetList *apps.PetSetList, w io.Writer, options PrintOptions) error {
for _, ps := range petSetList.Items {
if err := printPetSet(&ps, w, options); err != nil {
func printStatefulSetList(statefulSetList *apps.StatefulSetList, w io.Writer, options PrintOptions) error {
for _, ps := range statefulSetList.Items {
if err := printStatefulSet(&ps, w, options); err != nil {
return err
}
}
......
......@@ -57,8 +57,8 @@ func ScalerFor(kind unversioned.GroupKind, c internalclientset.Interface) (Scale
return &ReplicaSetScaler{c.Extensions()}, nil
case extensions.Kind("Job"), batch.Kind("Job"):
return &JobScaler{c.Batch()}, nil // Either kind of job can be scaled with Batch interface.
case apps.Kind("PetSet"):
return &PetSetScaler{c.Apps()}, nil
case apps.Kind("StatefulSet"):
return &StatefulSetScaler{c.Apps()}, nil
case extensions.Kind("Deployment"):
return &DeploymentScaler{c.Extensions()}, nil
}
......@@ -137,8 +137,8 @@ func ScaleCondition(r Scaler, precondition *ScalePrecondition, namespace, name s
}
}
// ValidatePetSet ensures that the preconditions match. Returns nil if they are valid, an error otherwise.
func (precondition *ScalePrecondition) ValidatePetSet(ps *apps.PetSet) error {
// ValidateStatefulSet ensures that the preconditions match. Returns nil if they are valid, an error otherwise.
func (precondition *ScalePrecondition) ValidateStatefulSet(ps *apps.StatefulSet) error {
if precondition.Size != -1 && int(ps.Spec.Replicas) != precondition.Size {
return PreconditionError{"replicas", strconv.Itoa(precondition.Size), strconv.Itoa(int(ps.Spec.Replicas))}
}
......@@ -328,34 +328,34 @@ func (precondition *ScalePrecondition) ValidateJob(job *batch.Job) error {
return nil
}
type PetSetScaler struct {
c appsclient.PetSetsGetter
type StatefulSetScaler struct {
c appsclient.StatefulSetsGetter
}
// ScaleSimple does a simple one-shot attempt at scaling. It returns the
// resourceVersion of the petset if the update is successful.
func (scaler *PetSetScaler) ScaleSimple(namespace, name string, preconditions *ScalePrecondition, newSize uint) (string, error) {
ps, err := scaler.c.PetSets(namespace).Get(name)
// resourceVersion of the statefulset if the update is successful.
func (scaler *StatefulSetScaler) ScaleSimple(namespace, name string, preconditions *ScalePrecondition, newSize uint) (string, error) {
ps, err := scaler.c.StatefulSets(namespace).Get(name)
if err != nil {
return "", ScaleError{ScaleGetFailure, "Unknown", err}
}
if preconditions != nil {
if err := preconditions.ValidatePetSet(ps); err != nil {
if err := preconditions.ValidateStatefulSet(ps); err != nil {
return "", err
}
}
ps.Spec.Replicas = int32(newSize)
updatedPetSet, err := scaler.c.PetSets(namespace).Update(ps)
updatedStatefulSet, err := scaler.c.StatefulSets(namespace).Update(ps)
if err != nil {
if errors.IsConflict(err) {
return "", ScaleError{ScaleUpdateConflictFailure, ps.ResourceVersion, err}
}
return "", ScaleError{ScaleUpdateFailure, ps.ResourceVersion, err}
}
return updatedPetSet.ResourceVersion, nil
return updatedStatefulSet.ResourceVersion, nil
}
func (scaler *PetSetScaler) Scale(namespace, name string, newSize uint, preconditions *ScalePrecondition, retry, waitForReplicas *RetryParams) error {
func (scaler *StatefulSetScaler) Scale(namespace, name string, newSize uint, preconditions *ScalePrecondition, retry, waitForReplicas *RetryParams) error {
if preconditions == nil {
preconditions = &ScalePrecondition{-1, ""}
}
......@@ -368,11 +368,11 @@ func (scaler *PetSetScaler) Scale(namespace, name string, newSize uint, precondi
return err
}
if waitForReplicas != nil {
job, err := scaler.c.PetSets(namespace).Get(name)
job, err := scaler.c.StatefulSets(namespace).Get(name)
if err != nil {
return err
}
err = wait.Poll(waitForReplicas.Interval, waitForReplicas.Timeout, client.PetSetHasDesiredPets(scaler.c, job))
err = wait.Poll(waitForReplicas.Interval, waitForReplicas.Timeout, client.StatefulSetHasDesiredPets(scaler.c, job))
if err == wait.ErrWaitTimeout {
return fmt.Errorf("timed out waiting for %q to be synced", name)
}
......
......@@ -87,8 +87,8 @@ func ReaperFor(kind unversioned.GroupKind, c internalclientset.Interface) (Reape
case extensions.Kind("Job"), batch.Kind("Job"):
return &JobReaper{c.Batch(), c.Core(), Interval, Timeout}, nil
case apps.Kind("PetSet"):
return &PetSetReaper{c.Apps(), c.Core(), Interval, Timeout}, nil
case apps.Kind("StatefulSet"):
return &StatefulSetReaper{c.Apps(), c.Core(), Interval, Timeout}, nil
case extensions.Kind("Deployment"):
return &DeploymentReaper{c.Extensions(), c.Extensions(), Interval, Timeout}, nil
......@@ -129,8 +129,8 @@ type PodReaper struct {
type ServiceReaper struct {
client coreclient.ServicesGetter
}
type PetSetReaper struct {
client appsclient.PetSetsGetter
type StatefulSetReaper struct {
client appsclient.StatefulSetsGetter
podClient coreclient.PodsGetter
pollInterval, timeout time.Duration
}
......@@ -325,10 +325,10 @@ func (reaper *DaemonSetReaper) Stop(namespace, name string, timeout time.Duratio
return reaper.client.DaemonSets(namespace).Delete(name, nil)
}
func (reaper *PetSetReaper) Stop(namespace, name string, timeout time.Duration, gracePeriod *api.DeleteOptions) error {
petsets := reaper.client.PetSets(namespace)
scaler := &PetSetScaler{reaper.client}
ps, err := petsets.Get(name)
func (reaper *StatefulSetReaper) Stop(namespace, name string, timeout time.Duration, gracePeriod *api.DeleteOptions) error {
statefulsets := reaper.client.StatefulSets(namespace)
scaler := &StatefulSetScaler{reaper.client}
ps, err := statefulsets.Get(name)
if err != nil {
return err
}
......@@ -337,13 +337,13 @@ func (reaper *PetSetReaper) Stop(namespace, name string, timeout time.Duration,
timeout = Timeout + time.Duration(10*numPets)*time.Second
}
retry := NewRetryParams(reaper.pollInterval, reaper.timeout)
waitForPetSet := NewRetryParams(reaper.pollInterval, reaper.timeout)
if err = scaler.Scale(namespace, name, 0, nil, retry, waitForPetSet); err != nil {
waitForStatefulSet := NewRetryParams(reaper.pollInterval, reaper.timeout)
if err = scaler.Scale(namespace, name, 0, nil, retry, waitForStatefulSet); err != nil {
return err
}
// TODO: This shouldn't be needed, see corresponding TODO in PetSetHasDesiredPets.
// PetSet should track generation number.
// TODO: This shouldn't be needed, see corresponding TODO in StatefulSetHasDesiredPets.
// StatefulSet should track generation number.
pods := reaper.podClient.Pods(namespace)
selector, _ := unversioned.LabelSelectorAsSelector(ps.Spec.Selector)
options := api.ListOptions{LabelSelector: selector}
......@@ -365,8 +365,8 @@ func (reaper *PetSetReaper) Stop(namespace, name string, timeout time.Duration,
}
// TODO: Cleanup volumes? We don't want to accidentally delete volumes from
// stop, so just leave this up to the petset.
return petsets.Delete(name, nil)
// stop, so just leave this up to the statefulset.
return statefulsets.Delete(name, nil)
}
func (reaper *JobReaper) Stop(namespace, name string, timeout time.Duration, gracePeriod *api.DeleteOptions) error {
......
......@@ -37,11 +37,11 @@ type REST struct {
func NewREST(opts generic.RESTOptions) (*REST, *StatusREST) {
prefix := "/" + opts.ResourcePrefix
newListFunc := func() runtime.Object { return &appsapi.PetSetList{} }
newListFunc := func() runtime.Object { return &appsapi.StatefulSetList{} }
storageInterface, dFunc := opts.Decorator(
opts.StorageConfig,
cachesize.GetWatchCacheSizeByResource(cachesize.PetSet),
&appsapi.PetSet{},
cachesize.GetWatchCacheSizeByResource(cachesize.StatefulSet),
&appsapi.StatefulSet{},
prefix,
petset.Strategy,
newListFunc,
......@@ -49,27 +49,27 @@ func NewREST(opts generic.RESTOptions) (*REST, *StatusREST) {
)
store := &registry.Store{
NewFunc: func() runtime.Object { return &appsapi.PetSet{} },
NewFunc: func() runtime.Object { return &appsapi.StatefulSet{} },
// NewListFunc returns an object capable of storing results of an etcd list.
NewListFunc: newListFunc,
// Produces a petSet that etcd understands, to the root of the resource
// Produces a statefulSet that etcd understands, to the root of the resource
// by combining the namespace in the context with the given prefix
KeyRootFunc: func(ctx api.Context) string {
return registry.NamespaceKeyRootFunc(ctx, prefix)
},
// Produces a petSet that etcd understands, to the resource by combining
// Produces a statefulSet that etcd understands, to the resource by combining
// the namespace in the context with the given prefix
KeyFunc: func(ctx api.Context, name string) (string, error) {
return registry.NamespaceKeyFunc(ctx, prefix, name)
},
// Retrieve the name field of a replication controller
ObjectNameFunc: func(obj runtime.Object) (string, error) {
return obj.(*appsapi.PetSet).Name, nil
return obj.(*appsapi.StatefulSet).Name, nil
},
// Used to match objects based on labels/fields for list and watch
PredicateFunc: petset.MatchPetSet,
QualifiedResource: appsapi.Resource("petsets"),
PredicateFunc: petset.MatchStatefulSet,
QualifiedResource: appsapi.Resource("statefulsets"),
EnableGarbageCollection: opts.EnableGarbageCollection,
DeleteCollectionWorkers: opts.DeleteCollectionWorkers,
......@@ -88,13 +88,13 @@ func NewREST(opts generic.RESTOptions) (*REST, *StatusREST) {
return &REST{store}, &StatusREST{store: &statusStore}
}
// StatusREST implements the REST endpoint for changing the status of an petSet
// StatusREST implements the REST endpoint for changing the status of an statefulSet
type StatusREST struct {
store *registry.Store
}
func (r *StatusREST) New() runtime.Object {
return &appsapi.PetSet{}
return &appsapi.StatefulSet{}
}
// Get retrieves the object from the storage. It is required to support Patch.
......
......@@ -33,30 +33,30 @@ import (
func newStorage(t *testing.T) (*REST, *StatusREST, *etcdtesting.EtcdTestServer) {
etcdStorage, server := registrytest.NewEtcdStorage(t, apps.GroupName)
restOptions := generic.RESTOptions{StorageConfig: etcdStorage, Decorator: generic.UndecoratedStorage, DeleteCollectionWorkers: 1, ResourcePrefix: "petsets"}
petSetStorage, statusStorage := NewREST(restOptions)
return petSetStorage, statusStorage, server
restOptions := generic.RESTOptions{StorageConfig: etcdStorage, Decorator: generic.UndecoratedStorage, DeleteCollectionWorkers: 1, ResourcePrefix: "statefulsets"}
statefulSetStorage, statusStorage := NewREST(restOptions)
return statefulSetStorage, statusStorage, server
}
// createPetSet is a helper function that returns a PetSet with the updated resource version.
func createPetSet(storage *REST, ps apps.PetSet, t *testing.T) (apps.PetSet, error) {
// createStatefulSet is a helper function that returns a StatefulSet with the updated resource version.
func createStatefulSet(storage *REST, ps apps.StatefulSet, t *testing.T) (apps.StatefulSet, error) {
ctx := api.WithNamespace(api.NewContext(), ps.Namespace)
obj, err := storage.Create(ctx, &ps)
if err != nil {
t.Errorf("Failed to create PetSet, %v", err)
t.Errorf("Failed to create StatefulSet, %v", err)
}
newPS := obj.(*apps.PetSet)
newPS := obj.(*apps.StatefulSet)
return *newPS, nil
}
func validNewPetSet() *apps.PetSet {
return &apps.PetSet{
func validNewStatefulSet() *apps.StatefulSet {
return &apps.StatefulSet{
ObjectMeta: api.ObjectMeta{
Name: "foo",
Namespace: api.NamespaceDefault,
Labels: map[string]string{"a": "b"},
},
Spec: apps.PetSetSpec{
Spec: apps.StatefulSetSpec{
Selector: &unversioned.LabelSelector{MatchLabels: map[string]string{"a": "b"}},
Template: api.PodTemplateSpec{
ObjectMeta: api.ObjectMeta{
......@@ -76,7 +76,7 @@ func validNewPetSet() *apps.PetSet {
},
Replicas: 7,
},
Status: apps.PetSetStatus{},
Status: apps.StatefulSetStatus{},
}
}
......@@ -85,7 +85,7 @@ func TestCreate(t *testing.T) {
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := registrytest.New(t, storage.Store)
ps := validNewPetSet()
ps := validNewStatefulSet()
ps.ObjectMeta = api.ObjectMeta{}
test.TestCreate(
// valid
......@@ -101,17 +101,17 @@ func TestStatusUpdate(t *testing.T) {
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
ctx := api.WithNamespace(api.NewContext(), api.NamespaceDefault)
key := etcdtest.AddPrefix("/petsets/" + api.NamespaceDefault + "/foo")
validPetSet := validNewPetSet()
if err := storage.Storage.Create(ctx, key, validPetSet, nil, 0); err != nil {
key := etcdtest.AddPrefix("/statefulsets/" + api.NamespaceDefault + "/foo")
validStatefulSet := validNewStatefulSet()
if err := storage.Storage.Create(ctx, key, validStatefulSet, nil, 0); err != nil {
t.Fatalf("unexpected error: %v", err)
}
update := apps.PetSet{
ObjectMeta: validPetSet.ObjectMeta,
Spec: apps.PetSetSpec{
update := apps.StatefulSet{
ObjectMeta: validStatefulSet.ObjectMeta,
Spec: apps.StatefulSetSpec{
Replicas: 7,
},
Status: apps.PetSetStatus{
Status: apps.StatefulSetStatus{
Replicas: 7,
},
}
......@@ -124,7 +124,7 @@ func TestStatusUpdate(t *testing.T) {
t.Fatalf("unexpected error: %v", err)
}
ps := obj.(*apps.PetSet)
ps := obj.(*apps.StatefulSet)
if ps.Spec.Replicas != 7 {
t.Errorf("we expected .spec.replicas to not be updated but it was updated to %v", ps.Spec.Replicas)
}
......@@ -138,7 +138,7 @@ func TestGet(t *testing.T) {
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := registrytest.New(t, storage.Store)
test.TestGet(validNewPetSet())
test.TestGet(validNewStatefulSet())
}
func TestList(t *testing.T) {
......@@ -146,7 +146,7 @@ func TestList(t *testing.T) {
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := registrytest.New(t, storage.Store)
test.TestList(validNewPetSet())
test.TestList(validNewStatefulSet())
}
func TestDelete(t *testing.T) {
......@@ -154,7 +154,7 @@ func TestDelete(t *testing.T) {
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := registrytest.New(t, storage.Store)
test.TestDelete(validNewPetSet())
test.TestDelete(validNewStatefulSet())
}
func TestWatch(t *testing.T) {
......@@ -163,7 +163,7 @@ func TestWatch(t *testing.T) {
defer storage.Store.DestroyFunc()
test := registrytest.New(t, storage.Store)
test.TestWatch(
validNewPetSet(),
validNewStatefulSet(),
// matching labels
[]labels.Set{
{"a": "b"},
......
......@@ -31,109 +31,109 @@ import (
"k8s.io/kubernetes/pkg/util/validation/field"
)
// petSetStrategy implements verification logic for Replication PetSets.
type petSetStrategy struct {
// statefulSetStrategy implements verification logic for Replication StatefulSets.
type statefulSetStrategy struct {
runtime.ObjectTyper
api.NameGenerator
}
// Strategy is the default logic that applies when creating and updating Replication PetSet objects.
var Strategy = petSetStrategy{api.Scheme, api.SimpleNameGenerator}
// Strategy is the default logic that applies when creating and updating Replication StatefulSet objects.
var Strategy = statefulSetStrategy{api.Scheme, api.SimpleNameGenerator}
// NamespaceScoped returns true because all PetSet' need to be within a namespace.
func (petSetStrategy) NamespaceScoped() bool {
// NamespaceScoped returns true because all StatefulSet' need to be within a namespace.
func (statefulSetStrategy) NamespaceScoped() bool {
return true
}
// PrepareForCreate clears the status of an PetSet before creation.
func (petSetStrategy) PrepareForCreate(ctx api.Context, obj runtime.Object) {
petSet := obj.(*apps.PetSet)
// PrepareForCreate clears the status of an StatefulSet before creation.
func (statefulSetStrategy) PrepareForCreate(ctx api.Context, obj runtime.Object) {
statefulSet := obj.(*apps.StatefulSet)
// create cannot set status
petSet.Status = apps.PetSetStatus{}
statefulSet.Status = apps.StatefulSetStatus{}
petSet.Generation = 1
statefulSet.Generation = 1
}
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
func (petSetStrategy) PrepareForUpdate(ctx api.Context, obj, old runtime.Object) {
newPetSet := obj.(*apps.PetSet)
oldPetSet := old.(*apps.PetSet)
func (statefulSetStrategy) PrepareForUpdate(ctx api.Context, obj, old runtime.Object) {
newStatefulSet := obj.(*apps.StatefulSet)
oldStatefulSet := old.(*apps.StatefulSet)
// Update is not allowed to set status
newPetSet.Status = oldPetSet.Status
newStatefulSet.Status = oldStatefulSet.Status
// Any changes to the spec increment the generation number, any changes to the
// status should reflect the generation number of the corresponding object.
// See api.ObjectMeta description for more information on Generation.
if !reflect.DeepEqual(oldPetSet.Spec, newPetSet.Spec) {
newPetSet.Generation = oldPetSet.Generation + 1
if !reflect.DeepEqual(oldStatefulSet.Spec, newStatefulSet.Spec) {
newStatefulSet.Generation = oldStatefulSet.Generation + 1
}
}
// Validate validates a new PetSet.
func (petSetStrategy) Validate(ctx api.Context, obj runtime.Object) field.ErrorList {
petSet := obj.(*apps.PetSet)
return validation.ValidatePetSet(petSet)
// Validate validates a new StatefulSet.
func (statefulSetStrategy) Validate(ctx api.Context, obj runtime.Object) field.ErrorList {
statefulSet := obj.(*apps.StatefulSet)
return validation.ValidateStatefulSet(statefulSet)
}
// Canonicalize normalizes the object after validation.
func (petSetStrategy) Canonicalize(obj runtime.Object) {
func (statefulSetStrategy) Canonicalize(obj runtime.Object) {
}
// AllowCreateOnUpdate is false for PetSet; this means POST is needed to create one.
func (petSetStrategy) AllowCreateOnUpdate() bool {
// AllowCreateOnUpdate is false for StatefulSet; this means POST is needed to create one.
func (statefulSetStrategy) AllowCreateOnUpdate() bool {
return false
}
// ValidateUpdate is the default update validation for an end user.
func (petSetStrategy) ValidateUpdate(ctx api.Context, obj, old runtime.Object) field.ErrorList {
validationErrorList := validation.ValidatePetSet(obj.(*apps.PetSet))
updateErrorList := validation.ValidatePetSetUpdate(obj.(*apps.PetSet), old.(*apps.PetSet))
func (statefulSetStrategy) ValidateUpdate(ctx api.Context, obj, old runtime.Object) field.ErrorList {
validationErrorList := validation.ValidateStatefulSet(obj.(*apps.StatefulSet))
updateErrorList := validation.ValidateStatefulSetUpdate(obj.(*apps.StatefulSet), old.(*apps.StatefulSet))
return append(validationErrorList, updateErrorList...)
}
// AllowUnconditionalUpdate is the default update policy for PetSet objects.
func (petSetStrategy) AllowUnconditionalUpdate() bool {
// AllowUnconditionalUpdate is the default update policy for StatefulSet objects.
func (statefulSetStrategy) AllowUnconditionalUpdate() bool {
return true
}
// PetSetToSelectableFields returns a field set that represents the object.
func PetSetToSelectableFields(petSet *apps.PetSet) fields.Set {
return generic.ObjectMetaFieldsSet(&petSet.ObjectMeta, true)
// StatefulSetToSelectableFields returns a field set that represents the object.
func StatefulSetToSelectableFields(statefulSet *apps.StatefulSet) fields.Set {
return generic.ObjectMetaFieldsSet(&statefulSet.ObjectMeta, true)
}
// MatchPetSet is the filter used by the generic etcd backend to watch events
// MatchStatefulSet is the filter used by the generic etcd backend to watch events
// from etcd to clients of the apiserver only interested in specific labels/fields.
func MatchPetSet(label labels.Selector, field fields.Selector) storage.SelectionPredicate {
func MatchStatefulSet(label labels.Selector, field fields.Selector) storage.SelectionPredicate {
return storage.SelectionPredicate{
Label: label,
Field: field,
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) {
petSet, ok := obj.(*apps.PetSet)
statefulSet, ok := obj.(*apps.StatefulSet)
if !ok {
return nil, nil, fmt.Errorf("given object is not an PetSet.")
return nil, nil, fmt.Errorf("given object is not an StatefulSet.")
}
return labels.Set(petSet.ObjectMeta.Labels), PetSetToSelectableFields(petSet), nil
return labels.Set(statefulSet.ObjectMeta.Labels), StatefulSetToSelectableFields(statefulSet), nil
},
}
}
type petSetStatusStrategy struct {
petSetStrategy
type statefulSetStatusStrategy struct {
statefulSetStrategy
}
var StatusStrategy = petSetStatusStrategy{Strategy}
var StatusStrategy = statefulSetStatusStrategy{Strategy}
// PrepareForUpdate clears fields that are not allowed to be set by end users on update of status
func (petSetStatusStrategy) PrepareForUpdate(ctx api.Context, obj, old runtime.Object) {
newPetSet := obj.(*apps.PetSet)
oldPetSet := old.(*apps.PetSet)
func (statefulSetStatusStrategy) PrepareForUpdate(ctx api.Context, obj, old runtime.Object) {
newStatefulSet := obj.(*apps.StatefulSet)
oldStatefulSet := old.(*apps.StatefulSet)
// status changes are not allowed to update spec
newPetSet.Spec = oldPetSet.Spec
newStatefulSet.Spec = oldStatefulSet.Spec
}
// ValidateUpdate is the default update validation for an end user updating status
func (petSetStatusStrategy) ValidateUpdate(ctx api.Context, obj, old runtime.Object) field.ErrorList {
func (statefulSetStatusStrategy) ValidateUpdate(ctx api.Context, obj, old runtime.Object) field.ErrorList {
// TODO: Validate status updates.
return validation.ValidatePetSetStatusUpdate(obj.(*apps.PetSet), old.(*apps.PetSet))
return validation.ValidateStatefulSetStatusUpdate(obj.(*apps.StatefulSet), old.(*apps.StatefulSet))
}
......@@ -24,13 +24,13 @@ import (
"k8s.io/kubernetes/pkg/apis/apps"
)
func TestPetSetStrategy(t *testing.T) {
func TestStatefulSetStrategy(t *testing.T) {
ctx := api.NewDefaultContext()
if !Strategy.NamespaceScoped() {
t.Errorf("PetSet must be namespace scoped")
t.Errorf("StatefulSet must be namespace scoped")
}
if Strategy.AllowCreateOnUpdate() {
t.Errorf("PetSet should not allow create on update")
t.Errorf("StatefulSet should not allow create on update")
}
validSelector := map[string]string{"a": "b"}
......@@ -46,18 +46,18 @@ func TestPetSetStrategy(t *testing.T) {
},
},
}
ps := &apps.PetSet{
ps := &apps.StatefulSet{
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
Spec: apps.PetSetSpec{
Spec: apps.StatefulSetSpec{
Selector: &unversioned.LabelSelector{MatchLabels: validSelector},
Template: validPodTemplate.Template,
},
Status: apps.PetSetStatus{Replicas: 3},
Status: apps.StatefulSetStatus{Replicas: 3},
}
Strategy.PrepareForCreate(ctx, ps)
if ps.Status.Replicas != 0 {
t.Error("PetSet should not allow setting status.pets on create")
t.Error("StatefulSet should not allow setting status.replicas on create")
}
errs := Strategy.Validate(ctx, ps)
if len(errs) != 0 {
......@@ -65,35 +65,35 @@ func TestPetSetStrategy(t *testing.T) {
}
// Just Spec.Replicas is allowed to change
validPs := &apps.PetSet{
validPs := &apps.StatefulSet{
ObjectMeta: api.ObjectMeta{Name: ps.Name, Namespace: ps.Namespace, ResourceVersion: "1", Generation: 1},
Spec: apps.PetSetSpec{
Spec: apps.StatefulSetSpec{
Selector: ps.Spec.Selector,
Template: validPodTemplate.Template,
},
Status: apps.PetSetStatus{Replicas: 4},
Status: apps.StatefulSetStatus{Replicas: 4},
}
Strategy.PrepareForUpdate(ctx, validPs, ps)
errs = Strategy.ValidateUpdate(ctx, validPs, ps)
if len(errs) != 0 {
t.Errorf("Updating spec.Replicas is allowed on a petset: %v", errs)
t.Errorf("Updating spec.Replicas is allowed on a statefulset: %v", errs)
}
validPs.Spec.Selector = &unversioned.LabelSelector{MatchLabels: map[string]string{"a": "bar"}}
Strategy.PrepareForUpdate(ctx, validPs, ps)
errs = Strategy.ValidateUpdate(ctx, validPs, ps)
if len(errs) == 0 {
t.Errorf("Expected a validation error since updates are disallowed on petsets.")
t.Errorf("Expected a validation error since updates are disallowed on statefulsets.")
}
}
func TestPetSetStatusStrategy(t *testing.T) {
func TestStatefulSetStatusStrategy(t *testing.T) {
ctx := api.NewDefaultContext()
if !StatusStrategy.NamespaceScoped() {
t.Errorf("PetSet must be namespace scoped")
t.Errorf("StatefulSet must be namespace scoped")
}
if StatusStrategy.AllowCreateOnUpdate() {
t.Errorf("PetSet should not allow create on update")
t.Errorf("StatefulSet should not allow create on update")
}
validSelector := map[string]string{"a": "b"}
validPodTemplate := api.PodTemplate{
......@@ -108,34 +108,34 @@ func TestPetSetStatusStrategy(t *testing.T) {
},
},
}
oldPS := &apps.PetSet{
oldPS := &apps.StatefulSet{
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault, ResourceVersion: "10"},
Spec: apps.PetSetSpec{
Spec: apps.StatefulSetSpec{
Replicas: 3,
Selector: &unversioned.LabelSelector{MatchLabels: validSelector},
Template: validPodTemplate.Template,
},
Status: apps.PetSetStatus{
Status: apps.StatefulSetStatus{
Replicas: 1,
},
}
newPS := &apps.PetSet{
newPS := &apps.StatefulSet{
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault, ResourceVersion: "9"},
Spec: apps.PetSetSpec{
Spec: apps.StatefulSetSpec{
Replicas: 1,
Selector: &unversioned.LabelSelector{MatchLabels: validSelector},
Template: validPodTemplate.Template,
},
Status: apps.PetSetStatus{
Status: apps.StatefulSetStatus{
Replicas: 2,
},
}
StatusStrategy.PrepareForUpdate(ctx, newPS, oldPS)
if newPS.Status.Replicas != 2 {
t.Errorf("PetSet status updates should allow change of pets: %v", newPS.Status.Replicas)
t.Errorf("StatefulSet status updates should allow change of pods: %v", newPS.Status.Replicas)
}
if newPS.Spec.Replicas != 3 {
t.Errorf("PetSet status updates should not clobber spec: %v", newPS.Spec)
t.Errorf("StatefulSet status updates should not clobber spec: %v", newPS.Spec)
}
errs := StatusStrategy.ValidateUpdate(ctx, newPS, oldPS)
if len(errs) != 0 {
......
......@@ -21,7 +21,7 @@ import (
"k8s.io/kubernetes/pkg/apis/apps"
appsapiv1alpha1 "k8s.io/kubernetes/pkg/apis/apps/v1alpha1"
"k8s.io/kubernetes/pkg/genericapiserver"
petsetetcd "k8s.io/kubernetes/pkg/registry/apps/petset/etcd"
statefulsetetcd "k8s.io/kubernetes/pkg/registry/apps/petset/etcd"
)
type RESTStorageProvider struct{}
......@@ -43,10 +43,10 @@ func (p RESTStorageProvider) v1alpha1Storage(apiResourceConfigSource genericapis
version := appsapiv1alpha1.SchemeGroupVersion
storage := map[string]rest.Storage{}
if apiResourceConfigSource.ResourceEnabled(version.WithResource("petsets")) {
petsetStorage, petsetStatusStorage := petsetetcd.NewREST(restOptionsGetter(apps.Resource("petsets")))
storage["petsets"] = petsetStorage
storage["petsets/status"] = petsetStatusStorage
if apiResourceConfigSource.ResourceEnabled(version.WithResource("statefulsets")) {
statefulsetStorage, statefulsetStatusStorage := statefulsetetcd.NewREST(restOptionsGetter(apps.Resource("statefulsets")))
storage["statefulsets"] = statefulsetStorage
storage["statefulsets/status"] = statefulsetStatusStorage
}
return storage
}
......@@ -39,7 +39,7 @@ const (
HorizontalPodAutoscalers Resource = "horizontalpodautoscalers"
Ingress Resource = "ingress"
PodDisruptionBudget Resource = "poddisruptionbudgets"
PetSet Resource = "petset"
StatefulSet Resource = "statefulset"
Jobs Resource = "jobs"
LimitRanges Resource = "limitranges"
Namespaces Resource = "namespaces"
......
......@@ -274,7 +274,7 @@ func GetPath(mounter Mounter) (string, error) {
// ChooseZone implements our heuristics for choosing a zone for volume creation based on the volume name
// Volumes are generally round-robin-ed across all active zones, using the hash of the PVC Name.
// However, if the PVCName ends with `-<integer>`, we will hash the prefix, and then add the integer to the hash.
// This means that a PetSet's volumes (`claimname-petsetname-id`) will spread across available zones,
// This means that a StatefulSet's volumes (`claimname-statefulsetname-id`) will spread across available zones,
// assuming the id values are consecutive.
func ChooseZoneForVolume(zones sets.String, pvcName string) string {
// We create the volume in a zone determined by the name
......@@ -290,8 +290,8 @@ func ChooseZoneForVolume(zones sets.String, pvcName string) string {
} else {
hashString := pvcName
// Heuristic to make sure that volumes in a PetSet are spread across zones
// PetSet PVCs are (currently) named ClaimName-PetSetName-Id,
// Heuristic to make sure that volumes in a StatefulSet are spread across zones
// StatefulSet PVCs are (currently) named ClaimName-StatefulSetName-Id,
// where Id is an integer index
lastDash := strings.LastIndexByte(pvcName, '-')
if lastDash != -1 {
......@@ -302,7 +302,7 @@ func ChooseZoneForVolume(zones sets.String, pvcName string) string {
index = uint32(petID)
// We still hash the volume name, but only the base
hashString = pvcName[:lastDash]
glog.V(2).Infof("Detected PetSet-style volume name %q; index=%d", pvcName, index)
glog.V(2).Infof("Detected StatefulSet-style volume name %q; index=%d", pvcName, index)
}
}
......@@ -314,7 +314,7 @@ func ChooseZoneForVolume(zones sets.String, pvcName string) string {
// Zones.List returns zones in a consistent order (sorted)
// We do have a potential failure case where volumes will not be properly spread,
// if the set of zones changes during PetSet volume creation. However, this is
// if the set of zones changes during StatefulSet volume creation. However, this is
// probably relatively unlikely because we expect the set of zones to be essentially
// static for clusters.
// Hopefully we can address this problem if/when we do full scheduler integration of
......
......@@ -82,7 +82,7 @@ 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("petsets").RuleOrDie(),
rbac.NewRule(ReadWrite...).Groups(appsGroup).Resources("statefulsets").RuleOrDie(),
rbac.NewRule(ReadWrite...).Groups(autoscalingGroup).Resources("horizontalpodautoscalers").RuleOrDie(),
......@@ -112,7 +112,7 @@ 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("petsets").RuleOrDie(),
rbac.NewRule(ReadWrite...).Groups(appsGroup).Resources("statefulsets").RuleOrDie(),
rbac.NewRule(ReadWrite...).Groups(autoscalingGroup).Resources("horizontalpodautoscalers").RuleOrDie(),
......@@ -135,7 +135,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("petsets").RuleOrDie(),
rbac.NewRule(Read...).Groups(appsGroup).Resources("statefulsets").RuleOrDie(),
rbac.NewRule(Read...).Groups(autoscalingGroup).Resources("horizontalpodautoscalers").RuleOrDie(),
......
......@@ -37,7 +37,7 @@ const (
// GCE instances can have up to 16 PD volumes attached.
DefaultMaxGCEPDVolumes = 16
ClusterAutoscalerProvider = "ClusterAutoscalerProvider"
PetSetKind = "PetSet"
StatefulSetKind = "StatefulSet"
)
// getMaxVols checks the max PD volumes environment variable, otherwise returning a default value
......@@ -239,7 +239,7 @@ func GetEquivalencePod(pod *api.Pod) interface{} {
func isValidControllerKind(kind string) bool {
switch kind {
// list of kinds that we cannot handle
case PetSetKind:
case StatefulSetKind:
return false
default:
return true
......
......@@ -244,8 +244,8 @@ var _ = framework.KubeDescribe("[Feature:Example]", func() {
})
})
framework.KubeDescribe("CassandraPetSet", func() {
It("should create petset", func() {
framework.KubeDescribe("CassandraStatefulSet", func() {
It("should create statefulset", func() {
mkpath := func(file string) string {
return filepath.Join(framework.TestContext.RepoRoot, "examples/storage/cassandra", file)
}
......@@ -258,9 +258,9 @@ var _ = framework.KubeDescribe("[Feature:Example]", func() {
output := strings.Replace(string(input), "cassandra-0.cassandra.default.svc.cluster.local", "cassandra-0.cassandra."+ns+".svc.cluster.local", -1)
petSetYaml := "/tmp/cassandra-petset.yaml"
statefulsetYaml := "/tmp/cassandra-petset.yaml"
err = ioutil.WriteFile(petSetYaml, []byte(output), 0644)
err = ioutil.WriteFile(statefulsetYaml, []byte(output), 0644)
Expect(err).NotTo(HaveOccurred())
By("Starting the cassandra service")
......@@ -269,21 +269,21 @@ var _ = framework.KubeDescribe("[Feature:Example]", func() {
err = framework.WaitForService(c, ns, "cassandra", true, framework.Poll, framework.ServiceRespondingTimeout)
Expect(err).NotTo(HaveOccurred())
// Create an PetSet with n nodes in it. Each node will then be verified.
By("Creating a Cassandra PetSet")
// Create an StatefulSet with n nodes in it. Each node will then be verified.
By("Creating a Cassandra StatefulSet")
framework.RunKubectlOrDie("create", "-f", petSetYaml, nsFlag)
framework.RunKubectlOrDie("create", "-f", statefulsetYaml, nsFlag)
petsetPoll := 30 * time.Second
petsetTimeout := 10 * time.Minute
statefulsetPoll := 30 * time.Second
statefulsetTimeout := 10 * time.Minute
// TODO - parse this number out of the yaml
numPets := 3
label := labels.SelectorFromSet(labels.Set(map[string]string{"app": "cassandra"}))
err = wait.PollImmediate(petsetPoll, petsetTimeout,
err = wait.PollImmediate(statefulsetPoll, statefulsetTimeout,
func() (bool, error) {
podList, err := c.Core().Pods(ns).List(api.ListOptions{LabelSelector: label})
if err != nil {
return false, fmt.Errorf("Unable to get list of pods in petset %s", label)
return false, fmt.Errorf("Unable to get list of pods in statefulset %s", label)
}
ExpectNoError(err)
if len(podList.Items) < numPets {
......@@ -312,8 +312,8 @@ var _ = framework.KubeDescribe("[Feature:Example]", func() {
framework.Failf("Cassandra pod ip %s is not reporting Up and Normal 'UN' via nodetool status", pod.Status.PodIP)
}
})
// using out of petset e2e as deleting pvc is a pain
deleteAllPetSets(c, ns)
// using out of statefulset e2e as deleting pvc is a pain
deleteAllStatefulSets(c, ns)
})
})
......
......@@ -110,7 +110,7 @@ func verifyExpectedRcsExistAndGetExpectedPods(c clientset.Interface) ([]string,
if err != nil {
return nil, err
}
psList, err := c.Apps().PetSets(api.NamespaceSystem).List(options)
psList, err := c.Apps().StatefulSets(api.NamespaceSystem).List(options)
if err != nil {
return nil, err
}
......
apiVersion: apps/v1alpha1
kind: PetSet
kind: StatefulSet
metadata:
name: mysql
spec:
......
apiVersion: apps/v1alpha1
kind: PetSet
kind: StatefulSet
metadata:
name: rd
spec:
......
apiVersion: apps/v1alpha1
kind: PetSet
kind: StatefulSet
metadata:
name: zoo
spec:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment