diff --git a/pkg/build/controller/factory/factory.go b/pkg/build/controller/factory/factory.go index 4a1d1346b768..60cd82a8e380 100644 --- a/pkg/build/controller/factory/factory.go +++ b/pkg/build/controller/factory/factory.go @@ -25,12 +25,18 @@ import ( strategy "github.com/openshift/origin/pkg/build/controller/strategy" buildutil "github.com/openshift/origin/pkg/build/util" osclient "github.com/openshift/origin/pkg/client" + oscache "github.com/openshift/origin/pkg/client/cache" controller "github.com/openshift/origin/pkg/controller" imageapi "github.com/openshift/origin/pkg/image/api" errors "github.com/openshift/origin/pkg/util/errors" ) -const maxRetries = 60 +const ( + // We must avoid creating processing imagestream changes until the build config store has synced. + // If it hasn't synced, to avoid a hot loop, we'll wait this long between checks. + StoreSyncedPollPeriod = 100 * time.Millisecond + maxRetries = 60 +) // limitedLogAndRetry stops retrying after maxTimeout, failing the build. func limitedLogAndRetry(buildupdater buildclient.BuildUpdater, maxTimeout time.Duration) controller.RetryFunc { @@ -274,6 +280,8 @@ func (factory *BuildPodControllerFactory) CreateDeleteController() controller.Ru type ImageChangeControllerFactory struct { Client osclient.Interface BuildConfigInstantiator buildclient.BuildConfigInstantiator + BuildConfigIndex oscache.StoreToBuildConfigLister + BuildConfigIndexSynced func() bool // Stop may be set to allow controllers created by this factory to be terminated. Stop <-chan struct{} } @@ -284,14 +292,14 @@ func (factory *ImageChangeControllerFactory) Create() controller.RunnableControl queue := cache.NewResyncableFIFO(cache.MetaNamespaceKeyFunc) cache.NewReflector(&imageStreamLW{factory.Client}, &imageapi.ImageStream{}, queue, 2*time.Minute).RunUntil(factory.Stop) - store := cache.NewStore(cache.MetaNamespaceKeyFunc) - cache.NewReflector(&buildConfigLW{client: factory.Client}, &buildapi.BuildConfig{}, store, 2*time.Minute).RunUntil(factory.Stop) - imageChangeController := &buildcontroller.ImageChangeController{ - BuildConfigStore: store, + BuildConfigIndex: factory.BuildConfigIndex, BuildConfigInstantiator: factory.BuildConfigInstantiator, } + // Wait for the bc store to sync before starting any work in this controller. + factory.waitForSyncedStores() + return &controller.RetryController{ Queue: queue, RetryManager: controller.NewQueueRetryManager( @@ -305,11 +313,20 @@ func (factory *ImageChangeControllerFactory) Create() controller.RunnableControl ), Handle: func(obj interface{}) error { imageRepo := obj.(*imageapi.ImageStream) - return imageChangeController.HandleImageRepo(imageRepo) + return imageChangeController.HandleImageStream(imageRepo) }, } } +func (factory *ImageChangeControllerFactory) waitForSyncedStores() { + for !factory.BuildConfigIndexSynced() { + glog.V(4).Infof("Waiting for the bc caches to sync before starting the imagechange buildconfig controller worker") + select { + case <-time.After(StoreSyncedPollPeriod): + } + } +} + type BuildConfigControllerFactory struct { Client osclient.Interface KubeClient kclient.Interface diff --git a/pkg/build/controller/image_change_controller.go b/pkg/build/controller/image_change_controller.go index 6474092a6252..72dcb9570f07 100644 --- a/pkg/build/controller/image_change_controller.go +++ b/pkg/build/controller/image_change_controller.go @@ -8,12 +8,12 @@ import ( kerrors "k8s.io/kubernetes/pkg/api/errors" kapi "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/client/cache" utilruntime "k8s.io/kubernetes/pkg/util/runtime" buildapi "github.com/openshift/origin/pkg/build/api" buildclient "github.com/openshift/origin/pkg/build/client" buildutil "github.com/openshift/origin/pkg/build/util" + oscache "github.com/openshift/origin/pkg/client/cache" imageapi "github.com/openshift/origin/pkg/image/api" ) @@ -31,7 +31,7 @@ func (e ImageChangeControllerFatalError) Error() string { // builds when a new version of a tag referenced by a BuildConfig // is available. type ImageChangeController struct { - BuildConfigStore cache.Store + BuildConfigIndex oscache.StoreToBuildConfigLister BuildConfigInstantiator buildclient.BuildConfigInstantiator } @@ -42,9 +42,9 @@ func getImageStreamNameFromReference(ref *kapi.ObjectReference) string { return strings.Split(name, "@")[0] } -// HandleImageRepo processes the next ImageStream event. -func (c *ImageChangeController) HandleImageRepo(repo *imageapi.ImageStream) error { - glog.V(4).Infof("Build image change controller detected ImageStream change %s", repo.Status.DockerImageRepository) +// HandleImageStream processes the next ImageStream event. +func (c *ImageChangeController) HandleImageStream(stream *imageapi.ImageStream) error { + glog.V(4).Infof("Build image change controller detected ImageStream change %s", stream.Status.DockerImageRepository) // Loop through all build configurations and record if there was an error // instead of breaking the loop. The error will be returned in the end, so the @@ -53,17 +53,18 @@ func (c *ImageChangeController) HandleImageRepo(repo *imageapi.ImageStream) erro // in a no-op for them. hasError := false - // TODO: this is inefficient - for _, bc := range c.BuildConfigStore.List() { - config := bc.(*buildapi.BuildConfig) - + bcs, err := c.BuildConfigIndex.GetConfigsForImageStreamTrigger(stream) + if err != nil { + return err + } + for _, config := range bcs { var ( from *kapi.ObjectReference shouldBuild = false triggeredImage = "" latest *imageapi.TagEvent ) - // For every ImageChange trigger find the latest tagged image from the image repository and + // For every ImageChange trigger find the latest tagged image from the image stream and // invoke a build using that image id. A new build is triggered only if the latest tagged image id or pull spec // differs from the last triggered build recorded on the build config for that trigger for _, trigger := range config.Spec.Triggers { @@ -90,21 +91,21 @@ func (c *ImageChangeController) HandleImageRepo(repo *imageapi.ImageStream) erro fromNamespace = config.Namespace } - // only trigger a build if this image repo matches the name and namespace of the ref in the build trigger + // only trigger a build if this image stream matches the name and namespace of the stream ref in the build trigger // also do not trigger if the imagerepo does not have a valid DockerImageRepository value for us to pull // the image from - if len(repo.Status.DockerImageRepository) == 0 || fromStreamName != repo.Name || fromNamespace != repo.Namespace { + if len(stream.Status.DockerImageRepository) == 0 || fromStreamName != stream.Name || fromNamespace != stream.Namespace { continue } // This split is safe because ImageStreamTag names always have the form // name:tag. - latest = imageapi.LatestTaggedImage(repo, tag) + latest = imageapi.LatestTaggedImage(stream, tag) if latest == nil { - glog.V(4).Infof("unable to find tagged image: no image recorded for %s/%s:%s", repo.Namespace, repo.Name, tag) + glog.V(4).Infof("unable to find tagged image: no image recorded for %s/%s:%s", stream.Namespace, stream.Name, tag) continue } - glog.V(4).Infof("Found ImageStream %s/%s with tag %s", repo.Namespace, repo.Name, tag) + glog.V(4).Infof("Found ImageStream %s/%s with tag %s", stream.Namespace, stream.Name, tag) // (must be different) to trigger a build last := trigger.ImageChange.LastTriggeredImageID @@ -155,7 +156,7 @@ func (c *ImageChangeController) HandleImageRepo(repo *imageapi.ImageStream) erro } } if hasError { - return fmt.Errorf("an error occurred processing 1 or more build configurations; the image change trigger for image stream %s will be retried", repo.Status.DockerImageRepository) + return fmt.Errorf("an error occurred processing 1 or more build configurations; the image change trigger for image stream %s will be retried", stream.Status.DockerImageRepository) } return nil } diff --git a/pkg/build/controller/image_change_controller_test.go b/pkg/build/controller/image_change_controller_test.go index a1c1cc530912..bfcc231f3fa6 100644 --- a/pkg/build/controller/image_change_controller_test.go +++ b/pkg/build/controller/image_change_controller_test.go @@ -26,9 +26,9 @@ func TestNewImageID(t *testing.T) { bcInstantiator := controller.BuildConfigInstantiator.(*buildConfigInstantiator) bcUpdater := bcInstantiator.buildConfigUpdater - err := controller.HandleImageRepo(imageStream) + err := controller.HandleImageStream(imageStream) if err != nil { - t.Fatalf("Unexpected error %v from HandleImageRepo", err) + t.Fatalf("Unexpected error %v from HandleImageStream", err) } if len(bcInstantiator.name) == 0 { @@ -54,9 +54,9 @@ func TestNewImageIDDefaultTag(t *testing.T) { bcInstantiator := controller.BuildConfigInstantiator.(*buildConfigInstantiator) bcUpdater := bcInstantiator.buildConfigUpdater - err := controller.HandleImageRepo(imageStream) + err := controller.HandleImageStream(imageStream) if err != nil { - t.Fatalf("Unexpected error %v from HandleImageRepo", err) + t.Fatalf("Unexpected error %v from HandleImageStream", err) } if len(bcInstantiator.name) == 0 { t.Error("Expected build generation when new image was created!") @@ -82,9 +82,9 @@ func TestNonExistentImageStream(t *testing.T) { bcInstantiator := controller.BuildConfigInstantiator.(*buildConfigInstantiator) bcUpdater := bcInstantiator.buildConfigUpdater - err := controller.HandleImageRepo(imageStream) + err := controller.HandleImageStream(imageStream) if err != nil { - t.Fatalf("Unexpected error %v from HandleImageRepo", err) + t.Fatalf("Unexpected error %v from HandleImageStream", err) } if len(bcInstantiator.name) != 0 { t.Error("New build generated when a different repository was updated!") @@ -103,9 +103,9 @@ func TestNewImageDifferentTagUpdate(t *testing.T) { bcInstantiator := controller.BuildConfigInstantiator.(*buildConfigInstantiator) bcUpdater := bcInstantiator.buildConfigUpdater - err := controller.HandleImageRepo(imageStream) + err := controller.HandleImageStream(imageStream) if err != nil { - t.Errorf("Unexpected error %v from HandleImageRepo", err) + t.Errorf("Unexpected error %v from HandleImageStream", err) } if len(bcInstantiator.name) != 0 { t.Error("New build generated when a different repository was updated!") @@ -126,9 +126,9 @@ func TestNewImageDifferentTagUpdate2(t *testing.T) { bcInstantiator := controller.BuildConfigInstantiator.(*buildConfigInstantiator) bcUpdater := bcInstantiator.buildConfigUpdater - err := controller.HandleImageRepo(imageStream) + err := controller.HandleImageStream(imageStream) if err != nil { - t.Errorf("Unexpected error %v from HandleImageRepo", err) + t.Errorf("Unexpected error %v from HandleImageStream", err) } if len(bcInstantiator.name) != 0 { t.Error("New build generated when a different repository was updated!") @@ -147,9 +147,9 @@ func TestNewDifferentImageUpdate(t *testing.T) { bcInstantiator := controller.BuildConfigInstantiator.(*buildConfigInstantiator) bcUpdater := bcInstantiator.buildConfigUpdater - err := controller.HandleImageRepo(imageStream) + err := controller.HandleImageStream(imageStream) if err != nil { - t.Errorf("Unexpected error %v from HandleImageRepo", err) + t.Errorf("Unexpected error %v from HandleImageStream", err) } if len(bcInstantiator.name) != 0 { t.Error("New build generated when a different repository was updated!") @@ -170,9 +170,9 @@ func TestSameStreamNameDifferentNamespaces(t *testing.T) { bcInstantiator := controller.BuildConfigInstantiator.(*buildConfigInstantiator) bcUpdater := bcInstantiator.buildConfigUpdater - err := controller.HandleImageRepo(imageStream) + err := controller.HandleImageStream(imageStream) if err != nil { - t.Errorf("Unexpected error %v from HandleImageRepo", err) + t.Errorf("Unexpected error %v from HandleImageStream", err) } if len(bcInstantiator.name) != 0 { t.Error("New build generated when a different repository was updated!") @@ -192,9 +192,9 @@ func TestBuildConfigWithDifferentTriggerType(t *testing.T) { bcInstantiator := controller.BuildConfigInstantiator.(*buildConfigInstantiator) bcUpdater := bcInstantiator.buildConfigUpdater - err := controller.HandleImageRepo(imageStream) + err := controller.HandleImageStream(imageStream) if err != nil { - t.Errorf("Unexpected error %v from HandleImageRepo", err) + t.Errorf("Unexpected error %v from HandleImageStream", err) } if len(bcInstantiator.name) != 0 { t.Error("New build generated when a different repository was updated!") @@ -215,9 +215,9 @@ func TestNoImageIDChange(t *testing.T) { bcInstantiator := controller.BuildConfigInstantiator.(*buildConfigInstantiator) bcUpdater := bcInstantiator.buildConfigUpdater - err := controller.HandleImageRepo(imageStream) + err := controller.HandleImageStream(imageStream) if err != nil { - t.Errorf("Unexpected error %v from HandleImageRepo", err) + t.Errorf("Unexpected error %v from HandleImageStream", err) } if len(bcInstantiator.name) != 0 { t.Error("New build generated when no change happened!") @@ -237,9 +237,9 @@ func TestBuildConfigInstantiatorError(t *testing.T) { bcInstantiator.err = fmt.Errorf("instantiating error") bcUpdater := bcInstantiator.buildConfigUpdater - err := controller.HandleImageRepo(imageStream) + err := controller.HandleImageStream(imageStream) if err == nil || !strings.Contains(err.Error(), "will be retried") { - t.Fatalf("Expected 'will be retried' from HandleImageRepo, got %s", err.Error()) + t.Fatalf("Expected 'will be retried' from HandleImageStream, got %s", err.Error()) } if actual, expected := bcInstantiator.newBuild.Spec.Strategy.DockerStrategy.From.Name, "registry.com/namespace/imagename:newImageID123"; actual != expected { t.Errorf("Image substitutions not properly setup for new build. Expected %s, got %s |", expected, actual) @@ -259,12 +259,12 @@ func TestBuildConfigUpdateError(t *testing.T) { bcUpdater := bcInstantiator.buildConfigUpdater bcUpdater.err = kerrors.NewConflict(buildapi.Resource("BuildConfig"), buildcfg.Name, errors.New("foo")) - err := controller.HandleImageRepo(imageStream) + err := controller.HandleImageStream(imageStream) if len(bcInstantiator.name) == 0 { t.Error("Expected build generation when new image was created!") } if err == nil || !strings.Contains(err.Error(), "will be retried") { - t.Fatalf("Expected 'will be retried' from HandleImageRepo, got %s", err.Error()) + t.Fatalf("Expected 'will be retried' from HandleImageStream, got %s", err.Error()) } } @@ -277,9 +277,9 @@ func TestNewImageIDNoDockerRepo(t *testing.T) { bcInstantiator := controller.BuildConfigInstantiator.(*buildConfigInstantiator) bcUpdater := bcInstantiator.buildConfigUpdater - err := controller.HandleImageRepo(imageStream) + err := controller.HandleImageStream(imageStream) if err != nil { - t.Errorf("Unexpected error %v from HandleImageRepo", err) + t.Errorf("Unexpected error %v from HandleImageStream", err) } if len(bcInstantiator.name) != 0 { t.Error("New build generated when no change happened!") @@ -417,7 +417,7 @@ func mockBuildConfigInstantiator(buildcfg *buildapi.BuildConfig, imageStream *im func mockImageChangeController(buildcfg *buildapi.BuildConfig, imageStream *imageapi.ImageStream, image *imageapi.Image) *ImageChangeController { return &ImageChangeController{ - BuildConfigStore: buildtest.NewFakeBuildConfigStore(buildcfg), + BuildConfigIndex: buildtest.NewFakeBuildConfigIndex(buildcfg), BuildConfigInstantiator: mockBuildConfigInstantiator(buildcfg, imageStream, image), } } diff --git a/pkg/build/controller/test/fake_build_config_index.go b/pkg/build/controller/test/fake_build_config_index.go new file mode 100644 index 000000000000..0093d6166077 --- /dev/null +++ b/pkg/build/controller/test/fake_build_config_index.go @@ -0,0 +1,24 @@ +package test + +import ( + buildapi "github.com/openshift/origin/pkg/build/api" + oscache "github.com/openshift/origin/pkg/client/cache" + imageapi "github.com/openshift/origin/pkg/image/api" +) + +type FakeBuildConfigIndex struct { + Build *buildapi.BuildConfig + Err error +} + +func NewFakeBuildConfigIndex(build *buildapi.BuildConfig) oscache.StoreToBuildConfigLister { + return &FakeBuildConfigIndex{Build: build} +} + +func (i *FakeBuildConfigIndex) List() ([]*buildapi.BuildConfig, error) { + return []*buildapi.BuildConfig{i.Build}, nil +} + +func (i *FakeBuildConfigIndex) GetConfigsForImageStreamTrigger(stream *imageapi.ImageStream) ([]*buildapi.BuildConfig, error) { + return []*buildapi.BuildConfig{i.Build}, nil +} diff --git a/pkg/build/controller/test/fake_build_config_store.go b/pkg/build/controller/test/fake_build_config_store.go deleted file mode 100644 index a66c331d510d..000000000000 --- a/pkg/build/controller/test/fake_build_config_store.go +++ /dev/null @@ -1,62 +0,0 @@ -package test - -import ( - buildapi "github.com/openshift/origin/pkg/build/api" - "k8s.io/kubernetes/pkg/util/sets" -) - -type FakeBuildConfigStore struct { - Build *buildapi.BuildConfig - Err error -} - -func NewFakeBuildConfigStore(build *buildapi.BuildConfig) FakeBuildConfigStore { - return FakeBuildConfigStore{Build: build} -} - -func (s FakeBuildConfigStore) Add(obj interface{}) error { - return s.Err -} - -func (s FakeBuildConfigStore) Update(obj interface{}) error { - return s.Err -} - -func (s FakeBuildConfigStore) Delete(obj interface{}) error { - return s.Err -} - -func (s FakeBuildConfigStore) Resync() error { - return s.Err -} - -func (s FakeBuildConfigStore) List() []interface{} { - return []interface{}{s.Build} -} - -func (s FakeBuildConfigStore) ListKeys() []string { - return []string{"config"} -} - -func (s FakeBuildConfigStore) ContainedIDs() sets.String { - return sets.NewString() -} - -func (s FakeBuildConfigStore) Get(obj interface{}) (item interface{}, exists bool, err error) { - return s.GetByKey("") -} - -func (s FakeBuildConfigStore) GetByKey(id string) (item interface{}, exists bool, err error) { - if s.Err != nil { - return nil, false, err - } - if s.Build == nil { - return nil, false, nil - } - - return s.Build, true, nil -} - -func (s FakeBuildConfigStore) Replace(list []interface{}, resourceVersion string) error { - return nil -} diff --git a/pkg/client/cache/buildconfig.go b/pkg/client/cache/buildconfig.go new file mode 100644 index 000000000000..9523ad7b5478 --- /dev/null +++ b/pkg/client/cache/buildconfig.go @@ -0,0 +1,100 @@ +package cache + +import ( + kapi "k8s.io/kubernetes/pkg/api" + kapierrors "k8s.io/kubernetes/pkg/api/errors" + "k8s.io/kubernetes/pkg/client/cache" + "k8s.io/kubernetes/pkg/labels" + + buildapi "github.com/openshift/origin/pkg/build/api" + imageapi "github.com/openshift/origin/pkg/image/api" +) + +// StoreToBuildConfigLister gives a store List and Exists methods. The store must contain only buildconfigs. +type StoreToBuildConfigLister interface { + List() ([]*buildapi.BuildConfig, error) + GetConfigsForImageStreamTrigger(*imageapi.ImageStream) ([]*buildapi.BuildConfig, error) +} + +// StoreToBuildConfigListerImpl implements a StoreToBuildConfigLister +type StoreToBuildConfigListerImpl struct { + cache.Indexer +} + +// List all buildconfigs in the store. +func (s *StoreToBuildConfigListerImpl) List() ([]*buildapi.BuildConfig, error) { + configs := []*buildapi.BuildConfig{} + for _, c := range s.Indexer.List() { + configs = append(configs, c.(*buildapi.BuildConfig)) + } + return configs, nil +} + +// GetConfigsForImageStream returns all the build configs that are triggered by the provided image stream +// by searching through using the ImageStreamReferenceIndex (build configs are indexed in the cache +// by image stream references). +func (s *StoreToBuildConfigListerImpl) GetConfigsForImageStreamTrigger(stream *imageapi.ImageStream) ([]*buildapi.BuildConfig, error) { + items, err := s.Indexer.ByIndex(ImageStreamReferenceIndex, stream.Namespace+"/"+stream.Name) + if err != nil { + return nil, err + } + + var configs []*buildapi.BuildConfig + + for _, obj := range items { + config := obj.(*buildapi.BuildConfig) + configs = append(configs, config) + } + + return configs, nil +} + +func (s *StoreToBuildConfigListerImpl) buildconfigs(namespace string) storebuildconfigsNamespacer { + return storebuildconfigsNamespacer{s.Indexer, namespace} +} + +type storebuildconfigsNamespacer struct { + indexer cache.Indexer + namespace string +} + +// Get the build config matching the name from the cache. +func (s storebuildconfigsNamespacer) Get(name string) (*buildapi.BuildConfig, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, kapierrors.NewNotFound(buildapi.Resource("BuildConfig"), name) + } + return obj.(*buildapi.BuildConfig), nil +} + +// List all the buildconfigs that match the provided selector using a namespace index. +// If the indexed list fails then we will fallback to listing from all namespaces and filter +// by the namespace we want. +func (s storebuildconfigsNamespacer) List(selector labels.Selector) ([]*buildapi.BuildConfig, error) { + configs := []*buildapi.BuildConfig{} + + if s.namespace == kapi.NamespaceAll { + for _, obj := range s.indexer.List() { + bc := obj.(*buildapi.BuildConfig) + if selector.Matches(labels.Set(bc.Labels)) { + configs = append(configs, bc) + } + } + return configs, nil + } + + items, err := s.indexer.ByIndex(cache.NamespaceIndex, s.namespace) + if err != nil { + return nil, err + } + for _, obj := range items { + bc := obj.(*buildapi.BuildConfig) + if selector.Matches(labels.Set(bc.Labels)) { + configs = append(configs, bc) + } + } + return configs, nil +} diff --git a/pkg/client/cache/index.go b/pkg/client/cache/index.go index 04817bab9834..6660b5dcbb3a 100644 --- a/pkg/client/cache/index.go +++ b/pkg/client/cache/index.go @@ -3,6 +3,8 @@ package cache import ( "fmt" + buildapi "github.com/openshift/origin/pkg/build/api" + buildutil "github.com/openshift/origin/pkg/build/util" deployapi "github.com/openshift/origin/pkg/deploy/api" imageapi "github.com/openshift/origin/pkg/image/api" ) @@ -15,6 +17,40 @@ const ( func ImageStreamReferenceIndexFunc(obj interface{}) ([]string, error) { switch t := obj.(type) { // TODO: Add support for build configs + case *buildapi.BuildConfig: + var keys []string + + for _, trigger := range t.Spec.Triggers { + if trigger.Type != buildapi.ImageChangeBuildTriggerType { + continue + } + from := trigger.ImageChange.From + + // the default imagechange trigger has no "from" value, it + // uses the input image of the buildconfig as the imagestream + // to trigger off, so we need to look that up. + if from == nil { + from = buildutil.GetInputReference(t.Spec.Strategy) + if from == nil || from.Kind != "ImageStreamTag" { + continue + } + } + name, _, _ := imageapi.SplitImageStreamTag(from.Name) + namespace := from.Namespace + + // if the imagestream reference has no namespace, use the + // namespace of the buildconfig. + if len(namespace) == 0 { + namespace = t.Namespace + } + keys = append(keys, namespace+"/"+name) + } + + if len(keys) == 0 { + // Return an empty key for configs that don't hold object references. + keys = append(keys, "") + } + return keys, nil case *deployapi.DeploymentConfig: var keys []string diff --git a/pkg/cmd/server/origin/run_components.go b/pkg/cmd/server/origin/run_components.go index ab91497a54d4..36d32442f541 100644 --- a/pkg/cmd/server/origin/run_components.go +++ b/pkg/cmd/server/origin/run_components.go @@ -32,7 +32,10 @@ import ( buildcontrollerfactory "github.com/openshift/origin/pkg/build/controller/factory" buildstrategy "github.com/openshift/origin/pkg/build/controller/strategy" osclient "github.com/openshift/origin/pkg/client" + oscache "github.com/openshift/origin/pkg/client/cache" cmdadmission "github.com/openshift/origin/pkg/cmd/server/admission" + configapi "github.com/openshift/origin/pkg/cmd/server/api" + "github.com/openshift/origin/pkg/cmd/server/bootstrappolicy" "github.com/openshift/origin/pkg/cmd/server/crypto" cmdutil "github.com/openshift/origin/pkg/cmd/util" "github.com/openshift/origin/pkg/cmd/util/clientcmd" @@ -44,21 +47,18 @@ import ( "github.com/openshift/origin/pkg/dns" imagecontroller "github.com/openshift/origin/pkg/image/controller" projectcontroller "github.com/openshift/origin/pkg/project/controller" + quota "github.com/openshift/origin/pkg/quota" + quotacontroller "github.com/openshift/origin/pkg/quota/controller" + "github.com/openshift/origin/pkg/quota/controller/clusterquotareconciliation" + sdnplugin "github.com/openshift/origin/pkg/sdn/plugin" securitycontroller "github.com/openshift/origin/pkg/security/controller" "github.com/openshift/origin/pkg/security/mcs" "github.com/openshift/origin/pkg/security/uid" "github.com/openshift/origin/pkg/security/uidallocator" "github.com/openshift/origin/pkg/service/controller/ingressip" servingcertcontroller "github.com/openshift/origin/pkg/service/controller/servingcert" - unidlingcontroller "github.com/openshift/origin/pkg/unidling/controller" - - configapi "github.com/openshift/origin/pkg/cmd/server/api" - "github.com/openshift/origin/pkg/cmd/server/bootstrappolicy" - quota "github.com/openshift/origin/pkg/quota" - quotacontroller "github.com/openshift/origin/pkg/quota/controller" - "github.com/openshift/origin/pkg/quota/controller/clusterquotareconciliation" - sdnplugin "github.com/openshift/origin/pkg/sdn/plugin" serviceaccountcontrollers "github.com/openshift/origin/pkg/serviceaccounts/controllers" + unidlingcontroller "github.com/openshift/origin/pkg/unidling/controller" ) const ( @@ -291,7 +291,9 @@ func (c *MasterConfig) RunBuildPodController() { func (c *MasterConfig) RunBuildImageChangeTriggerController() { bcClient, _ := c.BuildImageChangeTriggerControllerClients() bcInstantiator := buildclient.NewOSClientBuildConfigInstantiatorClient(bcClient) - factory := buildcontrollerfactory.ImageChangeControllerFactory{Client: bcClient, BuildConfigInstantiator: bcInstantiator} + bcIndex := &oscache.StoreToBuildConfigListerImpl{c.Informers.BuildConfigs().Indexer()} + bcIndexSynced := c.Informers.BuildConfigs().Informer().HasSynced + factory := buildcontrollerfactory.ImageChangeControllerFactory{Client: bcClient, BuildConfigInstantiator: bcInstantiator, BuildConfigIndex: bcIndex, BuildConfigIndexSynced: bcIndexSynced} factory.Create().Run() } diff --git a/pkg/controller/shared/buildconfig_informers.go b/pkg/controller/shared/buildconfig_informers.go new file mode 100644 index 000000000000..2ac87d653a85 --- /dev/null +++ b/pkg/controller/shared/buildconfig_informers.go @@ -0,0 +1,63 @@ +package shared + +import ( + "reflect" + + kapi "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/client/cache" + "k8s.io/kubernetes/pkg/controller/framework" + "k8s.io/kubernetes/pkg/runtime" + "k8s.io/kubernetes/pkg/watch" + + buildapi "github.com/openshift/origin/pkg/build/api" + oscache "github.com/openshift/origin/pkg/client/cache" +) + +type BuildConfigInformer interface { + Informer() framework.SharedIndexInformer + Indexer() cache.Indexer + Lister() oscache.StoreToBuildConfigLister +} + +type buildConfigInformer struct { + *sharedInformerFactory +} + +func (f *buildConfigInformer) Informer() framework.SharedIndexInformer { + f.lock.Lock() + defer f.lock.Unlock() + + informerObj := &buildapi.BuildConfig{} + informerType := reflect.TypeOf(informerObj) + informer, exists := f.informers[informerType] + if exists { + return informer + } + + informer = framework.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options kapi.ListOptions) (runtime.Object, error) { + return f.originClient.BuildConfigs(kapi.NamespaceAll).List(options) + }, + WatchFunc: func(options kapi.ListOptions) (watch.Interface, error) { + return f.originClient.BuildConfigs(kapi.NamespaceAll).Watch(options) + }, + }, + informerObj, + f.defaultResync, + cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc, oscache.ImageStreamReferenceIndex: oscache.ImageStreamReferenceIndexFunc}, + ) + f.informers[informerType] = informer + + return informer +} + +func (f *buildConfigInformer) Indexer() cache.Indexer { + informer := f.Informer() + return informer.GetIndexer() +} + +func (f *buildConfigInformer) Lister() oscache.StoreToBuildConfigLister { + informer := f.Informer() + return &oscache.StoreToBuildConfigListerImpl{Indexer: informer.GetIndexer()} +} diff --git a/pkg/controller/shared/shared_informer.go b/pkg/controller/shared/shared_informer.go index 55771b24c3cf..d5f833570791 100644 --- a/pkg/controller/shared/shared_informer.go +++ b/pkg/controller/shared/shared_informer.go @@ -32,6 +32,7 @@ type InformerFactory interface { PolicyBindings() PolicyBindingInformer DeploymentConfigs() DeploymentConfigInformer + BuildConfigs() BuildConfigInformer ImageStreams() ImageStreamInformer SecurityContextConstraints() SecurityContextConstraintsInformer ClusterResourceQuotas() ClusterResourceQuotaInformer @@ -146,6 +147,10 @@ func (f *sharedInformerFactory) DeploymentConfigs() DeploymentConfigInformer { return &deploymentConfigInformer{sharedInformerFactory: f} } +func (f *sharedInformerFactory) BuildConfigs() BuildConfigInformer { + return &buildConfigInformer{sharedInformerFactory: f} +} + func (f *sharedInformerFactory) ImageStreams() ImageStreamInformer { return &imageStreamInformer{sharedInformerFactory: f} }