1 Star 0 Fork 0

zhuchance/kubernetes

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
controller.go 20.41 KB
一键复制 编辑 原始数据 按行查看 历史
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585
/*
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 sync
import (
"fmt"
"time"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
pkgruntime "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/apimachinery/pkg/watch"
clientv1 "k8s.io/client-go/pkg/api/v1"
restclient "k8s.io/client-go/rest"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/tools/record"
"k8s.io/client-go/util/flowcontrol"
"k8s.io/client-go/util/workqueue"
federationapi "k8s.io/kubernetes/federation/apis/federation/v1beta1"
federationclientset "k8s.io/kubernetes/federation/client/clientset_generated/federation_clientset"
"k8s.io/kubernetes/federation/pkg/federatedtypes"
"k8s.io/kubernetes/federation/pkg/federation-controller/util"
"k8s.io/kubernetes/federation/pkg/federation-controller/util/clusterselector"
"k8s.io/kubernetes/federation/pkg/federation-controller/util/deletionhelper"
"k8s.io/kubernetes/federation/pkg/federation-controller/util/eventsink"
"k8s.io/kubernetes/pkg/api"
kubeclientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
"k8s.io/kubernetes/pkg/controller"
"github.com/golang/glog"
)
const (
allClustersKey = "ALL_CLUSTERS"
)
// FederationSyncController synchronizes the state of a federated type
// to clusters that are members of the federation.
type FederationSyncController struct {
// For triggering reconciliation of a single resource. This is
// used when there is an add/update/delete operation on a resource
// in either federated API server or in some member of the
// federation.
deliverer *util.DelayingDeliverer
// For triggering reconciliation of all target resources. This is
// used when a new cluster becomes available.
clusterDeliverer *util.DelayingDeliverer
// Contains resources present in members of federation.
informer util.FederatedInformer
// For updating members of federation.
updater util.FederatedUpdater
// Definitions of resources that should be federated.
store cache.Store
// Informer controller for resources that should be federated.
controller cache.Controller
// Work queue allowing parallel processing of resources
workQueue workqueue.Interface
// Backoff manager
backoff *flowcontrol.Backoff
// For events
eventRecorder record.EventRecorder
deletionHelper *deletionhelper.DeletionHelper
reviewDelay time.Duration
clusterAvailableDelay time.Duration
smallDelay time.Duration
updateTimeout time.Duration
adapter federatedtypes.FederatedTypeAdapter
}
// StartFederationSyncController starts a new sync controller for a type adapter
func StartFederationSyncController(kind string, adapterFactory federatedtypes.AdapterFactory, config *restclient.Config, stopChan <-chan struct{}, minimizeLatency bool) {
restclient.AddUserAgent(config, fmt.Sprintf("federation-%s-controller", kind))
client := federationclientset.NewForConfigOrDie(config)
adapter := adapterFactory(client)
controller := newFederationSyncController(client, adapter)
if minimizeLatency {
controller.minimizeLatency()
}
glog.Infof(fmt.Sprintf("Starting federated sync controller for %s resources", kind))
controller.Run(stopChan)
}
// newFederationSyncController returns a new sync controller for the given client and type adapter
func newFederationSyncController(client federationclientset.Interface, adapter federatedtypes.FederatedTypeAdapter) *FederationSyncController {
broadcaster := record.NewBroadcaster()
broadcaster.StartRecordingToSink(eventsink.NewFederatedEventSink(client))
recorder := broadcaster.NewRecorder(api.Scheme, clientv1.EventSource{Component: fmt.Sprintf("federation-%v-controller", adapter.Kind())})
s := &FederationSyncController{
reviewDelay: time.Second * 10,
clusterAvailableDelay: time.Second * 20,
smallDelay: time.Second * 3,
updateTimeout: time.Second * 30,
workQueue: workqueue.New(),
backoff: flowcontrol.NewBackOff(5*time.Second, time.Minute),
eventRecorder: recorder,
adapter: adapter,
}
// Build delivereres for triggering reconciliations.
s.deliverer = util.NewDelayingDeliverer()
s.clusterDeliverer = util.NewDelayingDeliverer()
// Start informer in federated API servers on the resource type that should be federated.
s.store, s.controller = cache.NewInformer(
&cache.ListWatch{
ListFunc: func(options metav1.ListOptions) (pkgruntime.Object, error) {
return adapter.FedList(metav1.NamespaceAll, options)
},
WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
return adapter.FedWatch(metav1.NamespaceAll, options)
},
},
adapter.ObjectType(),
controller.NoResyncPeriodFunc(),
util.NewTriggerOnAllChanges(func(obj pkgruntime.Object) { s.deliverObj(obj, 0, false) }))
// Federated informer on the resource type in members of federation.
s.informer = util.NewFederatedInformer(
client,
func(cluster *federationapi.Cluster, targetClient kubeclientset.Interface) (cache.Store, cache.Controller) {
return cache.NewInformer(
&cache.ListWatch{
ListFunc: func(options metav1.ListOptions) (pkgruntime.Object, error) {
return adapter.ClusterList(targetClient, metav1.NamespaceAll, options)
},
WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
return adapter.ClusterWatch(targetClient, metav1.NamespaceAll, options)
},
},
adapter.ObjectType(),
controller.NoResyncPeriodFunc(),
// Trigger reconciliation whenever something in federated cluster is changed. In most cases it
// would be just confirmation that some operation on the target resource type had succeeded.
util.NewTriggerOnAllChanges(
func(obj pkgruntime.Object) {
s.deliverObj(obj, s.reviewDelay, false)
},
))
},
&util.ClusterLifecycleHandlerFuncs{
ClusterAvailable: func(cluster *federationapi.Cluster) {
// When new cluster becomes available process all the target resources again.
s.clusterDeliverer.DeliverAt(allClustersKey, nil, time.Now().Add(s.clusterAvailableDelay))
},
},
)
// Federated updeater along with Create/Update/Delete operations.
s.updater = util.NewFederatedUpdater(s.informer, adapter.Kind(), s.updateTimeout, s.eventRecorder,
func(client kubeclientset.Interface, obj pkgruntime.Object) error {
_, err := adapter.ClusterCreate(client, obj)
return err
},
func(client kubeclientset.Interface, obj pkgruntime.Object) error {
_, err := adapter.ClusterUpdate(client, obj)
return err
},
func(client kubeclientset.Interface, obj pkgruntime.Object) error {
namespacedName := adapter.NamespacedName(obj)
orphanDependents := false
err := adapter.ClusterDelete(client, namespacedName, &metav1.DeleteOptions{OrphanDependents: &orphanDependents})
return err
})
s.deletionHelper = deletionhelper.NewDeletionHelper(
s.updateObject,
// objNameFunc
func(obj pkgruntime.Object) string {
return adapter.NamespacedName(obj).String()
},
s.informer,
s.updater,
)
return s
}
// minimizeLatency reduces delays and timeouts to make the controller more responsive (useful for testing).
func (s *FederationSyncController) minimizeLatency() {
s.clusterAvailableDelay = time.Second
s.reviewDelay = 50 * time.Millisecond
s.smallDelay = 20 * time.Millisecond
s.updateTimeout = 5 * time.Second
}
// Sends the given updated object to apiserver.
func (s *FederationSyncController) updateObject(obj pkgruntime.Object) (pkgruntime.Object, error) {
return s.adapter.FedUpdate(obj)
}
func (s *FederationSyncController) Run(stopChan <-chan struct{}) {
go s.controller.Run(stopChan)
s.informer.Start()
s.deliverer.StartWithHandler(func(item *util.DelayingDelivererItem) {
s.workQueue.Add(item)
})
s.clusterDeliverer.StartWithHandler(func(_ *util.DelayingDelivererItem) {
s.reconcileOnClusterChange()
})
// TODO: Allow multiple workers.
go wait.Until(s.worker, time.Second, stopChan)
util.StartBackoffGC(s.backoff, stopChan)
// Ensure all goroutines are cleaned up when the stop channel closes
go func() {
<-stopChan
s.informer.Stop()
s.workQueue.ShutDown()
s.deliverer.Stop()
s.clusterDeliverer.Stop()
}()
}
type reconciliationStatus int
const (
statusAllOK reconciliationStatus = iota
statusNeedsRecheck
statusError
statusNotSynced
)
func (s *FederationSyncController) worker() {
for {
obj, quit := s.workQueue.Get()
if quit {
return
}
item := obj.(*util.DelayingDelivererItem)
namespacedName := item.Value.(*types.NamespacedName)
status := s.reconcile(*namespacedName)
s.workQueue.Done(item)
switch status {
case statusAllOK:
break
case statusError:
s.deliver(*namespacedName, 0, true)
case statusNeedsRecheck:
s.deliver(*namespacedName, s.reviewDelay, false)
case statusNotSynced:
s.deliver(*namespacedName, s.clusterAvailableDelay, false)
}
}
}
func (s *FederationSyncController) deliverObj(obj pkgruntime.Object, delay time.Duration, failed bool) {
namespacedName := s.adapter.NamespacedName(obj)
s.deliver(namespacedName, delay, failed)
}
// Adds backoff to delay if this delivery is related to some failure. Resets backoff if there was no failure.
func (s *FederationSyncController) deliver(namespacedName types.NamespacedName, delay time.Duration, failed bool) {
key := namespacedName.String()
if failed {
s.backoff.Next(key, time.Now())
delay = delay + s.backoff.Get(key)
} else {
s.backoff.Reset(key)
}
s.deliverer.DeliverAfter(key, &namespacedName, delay)
}
// Check whether all data stores are in sync. False is returned if any of the informer/stores is not yet
// synced with the corresponding api server.
func (s *FederationSyncController) isSynced() bool {
if !s.informer.ClustersSynced() {
glog.V(2).Infof("Cluster list not synced")
return false
}
clusters, err := s.informer.GetReadyClusters()
if err != nil {
runtime.HandleError(fmt.Errorf("Failed to get ready clusters: %v", err))
return false
}
if !s.informer.GetTargetStore().ClustersSynced(clusters) {
return false
}
return true
}
// The function triggers reconciliation of all target federated resources.
func (s *FederationSyncController) reconcileOnClusterChange() {
if !s.isSynced() {
s.clusterDeliverer.DeliverAt(allClustersKey, nil, time.Now().Add(s.clusterAvailableDelay))
}
for _, obj := range s.store.List() {
namespacedName := s.adapter.NamespacedName(obj.(pkgruntime.Object))
s.deliver(namespacedName, s.smallDelay, false)
}
}
func (s *FederationSyncController) reconcile(namespacedName types.NamespacedName) reconciliationStatus {
if !s.isSynced() {
return statusNotSynced
}
kind := s.adapter.Kind()
key := namespacedName.String()
obj, err := s.objFromCache(kind, key)
if err != nil {
return statusError
}
if obj == nil {
return statusAllOK
}
meta := s.adapter.ObjectMeta(obj)
if meta.DeletionTimestamp != nil {
err := s.delete(obj, kind, namespacedName)
if err != nil {
msg := "Failed to delete %s %q: %v"
args := []interface{}{kind, namespacedName, err}
runtime.HandleError(fmt.Errorf(msg, args...))
s.eventRecorder.Eventf(obj, api.EventTypeWarning, "DeleteFailed", msg, args...)
return statusError
}
return statusAllOK
}
glog.V(3).Infof("Ensuring finalizers exist on %s %q", kind, key)
obj, err = s.deletionHelper.EnsureFinalizers(obj)
if err != nil {
runtime.HandleError(fmt.Errorf("Failed to ensure finalizers for %s %q: %v", kind, key, err))
return statusError
}
operationsAccessor := func(adapter federatedtypes.FederatedTypeAdapter, selectedClusters []*federationapi.Cluster, unselectedClusters []*federationapi.Cluster, obj pkgruntime.Object, schedulingInfo *federatedtypes.SchedulingInfo) ([]util.FederatedOperation, error) {
operations, err := clusterOperations(adapter, selectedClusters, unselectedClusters, obj, key, schedulingInfo, func(clusterName string) (interface{}, bool, error) {
return s.informer.GetTargetStore().GetByKey(clusterName, key)
})
if err != nil {
s.eventRecorder.Eventf(obj, api.EventTypeWarning, "FedClusterOperationsError", "Error obtaining sync operations for %s: %s error: %s", kind, key, err.Error())
}
return operations, err
}
return syncToClusters(
s.informer.GetReadyClusters,
operationsAccessor,
selectedClusters,
s.updater.Update,
s.adapter,
s.informer,
obj,
)
}
func (s *FederationSyncController) objFromCache(kind, key string) (pkgruntime.Object, error) {
cachedObj, exist, err := s.store.GetByKey(key)
if err != nil {
wrappedErr := fmt.Errorf("Failed to query %s store for %q: %v", kind, key, err)
runtime.HandleError(wrappedErr)
return nil, err
}
if !exist {
return nil, nil
}
// Create a copy before modifying the resource to prevent racing with other readers.
copiedObj, err := api.Scheme.DeepCopy(cachedObj)
if err != nil {
wrappedErr := fmt.Errorf("Error in retrieving %s %q from store: %v", kind, key, err)
runtime.HandleError(wrappedErr)
return nil, err
}
if !s.adapter.IsExpectedType(copiedObj) {
err = fmt.Errorf("Object is not the expected type: %v", copiedObj)
runtime.HandleError(err)
return nil, err
}
return copiedObj.(pkgruntime.Object), nil
}
// delete deletes the given resource or returns error if the deletion was not complete.
func (s *FederationSyncController) delete(obj pkgruntime.Object, kind string, namespacedName types.NamespacedName) error {
glog.V(3).Infof("Handling deletion of %s %q", kind, namespacedName)
_, err := s.deletionHelper.HandleObjectInUnderlyingClusters(obj)
if err != nil {
return err
}
err = s.adapter.FedDelete(namespacedName, nil)
if err != nil {
// Its all good if the error is not found error. That means it is deleted already and we do not have to do anything.
// This is expected when we are processing an update as a result of finalizer deletion.
// The process that deleted the last finalizer is also going to delete the resource and we do not have to do anything.
if !errors.IsNotFound(err) {
return err
}
}
return nil
}
type clustersAccessorFunc func() ([]*federationapi.Cluster, error)
type operationsFunc func(federatedtypes.FederatedTypeAdapter, []*federationapi.Cluster, []*federationapi.Cluster, pkgruntime.Object, *federatedtypes.SchedulingInfo) ([]util.FederatedOperation, error)
type clusterSelectorFunc func(*metav1.ObjectMeta, func(map[string]string, map[string]string) (bool, error), []*federationapi.Cluster) ([]*federationapi.Cluster, []*federationapi.Cluster, error)
type executionFunc func([]util.FederatedOperation) error
// syncToClusters ensures that the state of the given object is synchronized to member clusters.
func syncToClusters(clustersAccessor clustersAccessorFunc, operationsAccessor operationsFunc, selector clusterSelectorFunc, execute executionFunc, adapter federatedtypes.FederatedTypeAdapter, informer util.FederatedInformer, obj pkgruntime.Object) reconciliationStatus {
kind := adapter.Kind()
key := federatedtypes.ObjectKey(adapter, obj)
glog.V(3).Infof("Syncing %s %q in underlying clusters", kind, key)
clusters, err := clustersAccessor()
if err != nil {
runtime.HandleError(fmt.Errorf("Failed to get cluster list: %v", err))
return statusNotSynced
}
selectedClusters, unselectedClusters, err := selector(adapter.ObjectMeta(obj), clusterselector.SendToCluster, clusters)
if err != nil {
return statusError
}
var schedulingInfo *federatedtypes.SchedulingInfo
if adapter.IsSchedulingAdapter() {
schedulingAdapter, ok := adapter.(federatedtypes.SchedulingAdapter)
if !ok {
glog.Fatalf("Adapter for kind %q does not properly implement SchedulingAdapter.", kind)
}
schedulingInfo, err = schedulingAdapter.GetSchedule(obj, key, selectedClusters, informer)
if err != nil {
runtime.HandleError(fmt.Errorf("adapter.GetSchedule() failed on adapter for %s %q: %v", kind, key, err))
return statusError
}
}
operations, err := operationsAccessor(adapter, selectedClusters, unselectedClusters, obj, schedulingInfo)
if err != nil {
return statusError
}
if adapter.IsSchedulingAdapter() {
schedulingAdapter, ok := adapter.(federatedtypes.SchedulingAdapter)
if !ok {
glog.Fatalf("Adapter for kind %q does not properly implement SchedulingAdapter.", kind)
}
err = schedulingAdapter.UpdateFederatedStatus(obj, schedulingInfo.Status)
if err != nil {
runtime.HandleError(fmt.Errorf("adapter.UpdateFinished() failed on adapter for %s %q: %v", kind, key, err))
return statusError
}
}
if len(operations) == 0 {
return statusAllOK
}
err = execute(operations)
if err != nil {
runtime.HandleError(fmt.Errorf("Failed to execute updates for %s %q: %v", kind, key, err))
return statusError
}
// Everything is in order but let's be double sure
return statusNeedsRecheck
}
// selectedClusters filters the provided clusters into two slices, one containing the clusters selected by selector and the other containing the rest of the provided clusters.
func selectedClusters(objMeta *metav1.ObjectMeta, selector func(map[string]string, map[string]string) (bool, error), clusters []*federationapi.Cluster) ([]*federationapi.Cluster, []*federationapi.Cluster, error) {
selectedClusters := []*federationapi.Cluster{}
unselectedClusters := []*federationapi.Cluster{}
for _, cluster := range clusters {
send, err := selector(cluster.Labels, objMeta.Annotations)
if err != nil {
return nil, nil, err
} else if !send {
unselectedClusters = append(unselectedClusters, cluster)
} else {
selectedClusters = append(selectedClusters, cluster)
}
}
return selectedClusters, unselectedClusters, nil
}
type clusterObjectAccessorFunc func(clusterName string) (interface{}, bool, error)
// clusterOperations returns the list of operations needed to synchronize the state of the given object to the provided clusters
func clusterOperations(adapter federatedtypes.FederatedTypeAdapter, selectedClusters []*federationapi.Cluster, unselectedClusters []*federationapi.Cluster, obj pkgruntime.Object, key string, schedulingInfo *federatedtypes.SchedulingInfo, accessor clusterObjectAccessorFunc) ([]util.FederatedOperation, error) {
operations := make([]util.FederatedOperation, 0)
kind := adapter.Kind()
for _, cluster := range selectedClusters {
// The data should not be modified.
desiredObj := adapter.Copy(obj)
clusterObj, found, err := accessor(cluster.Name)
if err != nil {
wrappedErr := fmt.Errorf("Failed to get %s %q from cluster %q: %v", kind, key, cluster.Name, err)
runtime.HandleError(wrappedErr)
return nil, wrappedErr
}
shouldCreateIfNeeded := true
if adapter.IsSchedulingAdapter() {
schedulingAdapter, ok := adapter.(federatedtypes.SchedulingAdapter)
if !ok {
err = fmt.Errorf("adapter for kind %s does not properly implement SchedulingAdapter.", kind)
glog.Fatalf("Error: %v", err)
}
var clusterTypedObj pkgruntime.Object = nil
if clusterObj != nil {
clusterTypedObj = clusterObj.(pkgruntime.Object)
}
desiredObj, shouldCreateIfNeeded, err = schedulingAdapter.ScheduleObject(cluster, clusterTypedObj, desiredObj, schedulingInfo)
if err != nil {
runtime.HandleError(err)
return nil, err
}
}
var operationType util.FederatedOperationType = ""
if found {
clusterObj := clusterObj.(pkgruntime.Object)
if !adapter.Equivalent(desiredObj, clusterObj) {
operationType = util.OperationTypeUpdate
}
} else if shouldCreateIfNeeded {
operationType = util.OperationTypeAdd
}
if len(operationType) > 0 {
operations = append(operations, util.FederatedOperation{
Type: operationType,
Obj: desiredObj,
ClusterName: cluster.Name,
Key: key,
})
}
}
for _, cluster := range unselectedClusters {
clusterObj, found, err := accessor(cluster.Name)
if err != nil {
wrappedErr := fmt.Errorf("Failed to get %s %q from cluster %q: %v", kind, key, cluster.Name, err)
runtime.HandleError(wrappedErr)
return nil, wrappedErr
}
if found {
operations = append(operations, util.FederatedOperation{
Type: util.OperationTypeDelete,
Obj: clusterObj.(pkgruntime.Object),
ClusterName: cluster.Name,
Key: key,
})
}
}
return operations, nil
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Go
1
https://gitee.com/meoom/kubernetes.git
git@gitee.com:meoom/kubernetes.git
meoom
kubernetes
kubernetes
v1.7.5-beta.0

搜索帮助

23e8dbc6 1850385 7e0993f3 1850385