Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
K
k3s
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Jacklull
k3s
Commits
f6a186b1
Commit
f6a186b1
authored
Feb 14, 2017
by
Andy Goldstein
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Switch statefulset controller to shared informers
parent
eef16cf1
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
174 additions
and
135 deletions
+174
-135
apps.go
cmd/kube-controller-manager/app/apps.go
+2
-3
BUILD
pkg/controller/statefulset/BUILD
+12
-4
stateful_pod_control.go
pkg/controller/statefulset/stateful_pod_control.go
+62
-57
stateful_pod_control_test.go
pkg/controller/statefulset/stateful_pod_control_test.go
+0
-0
stateful_set.go
pkg/controller/statefulset/stateful_set.go
+48
-45
stateful_set_control.go
pkg/controller/statefulset/stateful_set_control.go
+23
-4
stateful_set_control_test.go
pkg/controller/statefulset/stateful_set_control_test.go
+0
-0
stateful_set_test.go
pkg/controller/statefulset/stateful_set_test.go
+16
-10
stateful_set_utils.go
pkg/controller/statefulset/stateful_set_utils.go
+1
-1
stateful_set_utils_test.go
pkg/controller/statefulset/stateful_set_utils_test.go
+3
-3
examples.go
test/e2e/examples.go
+3
-4
BUILD
test/e2e/framework/BUILD
+1
-0
statefulset_utils.go
test/e2e/framework/statefulset_utils.go
+3
-4
No files found.
cmd/kube-controller-manager/app/apps.go
View file @
f6a186b1
...
...
@@ -29,11 +29,10 @@ func startStatefulSetController(ctx ControllerContext) (bool, error) {
if
!
ctx
.
AvailableResources
[
schema
.
GroupVersionResource
{
Group
:
"apps"
,
Version
:
"v1beta1"
,
Resource
:
"statefulsets"
}]
{
return
false
,
nil
}
resyncPeriod
:=
ResyncPeriod
(
&
ctx
.
Options
)()
go
statefulset
.
NewStatefulSetController
(
ctx
.
InformerFactory
.
Pods
()
.
Informer
(),
ctx
.
NewInformerFactory
.
Core
()
.
V1
()
.
Pods
(),
ctx
.
NewInformerFactory
.
Apps
()
.
V1beta1
()
.
StatefulSets
(),
ctx
.
ClientBuilder
.
ClientOrDie
(
"statefulset-controller"
),
resyncPeriod
,
)
.
Run
(
1
,
ctx
.
Stop
)
return
true
,
nil
}
pkg/controller/statefulset/BUILD
View file @
f6a186b1
...
...
@@ -23,17 +23,20 @@ go_library(
"//pkg/api/v1/pod:go_default_library",
"//pkg/apis/apps/v1beta1:go_default_library",
"//pkg/client/clientset_generated/clientset:go_default_library",
"//pkg/client/legacylisters:go_default_library",
"//pkg/client/informers/informers_generated/externalversions/apps/v1beta1:go_default_library",
"//pkg/client/informers/informers_generated/externalversions/core/v1:go_default_library",
"//pkg/client/listers/apps/v1beta1:go_default_library",
"//pkg/client/listers/core/v1:go_default_library",
"//pkg/client/retry:go_default_library",
"//pkg/controller:go_default_library",
"//vendor:github.com/golang/glog",
"//vendor:k8s.io/apimachinery/pkg/api/errors",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apimachinery/pkg/util/errors",
"//vendor:k8s.io/apimachinery/pkg/util/runtime",
"//vendor:k8s.io/apimachinery/pkg/util/wait",
"//vendor:k8s.io/apimachinery/pkg/watch",
"//vendor:k8s.io/client-go/kubernetes/typed/core/v1",
"//vendor:k8s.io/client-go/pkg/api",
"//vendor:k8s.io/client-go/pkg/api/v1",
"//vendor:k8s.io/client-go/tools/cache",
"//vendor:k8s.io/client-go/tools/record",
...
...
@@ -56,17 +59,22 @@ go_test(
"//pkg/api/v1/pod:go_default_library",
"//pkg/apis/apps/v1beta1:go_default_library",
"//pkg/client/clientset_generated/clientset/fake:go_default_library",
"//pkg/client/informers/informers_generated/externalversions:go_default_library",
"//pkg/client/informers/informers_generated/externalversions/apps/v1beta1:go_default_library",
"//pkg/client/informers/informers_generated/externalversions/core/v1:go_default_library",
"//pkg/client/legacylisters:go_default_library",
"//pkg/client/listers/apps/v1beta1:go_default_library",
"//pkg/client/listers/core/v1:go_default_library",
"//pkg/controller:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/api/errors",
"//vendor:k8s.io/apimachinery/pkg/api/resource",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/labels",
"//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apimachinery/pkg/types",
"//vendor:k8s.io/client-go/testing",
"//vendor:k8s.io/client-go/tools/cache",
"//vendor:k8s.io/client-go/tools/record",
"//vendor:k8s.io/client-go/util/workqueue",
],
)
...
...
pkg/controller/statefulset/stateful_pod_control.go
View file @
f6a186b1
...
...
@@ -23,11 +23,15 @@ import (
apierrors
"k8s.io/apimachinery/pkg/api/errors"
metav1
"k8s.io/apimachinery/pkg/apis/meta/v1"
errorutils
"k8s.io/apimachinery/pkg/util/errors"
utilruntime
"k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/client-go/pkg/api"
"k8s.io/client-go/tools/record"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/v1"
apps
"k8s.io/kubernetes/pkg/apis/apps/v1beta1"
"k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
appslisters
"k8s.io/kubernetes/pkg/client/listers/apps/v1beta1"
corelisters
"k8s.io/kubernetes/pkg/client/listers/core/v1"
"k8s.io/kubernetes/pkg/client/retry"
)
// StatefulPodControlInterface defines the interface that StatefulSetController uses to create, update, and delete Pods,
...
...
@@ -52,15 +56,17 @@ type StatefulPodControlInterface interface {
UpdateStatefulSetReplicas
(
set
*
apps
.
StatefulSet
,
replicas
int32
)
error
}
func
NewRealStatefulPodControl
(
client
clientset
.
Interface
,
recorder
record
.
EventRecorder
)
StatefulPodControlInterface
{
return
&
realStatefulPodControl
{
client
,
recorder
}
func
NewRealStatefulPodControl
(
client
clientset
.
Interface
,
setLister
appslisters
.
StatefulSetLister
,
podLister
corelisters
.
PodLister
,
recorder
record
.
EventRecorder
)
StatefulPodControlInterface
{
return
&
realStatefulPodControl
{
client
,
setLister
,
podLister
,
recorder
}
}
// realStatefulPodControl implements StatefulPodControlInterface using a clientset.Interface to communicate with the
// API server. The struct is package private as the internal details are irrelevant to importing packages.
type
realStatefulPodControl
struct
{
client
clientset
.
Interface
recorder
record
.
EventRecorder
client
clientset
.
Interface
setLister
appslisters
.
StatefulSetLister
podLister
corelisters
.
PodLister
recorder
record
.
EventRecorder
}
func
(
spc
*
realStatefulPodControl
)
CreateStatefulPod
(
set
*
apps
.
StatefulSet
,
pod
*
v1
.
Pod
)
error
{
...
...
@@ -80,54 +86,56 @@ func (spc *realStatefulPodControl) CreateStatefulPod(set *apps.StatefulSet, pod
}
func
(
spc
*
realStatefulPodControl
)
UpdateStatefulPod
(
set
*
apps
.
StatefulSet
,
pod
*
v1
.
Pod
)
error
{
// we make a copy of the Pod on the stack and mutate the copy
// we copy back to pod to notify the caller of successful mutation
obj
,
err
:=
api
.
Scheme
.
Copy
(
pod
)
if
err
!=
nil
{
return
fmt
.
Errorf
(
"unable to copy pod: %v"
,
err
)
}
podCopy
:=
obj
.
(
*
v1
.
Pod
)
for
attempt
:=
0
;
attempt
<
maxUpdateRetries
;
attempt
++
{
attemptedUpdate
:=
false
err
:=
retry
.
RetryOnConflict
(
retry
.
DefaultBackoff
,
func
()
error
{
// assume the Pod is consistent
consistent
:=
true
// if the Pod does not conform to it
'
s identity, update the identity and dirty the Pod
if
!
identityMatches
(
set
,
pod
Copy
)
{
updateIdentity
(
set
,
pod
Copy
)
// if the Pod does not conform to its identity, update the identity and dirty the Pod
if
!
identityMatches
(
set
,
pod
)
{
updateIdentity
(
set
,
pod
)
consistent
=
false
}
// if the Pod does not conform to the StatefulSet's storage requirements, update the Pod's PVC's,
// dirty the Pod, and create any missing PVCs
if
!
storageMatches
(
set
,
pod
Copy
)
{
updateStorage
(
set
,
pod
Copy
)
if
!
storageMatches
(
set
,
pod
)
{
updateStorage
(
set
,
pod
)
consistent
=
false
if
err
:=
spc
.
createPersistentVolumeClaims
(
set
,
pod
Copy
);
err
!=
nil
{
if
err
:=
spc
.
createPersistentVolumeClaims
(
set
,
pod
);
err
!=
nil
{
spc
.
recordPodEvent
(
"update"
,
set
,
pod
,
err
)
return
err
}
}
// if the Pod is not dirty do nothing
if
consistent
{
*
pod
=
*
podCopy
return
nil
}
attemptedUpdate
=
true
// commit the update, retrying on conflicts
_
,
err
=
spc
.
client
.
Core
()
.
Pods
(
set
.
Namespace
)
.
Update
(
podCopy
)
if
!
apierrors
.
IsConflict
(
err
)
{
if
err
==
nil
{
*
pod
=
*
podCopy
}
spc
.
recordPodEvent
(
"update"
,
set
,
pod
,
err
)
return
err
_
,
err
:=
spc
.
client
.
Core
()
.
Pods
(
set
.
Namespace
)
.
Update
(
pod
)
if
err
==
nil
{
return
nil
}
conflicting
,
err
:=
spc
.
client
.
Core
()
.
Pods
(
set
.
Namespace
)
.
Get
(
podCopy
.
Name
,
metav1
.
GetOptions
{})
if
err
!=
nil
{
spc
.
recordPodEvent
(
"update"
,
set
,
podCopy
,
err
)
return
err
updateErr
:=
err
if
updated
,
err
:=
spc
.
podLister
.
Pods
(
set
.
Namespace
)
.
Get
(
pod
.
Name
);
err
==
nil
{
// make a copy so we don't mutate the shared cache
if
copy
,
err
:=
api
.
Scheme
.
DeepCopy
(
updated
);
err
==
nil
{
pod
=
copy
.
(
*
v1
.
Pod
)
}
else
{
utilruntime
.
HandleError
(
fmt
.
Errorf
(
"error copying updated Pod: %v"
,
err
))
}
}
else
{
utilruntime
.
HandleError
(
fmt
.
Errorf
(
"error getting updated Pod %s/%s from lister: %v"
,
set
.
Namespace
,
pod
.
Name
,
err
))
}
*
podCopy
=
*
conflicting
return
updateErr
})
if
attemptedUpdate
{
spc
.
recordPodEvent
(
"update"
,
set
,
pod
,
err
)
}
spc
.
recordPodEvent
(
"update"
,
set
,
pod
,
updateConflictError
)
return
updateConflictError
return
err
}
func
(
spc
*
realStatefulPodControl
)
DeleteStatefulPod
(
set
*
apps
.
StatefulSet
,
pod
*
v1
.
Pod
)
error
{
...
...
@@ -137,31 +145,28 @@ func (spc *realStatefulPodControl) DeleteStatefulPod(set *apps.StatefulSet, pod
}
func
(
spc
*
realStatefulPodControl
)
UpdateStatefulSetReplicas
(
set
*
apps
.
StatefulSet
,
replicas
int32
)
error
{
if
set
.
Status
.
Replicas
==
replicas
{
return
nil
}
obj
,
err
:=
api
.
Scheme
.
Copy
(
set
)
if
err
!=
nil
{
return
fmt
.
Errorf
(
"unable to copy set: %v"
,
err
)
}
setCopy
:=
obj
.
(
*
apps
.
StatefulSet
)
setCopy
.
Status
.
Replicas
=
replicas
for
attempt
:=
0
;
attempt
<
maxUpdateRetries
;
attempt
++
{
_
,
err
:=
spc
.
client
.
Apps
()
.
StatefulSets
(
setCopy
.
Namespace
)
.
UpdateStatus
(
setCopy
)
if
!
apierrors
.
IsConflict
(
err
)
{
if
err
==
nil
{
*
set
=
*
setCopy
}
return
err
return
retry
.
RetryOnConflict
(
retry
.
DefaultBackoff
,
func
()
error
{
set
.
Status
.
Replicas
=
replicas
_
,
err
:=
spc
.
client
.
Apps
()
.
StatefulSets
(
set
.
Namespace
)
.
UpdateStatus
(
set
)
if
err
==
nil
{
return
nil
}
conflicting
,
err
:=
spc
.
client
.
Apps
()
.
StatefulSets
(
setCopy
.
Namespace
)
.
Get
(
setCopy
.
Name
,
metav1
.
GetOptions
{})
if
err
!=
nil
{
return
err
updateErr
:=
err
if
updated
,
err
:=
spc
.
setLister
.
StatefulSets
(
set
.
Namespace
)
.
Get
(
set
.
Name
);
err
==
nil
{
// make a copy so we don't mutate the shared cache
if
copy
,
err
:=
api
.
Scheme
.
DeepCopy
(
updated
);
err
==
nil
{
set
=
copy
.
(
*
apps
.
StatefulSet
)
}
else
{
utilruntime
.
HandleError
(
fmt
.
Errorf
(
"error copying updated StatefulSet: %v"
,
err
))
}
}
else
{
utilruntime
.
HandleError
(
fmt
.
Errorf
(
"error getting updated StatefulSet %s/%s from lister: %v"
,
set
.
Namespace
,
set
.
Name
,
err
))
}
conflicting
.
Status
.
Replicas
=
setCopy
.
Status
.
Replicas
*
setCopy
=
*
conflicting
}
return
updateConflictError
return
updateErr
})
}
// recordPodEvent records an event for verb applied to a Pod in a StatefulSet. If err is nil the generated event will
...
...
pkg/controller/statefulset/stateful_pod_control_test.go
View file @
f6a186b1
This diff is collapsed.
Click to expand it.
pkg/controller/statefulset/stateful_set.go
View file @
f6a186b1
...
...
@@ -22,23 +22,23 @@ import (
"sort"
"time"
"k8s.io/apimachinery/pkg/api/errors"
metav1
"k8s.io/apimachinery/pkg/apis/meta/v1"
utilruntime
"k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/wait"
v1core
"k8s.io/client-go/kubernetes/typed/core/v1"
clientv1
"k8s.io/client-go/pkg/api/v1"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/tools/record"
"k8s.io/client-go/util/workqueue"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/v1"
apps
"k8s.io/kubernetes/pkg/apis/apps/v1beta1"
"k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
"k8s.io/kubernetes/pkg/client/legacylisters"
"k8s.io/apimachinery/pkg/runtime"
utilruntime
"k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/util/workqueue"
appsinformers
"k8s.io/kubernetes/pkg/client/informers/informers_generated/externalversions/apps/v1beta1"
coreinformers
"k8s.io/kubernetes/pkg/client/informers/informers_generated/externalversions/core/v1"
appslisters
"k8s.io/kubernetes/pkg/client/listers/apps/v1beta1"
corelisters
"k8s.io/kubernetes/pkg/client/listers/core/v1"
"k8s.io/kubernetes/pkg/controller"
"github.com/golang/glog"
...
...
@@ -56,20 +56,24 @@ type StatefulSetController struct {
// control returns an interface capable of syncing a stateful set.
// Abstracted out for testing.
control
StatefulSetControlInterface
// pod
Store is a cache of watched pods.
pod
Store
listers
.
StoreTo
PodLister
// pod
StoreSynced returns true if the pod store has synced at least once.
pod
Store
Synced
cache
.
InformerSynced
//
A store of StatefulSets, populated by setController.
set
Store
listers
.
StoreTo
StatefulSetLister
//
Watches changes to all StatefulSets.
set
Controller
cache
.
Controller
// pod
Lister is able to list/get pods from a shared informer's store
pod
Lister
corelisters
.
PodLister
// pod
ListerSynced returns true if the pod shared informer has synced at least once
pod
Lister
Synced
cache
.
InformerSynced
//
setLister is able to list/get stateful sets from a shared informer's store
set
Lister
appslisters
.
StatefulSetLister
//
setListerSynced returns true if the stateful set shared informer has synced at least once
set
ListerSynced
cache
.
InformerSynced
// StatefulSets that need to be synced.
queue
workqueue
.
RateLimitingInterface
}
// NewStatefulSetController creates a new statefulset controller.
func
NewStatefulSetController
(
podInformer
cache
.
SharedIndexInformer
,
kubeClient
clientset
.
Interface
,
resyncPeriod
time
.
Duration
)
*
StatefulSetController
{
func
NewStatefulSetController
(
podInformer
coreinformers
.
PodInformer
,
setInformer
appsinformers
.
StatefulSetInformer
,
kubeClient
clientset
.
Interface
,
)
*
StatefulSetController
{
eventBroadcaster
:=
record
.
NewBroadcaster
()
eventBroadcaster
.
StartLogging
(
glog
.
Infof
)
eventBroadcaster
.
StartRecordingToSink
(
&
v1core
.
EventSinkImpl
{
Interface
:
v1core
.
New
(
kubeClient
.
Core
()
.
RESTClient
())
.
Events
(
""
)})
...
...
@@ -77,11 +81,11 @@ func NewStatefulSetController(podInformer cache.SharedIndexInformer, kubeClient
ssc
:=
&
StatefulSetController
{
kubeClient
:
kubeClient
,
control
:
NewDefaultStatefulSetControl
(
NewRealStatefulPodControl
(
kubeClient
,
recorder
)),
control
:
NewDefaultStatefulSetControl
(
NewRealStatefulPodControl
(
kubeClient
,
setInformer
.
Lister
(),
podInformer
.
Lister
(),
recorder
)),
queue
:
workqueue
.
NewNamedRateLimitingQueue
(
workqueue
.
DefaultControllerRateLimiter
(),
"statefulset"
),
}
podInformer
.
AddEventHandler
(
cache
.
ResourceEventHandlerFuncs
{
podInformer
.
Informer
()
.
AddEventHandler
(
cache
.
ResourceEventHandlerFuncs
{
// lookup the statefulset and enqueue
AddFunc
:
ssc
.
addPod
,
// lookup current and old statefulset if labels changed
...
...
@@ -89,19 +93,10 @@ func NewStatefulSetController(podInformer cache.SharedIndexInformer, kubeClient
// lookup statefulset accounting for deletion tombstones
DeleteFunc
:
ssc
.
deletePod
,
})
ssc
.
podStore
.
Indexer
=
podInformer
.
GetIndexer
()
ssc
.
podLister
=
podInformer
.
Lister
()
ssc
.
podListerSynced
=
podInformer
.
Informer
()
.
HasSynced
ssc
.
setStore
.
Store
,
ssc
.
setController
=
cache
.
NewInformer
(
&
cache
.
ListWatch
{
ListFunc
:
func
(
options
metav1
.
ListOptions
)
(
runtime
.
Object
,
error
)
{
return
ssc
.
kubeClient
.
Apps
()
.
StatefulSets
(
v1
.
NamespaceAll
)
.
List
(
options
)
},
WatchFunc
:
func
(
options
metav1
.
ListOptions
)
(
watch
.
Interface
,
error
)
{
return
ssc
.
kubeClient
.
Apps
()
.
StatefulSets
(
v1
.
NamespaceAll
)
.
Watch
(
options
)
},
},
&
apps
.
StatefulSet
{},
statefulSetResyncPeriod
,
setInformer
.
Informer
()
.
AddEventHandlerWithResyncPeriod
(
cache
.
ResourceEventHandlerFuncs
{
AddFunc
:
ssc
.
enqueueStatefulSet
,
UpdateFunc
:
func
(
old
,
cur
interface
{})
{
...
...
@@ -114,9 +109,12 @@ func NewStatefulSetController(podInformer cache.SharedIndexInformer, kubeClient
},
DeleteFunc
:
ssc
.
enqueueStatefulSet
,
},
statefulSetResyncPeriod
,
)
ssc
.
setLister
=
setInformer
.
Lister
()
ssc
.
setListerSynced
=
setInformer
.
Informer
()
.
HasSynced
// TODO: Watch volumes
ssc
.
podStoreSynced
=
podInformer
.
GetController
()
.
HasSynced
return
ssc
}
...
...
@@ -124,18 +122,20 @@ func NewStatefulSetController(podInformer cache.SharedIndexInformer, kubeClient
func
(
ssc
*
StatefulSetController
)
Run
(
workers
int
,
stopCh
<-
chan
struct
{})
{
defer
utilruntime
.
HandleCrash
()
defer
ssc
.
queue
.
ShutDown
()
glog
.
Infof
(
"Starting statefulset controller"
)
if
!
cache
.
WaitForCacheSync
(
stopCh
,
ssc
.
podStoreSynced
)
{
if
!
cache
.
WaitForCacheSync
(
stopCh
,
ssc
.
podListerSynced
,
ssc
.
setListerSynced
)
{
utilruntime
.
HandleError
(
fmt
.
Errorf
(
"timed out waiting for caches to sync"
))
return
}
go
ssc
.
setController
.
Run
(
stopCh
)
for
i
:=
0
;
i
<
workers
;
i
++
{
go
wait
.
Until
(
ssc
.
worker
,
time
.
Second
,
stopCh
)
}
<-
stopCh
glog
.
Infof
(
"Shutting down statefulset controller"
)
}
// addPod adds the statefulset for the pod to the sync queue
...
...
@@ -204,12 +204,12 @@ func (ssc *StatefulSetController) getPodsForStatefulSet(set *apps.StatefulSet) (
if
err
!=
nil
{
return
[]
*
v1
.
Pod
{},
err
}
return
ssc
.
pod
Store
.
Pods
(
set
.
Namespace
)
.
List
(
sel
)
return
ssc
.
pod
Lister
.
Pods
(
set
.
Namespace
)
.
List
(
sel
)
}
// getStatefulSetForPod returns the StatefulSet managing the given pod.
func
(
ssc
*
StatefulSetController
)
getStatefulSetForPod
(
pod
*
v1
.
Pod
)
*
apps
.
StatefulSet
{
sets
,
err
:=
ssc
.
set
Store
.
GetPodStatefulSets
(
pod
)
sets
,
err
:=
ssc
.
set
Lister
.
GetPodStatefulSets
(
pod
)
if
err
!=
nil
{
glog
.
V
(
4
)
.
Infof
(
"No StatefulSets found for pod %v, StatefulSet controller will avoid syncing"
,
pod
.
Name
)
return
nil
...
...
@@ -225,14 +225,14 @@ func (ssc *StatefulSetController) getStatefulSetForPod(pod *v1.Pod) *apps.Statef
sort
.
Sort
(
overlappingStatefulSets
(
sets
))
// return the first created set for which pod is a member
for
i
:=
range
sets
{
if
isMemberOf
(
&
sets
[
i
],
pod
)
{
return
&
sets
[
i
]
if
isMemberOf
(
sets
[
i
],
pod
)
{
return
sets
[
i
]
}
}
glog
.
V
(
4
)
.
Infof
(
"No StatefulSets found for pod %v, StatefulSet controller will avoid syncing"
,
pod
.
Name
)
return
nil
}
return
&
sets
[
0
]
return
sets
[
0
]
}
...
...
@@ -276,8 +276,12 @@ func (ssc *StatefulSetController) sync(key string) error {
glog
.
V
(
4
)
.
Infof
(
"Finished syncing statefulset %q (%v)"
,
key
,
time
.
Now
()
.
Sub
(
startTime
))
}()
obj
,
exists
,
err
:=
ssc
.
setStore
.
Store
.
GetByKey
(
key
)
if
!
exists
{
namespace
,
name
,
err
:=
cache
.
SplitMetaNamespaceKey
(
key
)
if
err
!=
nil
{
return
err
}
set
,
err
:=
ssc
.
setLister
.
StatefulSets
(
namespace
)
.
Get
(
name
)
if
errors
.
IsNotFound
(
err
)
{
glog
.
Infof
(
"StatefulSet has been deleted %v"
,
key
)
return
nil
}
...
...
@@ -286,13 +290,12 @@ func (ssc *StatefulSetController) sync(key string) error {
return
err
}
set
:=
*
obj
.
(
*
apps
.
StatefulSet
)
pods
,
err
:=
ssc
.
getPodsForStatefulSet
(
&
set
)
pods
,
err
:=
ssc
.
getPodsForStatefulSet
(
set
)
if
err
!=
nil
{
return
err
}
return
ssc
.
syncStatefulSet
(
&
set
,
pods
)
return
ssc
.
syncStatefulSet
(
set
,
pods
)
}
// syncStatefulSet syncs a tuple of (statefulset, []*v1.Pod).
...
...
pkg/controller/statefulset/stateful_set_control.go
View file @
f6a186b1
...
...
@@ -17,8 +17,10 @@ limitations under the License.
package
statefulset
import
(
"fmt"
"sort"
"k8s.io/client-go/pkg/api"
"k8s.io/kubernetes/pkg/api/v1"
apps
"k8s.io/kubernetes/pkg/apis/apps/v1beta1"
...
...
@@ -97,8 +99,16 @@ func (ssc *defaultStatefulSetControl) UpdateStatefulSet(set *apps.StatefulSet, p
sort
.
Sort
(
ascendingOrdinal
(
condemned
))
// if the current number of replicas has changed update the statefulSets replicas
if
err
:=
ssc
.
podControl
.
UpdateStatefulSetReplicas
(
set
,
int32
(
ready
));
err
!=
nil
{
return
err
if
set
.
Status
.
Replicas
!=
int32
(
ready
)
{
obj
,
err
:=
api
.
Scheme
.
Copy
(
set
)
if
err
!=
nil
{
return
fmt
.
Errorf
(
"unable to copy set: %v"
,
err
)
}
set
=
obj
.
(
*
apps
.
StatefulSet
)
if
err
:=
ssc
.
podControl
.
UpdateStatefulSetReplicas
(
set
,
int32
(
ready
));
err
!=
nil
{
return
err
}
}
// Examine each replica with respect to its ordinal
...
...
@@ -123,8 +133,17 @@ func (ssc *defaultStatefulSetControl) UpdateStatefulSet(set *apps.StatefulSet, p
set
.
Name
,
replicas
[
i
]
.
Name
)
return
nil
}
// Enforce the StatefulSet invariants,
if
err
:=
ssc
.
podControl
.
UpdateStatefulPod
(
set
,
replicas
[
i
]);
err
!=
nil
{
// Enforce the StatefulSet invariants
if
identityMatches
(
set
,
replicas
[
i
])
&&
storageMatches
(
set
,
replicas
[
i
])
{
continue
}
// Make a deep copy so we don't mutate the shared cache
copy
,
err
:=
api
.
Scheme
.
DeepCopy
(
replicas
[
i
])
if
err
!=
nil
{
return
err
}
replica
:=
copy
.
(
*
v1
.
Pod
)
if
err
:=
ssc
.
podControl
.
UpdateStatefulPod
(
set
,
replica
);
err
!=
nil
{
return
err
}
}
...
...
pkg/controller/statefulset/stateful_set_control_test.go
View file @
f6a186b1
This diff is collapsed.
Click to expand it.
pkg/controller/statefulset/stateful_set_test.go
View file @
f6a186b1
...
...
@@ -24,13 +24,16 @@ import (
metav1
"k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/util/workqueue"
"k8s.io/kubernetes/pkg/api/v1"
apps
"k8s.io/kubernetes/pkg/apis/apps/v1beta1"
"k8s.io/kubernetes/pkg/client/clientset_generated/clientset/fake"
informers
"k8s.io/kubernetes/pkg/client/informers/informers_generated/externalversions"
"k8s.io/kubernetes/pkg/controller"
)
func
alwaysReady
()
bool
{
return
true
}
func
TestStatefulSetControllerCreates
(
t
*
testing
.
T
)
{
ssc
,
spc
:=
newFakeStatefulSetController
()
set
:=
newStatefulSet
(
3
)
...
...
@@ -343,15 +346,18 @@ func TestStatefulSetControllerGetStatefulSetForPodOverlapping(t *testing.T) {
}
func
newFakeStatefulSetController
()
(
*
StatefulSetController
,
*
fakeStatefulPodControl
)
{
fpc
:=
newFakeStatefulPodControl
()
ssc
:=
&
StatefulSetController
{
kubeClient
:
nil
,
podStoreSynced
:
func
()
bool
{
return
true
},
setStore
:
fpc
.
setsLister
,
podStore
:
fpc
.
podsLister
,
control
:
NewDefaultStatefulSetControl
(
fpc
),
queue
:
workqueue
.
NewNamedRateLimitingQueue
(
workqueue
.
DefaultControllerRateLimiter
(),
"statefulset"
),
}
client
:=
fake
.
NewSimpleClientset
()
informerFactory
:=
informers
.
NewSharedInformerFactory
(
client
,
controller
.
NoResyncPeriodFunc
())
fpc
:=
newFakeStatefulPodControl
(
informerFactory
.
Core
()
.
V1
()
.
Pods
(),
informerFactory
.
Apps
()
.
V1beta1
()
.
StatefulSets
())
ssc
:=
NewStatefulSetController
(
informerFactory
.
Core
()
.
V1
()
.
Pods
(),
informerFactory
.
Apps
()
.
V1beta1
()
.
StatefulSets
(),
client
,
)
ssc
.
podListerSynced
=
alwaysReady
ssc
.
setListerSynced
=
alwaysReady
ssc
.
control
=
NewDefaultStatefulSetControl
(
fpc
)
return
ssc
,
fpc
}
...
...
pkg/controller/statefulset/stateful_set_utils.go
View file @
f6a186b1
...
...
@@ -38,7 +38,7 @@ var updateConflictError = fmt.Errorf("aborting update after %d attempts", maxUpd
// 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
type
overlappingStatefulSets
[]
*
apps
.
StatefulSet
func
(
o
overlappingStatefulSets
)
Len
()
int
{
return
len
(
o
)
}
...
...
pkg/controller/statefulset/stateful_set_utils_test.go
View file @
f6a186b1
...
...
@@ -251,10 +251,10 @@ func TestAscendingOrdinal(t *testing.T) {
}
func
TestOverlappingStatefulSets
(
t
*
testing
.
T
)
{
sets
:=
make
([]
apps
.
StatefulSet
,
10
)
sets
:=
make
([]
*
apps
.
StatefulSet
,
10
)
perm
:=
rand
.
Perm
(
10
)
for
i
,
v
:=
range
perm
{
sets
[
i
]
=
*
newStatefulSet
(
10
)
sets
[
i
]
=
newStatefulSet
(
10
)
sets
[
i
]
.
CreationTimestamp
=
metav1
.
NewTime
(
sets
[
i
]
.
CreationTimestamp
.
Add
(
time
.
Duration
(
v
)
*
time
.
Second
))
}
sort
.
Sort
(
overlappingStatefulSets
(
sets
))
...
...
@@ -262,7 +262,7 @@ func TestOverlappingStatefulSets(t *testing.T) {
t
.
Error
(
"ascendingOrdinal fails to sort Pods"
)
}
for
i
,
v
:=
range
perm
{
sets
[
i
]
=
*
newStatefulSet
(
10
)
sets
[
i
]
=
newStatefulSet
(
10
)
sets
[
i
]
.
Name
=
strconv
.
FormatInt
(
int64
(
v
),
10
)
}
sort
.
Sort
(
overlappingStatefulSets
(
sets
))
...
...
test/e2e/examples.go
View file @
f6a186b1
...
...
@@ -264,20 +264,19 @@ var _ = framework.KubeDescribe("[Feature:Example]", 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
)
return
filepath
.
Join
(
"examples/storage/cassandra"
,
file
)
}
serviceYaml
:=
mkpath
(
"cassandra-service.yaml"
)
nsFlag
:=
fmt
.
Sprintf
(
"--namespace=%v"
,
ns
)
// have to change dns prefix because of the dynamic namespace
input
,
err
:=
ioutil
.
ReadFile
(
mkpath
(
"cassandra-statefulset.yaml"
))
Expect
(
err
)
.
NotTo
(
HaveOccurred
())
input
:=
generated
.
ReadOrDie
(
mkpath
(
"cassandra-statefulset.yaml"
))
output
:=
strings
.
Replace
(
string
(
input
),
"cassandra-0.cassandra.default.svc.cluster.local"
,
"cassandra-0.cassandra."
+
ns
+
".svc.cluster.local"
,
-
1
)
statefulsetYaml
:=
"/tmp/cassandra-statefulset.yaml"
err
=
ioutil
.
WriteFile
(
statefulsetYaml
,
[]
byte
(
output
),
0644
)
err
:
=
ioutil
.
WriteFile
(
statefulsetYaml
,
[]
byte
(
output
),
0644
)
Expect
(
err
)
.
NotTo
(
HaveOccurred
())
By
(
"Starting the cassandra service"
)
...
...
test/e2e/framework/BUILD
View file @
f6a186b1
...
...
@@ -71,6 +71,7 @@ go_library(
"//pkg/util/version:go_default_library",
"//plugin/pkg/scheduler/algorithm/predicates:go_default_library",
"//plugin/pkg/scheduler/schedulercache:go_default_library",
"//test/e2e/generated:go_default_library",
"//test/e2e/perftype:go_default_library",
"//test/utils:go_default_library",
"//vendor:github.com/golang/glog",
...
...
test/e2e/framework/statefulset_utils.go
View file @
f6a186b1
...
...
@@ -18,7 +18,6 @@ package framework
import
(
"fmt"
"io/ioutil"
"path/filepath"
"strconv"
"strings"
...
...
@@ -38,6 +37,7 @@ import (
"k8s.io/kubernetes/pkg/api/v1"
apps
"k8s.io/kubernetes/pkg/apis/apps/v1beta1"
"k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
"k8s.io/kubernetes/test/e2e/generated"
)
const
(
...
...
@@ -70,8 +70,7 @@ func CreateStatefulSetService(name string, labels map[string]string) *v1.Service
func
StatefulSetFromManifest
(
fileName
,
ns
string
)
*
apps
.
StatefulSet
{
var
ss
apps
.
StatefulSet
Logf
(
"Parsing statefulset from %v"
,
fileName
)
data
,
err
:=
ioutil
.
ReadFile
(
fileName
)
Expect
(
err
)
.
NotTo
(
HaveOccurred
())
data
:=
generated
.
ReadOrDie
(
fileName
)
json
,
err
:=
utilyaml
.
ToJSON
(
data
)
Expect
(
err
)
.
NotTo
(
HaveOccurred
())
...
...
@@ -99,7 +98,7 @@ func NewStatefulSetTester(c clientset.Interface) *StatefulSetTester {
// CreateStatefulSet creates a StatefulSet from the manifest at manifestPath in the Namespace ns using kubectl create.
func
(
s
*
StatefulSetTester
)
CreateStatefulSet
(
manifestPath
,
ns
string
)
*
apps
.
StatefulSet
{
mkpath
:=
func
(
file
string
)
string
{
return
filepath
.
Join
(
TestContext
.
RepoRoot
,
manifestPath
,
file
)
return
filepath
.
Join
(
manifestPath
,
file
)
}
ss
:=
StatefulSetFromManifest
(
mkpath
(
"statefulset.yaml"
),
ns
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment