Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

apps: switch to use generated internal clientset #16100

Merged
merged 3 commits into from
Sep 5, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions pkg/cmd/infra/deployer/deployer.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ import (
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"

"github.com/openshift/origin/pkg/client"
"github.com/openshift/origin/pkg/cmd/util"
deployapi "github.com/openshift/origin/pkg/deploy/apis/apps"
"github.com/openshift/origin/pkg/deploy/strategy"
"github.com/openshift/origin/pkg/deploy/strategy/recreate"
"github.com/openshift/origin/pkg/deploy/strategy/rolling"
deployutil "github.com/openshift/origin/pkg/deploy/util"
imageclientinternal "github.com/openshift/origin/pkg/image/generated/internalclientset"
ocmd "github.com/openshift/origin/pkg/oc/cli/cmd"
)

Expand Down Expand Up @@ -107,17 +107,17 @@ func (cfg *config) RunDeployer() error {
if err != nil {
return err
}
oc, err := client.New(kcfg)
openshiftInternalImageClient, err := imageclientinternal.NewForConfig(kcfg)
if err != nil {
return err
}

deployer := NewDeployer(kc, oc, cfg.Out, cfg.ErrOut, cfg.Until)
deployer := NewDeployer(kc, openshiftInternalImageClient, cfg.Out, cfg.ErrOut, cfg.Until)
return deployer.Deploy(cfg.Namespace, cfg.rcName)
}

// NewDeployer makes a new Deployer from a kube client.
func NewDeployer(client kclientset.Interface, oclient client.Interface, out, errOut io.Writer, until string) *Deployer {
func NewDeployer(client kclientset.Interface, images imageclientinternal.Interface, out, errOut io.Writer, until string) *Deployer {
scaler, _ := kubectl.ScalerFor(kapi.Kind("ReplicationController"), client)
return &Deployer{
out: out,
Expand All @@ -133,10 +133,10 @@ func NewDeployer(client kclientset.Interface, oclient client.Interface, out, err
strategyFor: func(config *deployapi.DeploymentConfig) (strategy.DeploymentStrategy, error) {
switch config.Spec.Strategy.Type {
case deployapi.DeploymentStrategyTypeRecreate:
return recreate.NewRecreateDeploymentStrategy(client, oclient, &kv1core.EventSinkImpl{Interface: kv1core.New(client.Core().RESTClient()).Events("")}, kapi.Codecs.UniversalDecoder(), out, errOut, until), nil
return recreate.NewRecreateDeploymentStrategy(client, images.Image(), &kv1core.EventSinkImpl{Interface: kv1core.New(client.Core().RESTClient()).Events("")}, kapi.Codecs.UniversalDecoder(), out, errOut, until), nil
case deployapi.DeploymentStrategyTypeRolling:
recreate := recreate.NewRecreateDeploymentStrategy(client, oclient, &kv1core.EventSinkImpl{Interface: kv1core.New(client.Core().RESTClient()).Events("")}, kapi.Codecs.UniversalDecoder(), out, errOut, until)
return rolling.NewRollingDeploymentStrategy(config.Namespace, client, oclient, &kv1core.EventSinkImpl{Interface: kv1core.New(client.Core().RESTClient()).Events("")}, kapi.Codecs.UniversalDecoder(), recreate, out, errOut, until), nil
recreate := recreate.NewRecreateDeploymentStrategy(client, images.Image(), &kv1core.EventSinkImpl{Interface: kv1core.New(client.Core().RESTClient()).Events("")}, kapi.Codecs.UniversalDecoder(), out, errOut, until)
return rolling.NewRollingDeploymentStrategy(config.Namespace, client, images.Image(), &kv1core.EventSinkImpl{Interface: kv1core.New(client.Core().RESTClient()).Events("")}, kapi.Codecs.UniversalDecoder(), recreate, out, errOut, until), nil
default:
return nil, fmt.Errorf("unsupported strategy type: %s", config.Spec.Strategy.Type)
}
Expand Down
13 changes: 2 additions & 11 deletions pkg/cmd/server/origin/controller/apps.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,11 @@ func (c *DeploymentConfigControllerConfig) RunController(ctx ControllerContext)
if err != nil {
return true, err
}
deprecatedOcDcClient, err := ctx.ClientBuilder.DeprecatedOpenshiftClient(saName)
if err != nil {
return true, err
}

go deployconfigcontroller.NewDeploymentConfigController(
ctx.AppInformers.Apps().InternalVersion().DeploymentConfigs().Informer(),
ctx.ExternalKubeInformers.Core().V1().ReplicationControllers(),
deprecatedOcDcClient,
ctx.ClientBuilder.OpenshiftInternalAppsClientOrDie(saName),
kubeClient,
c.Codec,
).Run(5, ctx.Stop)
Expand All @@ -70,16 +66,11 @@ func (c *DeploymentConfigControllerConfig) RunController(ctx ControllerContext)
func (c *DeploymentTriggerControllerConfig) RunController(ctx ControllerContext) (bool, error) {
saName := bootstrappolicy.InfraDeploymentTriggerControllerServiceAccountName

deprecatedOcTriggerClient, err := ctx.ClientBuilder.DeprecatedOpenshiftClient(saName)
if err != nil {
return true, err
}

go triggercontroller.NewDeploymentTriggerController(
ctx.AppInformers.Apps().InternalVersion().DeploymentConfigs().Informer(),
ctx.ExternalKubeInformers.Core().V1().ReplicationControllers().Informer(),
ctx.ImageInformers.Image().InternalVersion().ImageStreams().Informer(),
deprecatedOcTriggerClient,
ctx.ClientBuilder.OpenshiftInternalAppsClientOrDie(saName),
c.Codec,
).Run(5, ctx.Stop)

Expand Down
4 changes: 2 additions & 2 deletions pkg/cmd/server/origin/controller/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ type ImageImportControllerConfig struct {
func (c *ImageImportControllerConfig) RunController(ctx ControllerContext) (bool, error) {
informer := ctx.ImageInformers.Image().InternalVersion().ImageStreams()
controller := imagecontroller.NewImageStreamController(
ctx.ClientBuilder.OpenshiftImageClientOrDie(bootstrappolicy.InfraImageImportControllerServiceAccountName),
ctx.ClientBuilder.OpenshiftInternalImageClientOrDie(bootstrappolicy.InfraImageImportControllerServiceAccountName),
informer,
)
go controller.Run(5, ctx.Stop)
Expand All @@ -162,7 +162,7 @@ func (c *ImageImportControllerConfig) RunController(ctx ControllerContext) (bool
}

scheduledController := imagecontroller.NewScheduledImageStreamController(
ctx.ClientBuilder.OpenshiftImageClientOrDie(bootstrappolicy.InfraImageImportControllerServiceAccountName),
ctx.ClientBuilder.OpenshiftInternalImageClientOrDie(bootstrappolicy.InfraImageImportControllerServiceAccountName),
informer,
imagecontroller.ScheduledImageStreamControllerOptions{
Resync: time.Duration(c.ScheduledImageImportMinimumIntervalSeconds) * time.Second,
Expand Down
67 changes: 55 additions & 12 deletions pkg/cmd/server/origin/controller/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ import (
buildinformer "github.com/openshift/origin/pkg/build/generated/informers/internalversion"
osclient "github.com/openshift/origin/pkg/client"
appinformer "github.com/openshift/origin/pkg/deploy/generated/informers/internalversion"
appsclientinternal "github.com/openshift/origin/pkg/deploy/generated/internalclientset"
imageinformer "github.com/openshift/origin/pkg/image/generated/informers/internalversion"
imageclient "github.com/openshift/origin/pkg/image/generated/internalclientset"
imageclientinternal "github.com/openshift/origin/pkg/image/generated/internalclientset"
quotainformer "github.com/openshift/origin/pkg/quota/generated/informers/internalversion"
securityinformer "github.com/openshift/origin/pkg/security/generated/informers/internalversion"
templateinformer "github.com/openshift/origin/pkg/template/generated/informers/internalversion"
Expand Down Expand Up @@ -50,12 +51,20 @@ type ControllerClientBuilder interface {
controller.ControllerClientBuilder
KubeInternalClient(name string) (kclientsetinternal.Interface, error)
KubeInternalClientOrDie(name string) kclientsetinternal.Interface

// Legacy OpenShift client (pkg/client)
DeprecatedOpenshiftClient(name string) (osclient.Interface, error)
DeprecatedOpenshiftClientOrDie(name string) osclient.Interface
OpenshiftTemplateClient(name string) (templateclient.Interface, error)
OpenshiftTemplateClientOrDie(name string) templateclient.Interface
OpenshiftImageClient(name string) (imageclient.Interface, error)
OpenshiftImageClientOrDie(name string) imageclient.Interface

// OpenShift clients based on generated internal clientsets
OpenshiftInternalTemplateClient(name string) (templateclient.Interface, error)
OpenshiftInternalTemplateClientOrDie(name string) templateclient.Interface

OpenshiftInternalImageClient(name string) (imageclientinternal.Interface, error)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@deads2k @soltysh minor, but I like this naming scheme better:

OpenShift[Internal|External]Client

It matches the kube client scheme and allow us to add external clients in future...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@deads2k @soltysh minor, but I like this naming scheme better:

OpenShift[Internal|External]Client

It matches the kube client scheme and allow us to add external clients in future...

ok

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On a side not, I still wonder if we need both *Client and *ClientOrDie if we use the latter in all places that I've searched for.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@soltysh the apiserver use the *Client version to report errors properly, we need both.

OpenshiftInternalImageClientOrDie(name string) imageclientinternal.Interface

OpenshiftInternalAppsClient(name string) (appsclientinternal.Interface, error)
OpenshiftInternalAppsClientOrDie(name string) appsclientinternal.Interface
}

// InitFunc is used to launch a particular controller. It may run additional "should I activate checks".
Expand Down Expand Up @@ -99,32 +108,66 @@ func (b OpenshiftControllerClientBuilder) DeprecatedOpenshiftClientOrDie(name st
return client
}

func (b OpenshiftControllerClientBuilder) OpenshiftTemplateClient(name string) (templateclient.Interface, error) {
// OpenshiftInternalTemplateClient provides a REST client for the template API.
// If the client cannot be created because of configuration error, this function
// will return an error.
func (b OpenshiftControllerClientBuilder) OpenshiftInternalTemplateClient(name string) (templateclient.Interface, error) {
clientConfig, err := b.Config(name)
if err != nil {
return nil, err
}
return templateclient.NewForConfig(clientConfig)
}

func (b OpenshiftControllerClientBuilder) OpenshiftTemplateClientOrDie(name string) templateclient.Interface {
client, err := b.OpenshiftTemplateClient(name)
// OpenshiftInternalTemplateClientOrDie provides a REST client for the template API.
// If the client cannot be created because of configuration error, this function
// will panic.
func (b OpenshiftControllerClientBuilder) OpenshiftInternalTemplateClientOrDie(name string) templateclient.Interface {
client, err := b.OpenshiftInternalTemplateClient(name)
if err != nil {
glog.Fatal(err)
}
return client
}

// OpenshiftInternalImageClient provides a REST client for the image API.
// If the client cannot be created because of configuration error, this function
// will error.
func (b OpenshiftControllerClientBuilder) OpenshiftInternalImageClient(name string) (imageclientinternal.Interface, error) {
clientConfig, err := b.Config(name)
if err != nil {
return nil, err
}
return imageclientinternal.NewForConfig(clientConfig)
}

// OpenshiftInternalImageClientOrDie provides a REST client for the image API.
// If the client cannot be created because of configuration error, this function
// will panic.
func (b OpenshiftControllerClientBuilder) OpenshiftInternalImageClientOrDie(name string) imageclientinternal.Interface {
client, err := b.OpenshiftInternalImageClient(name)
if err != nil {
glog.Fatal(err)
}
return client
}

func (b OpenshiftControllerClientBuilder) OpenshiftImageClient(name string) (imageclient.Interface, error) {
// OpenshiftInternalAppsClient provides a REST client for the apps API.
// If the client cannot be created because of configuration error, this function
// will error.
func (b OpenshiftControllerClientBuilder) OpenshiftInternalAppsClient(name string) (appsclientinternal.Interface, error) {
clientConfig, err := b.Config(name)
if err != nil {
return nil, err
}
return imageclient.NewForConfig(clientConfig)
return appsclientinternal.NewForConfig(clientConfig)
}

func (b OpenshiftControllerClientBuilder) OpenshiftImageClientOrDie(name string) imageclient.Interface {
client, err := b.OpenshiftImageClient(name)
// OpenshiftInternalAppsClientOrDie provides a REST client for the apps API.
// If the client cannot be created because of configuration error, this function
// will panic.
func (b OpenshiftControllerClientBuilder) OpenshiftInternalAppsClientOrDie(name string) appsclientinternal.Interface {
client, err := b.OpenshiftInternalAppsClient(name)
if err != nil {
glog.Fatal(err)
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/cmd/server/origin/controller/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func RunTemplateInstanceController(ctx ControllerContext) (bool, error) {
restConfig,
ctx.ClientBuilder.DeprecatedOpenshiftClientOrDie(saName),
ctx.ClientBuilder.KubeInternalClientOrDie(saName),
ctx.ClientBuilder.OpenshiftTemplateClientOrDie(saName),
ctx.ClientBuilder.OpenshiftInternalTemplateClientOrDie(saName),
ctx.TemplateInformers.Template().InternalVersion().TemplateInstances(),
).Run(5, ctx.Stop)

Expand Down
16 changes: 11 additions & 5 deletions pkg/deploy/apiserver/apiserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,14 @@ import (
kclientsetinternal "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
kubeletclient "k8s.io/kubernetes/pkg/kubelet/client"

osclient "github.com/openshift/origin/pkg/client"
appsapiv1 "github.com/openshift/origin/pkg/deploy/apis/apps/v1"
appsclientinternal "github.com/openshift/origin/pkg/deploy/generated/internalclientset"
oappsclient "github.com/openshift/origin/pkg/deploy/generated/internalclientset"
deployconfigetcd "github.com/openshift/origin/pkg/deploy/registry/deployconfig/etcd"
deploylogregistry "github.com/openshift/origin/pkg/deploy/registry/deploylog"
deployconfiginstantiate "github.com/openshift/origin/pkg/deploy/registry/instantiate"
deployrollback "github.com/openshift/origin/pkg/deploy/registry/rollback"
imageclientinternal "github.com/openshift/origin/pkg/image/generated/internalclientset"
)

// AppsConfig is a non-serializeable config for running an apps.openshift.io apiserver
Expand Down Expand Up @@ -102,7 +103,12 @@ func (c *AppsConfig) newV1RESTStorage() (map[string]rest.Storage, error) {
// TODO sort out who is using this and why. it was hardcoded before the migration and I suspect that it is being used
// to serialize out objects into annotations.
externalVersionCodec := kapi.Codecs.LegacyCodec(schema.GroupVersion{Group: "", Version: "v1"})
deprecatedOpenshiftClient, err := osclient.New(c.GenericConfig.LoopbackClientConfig)
openshiftInternalAppsClient, err := appsclientinternal.NewForConfig(c.GenericConfig.LoopbackClientConfig)
if err != nil {
return nil, err
}
// This client is using the core api server client config, since the apps server doesn't host images
openshiftInternalImageClient, err := imageclientinternal.NewForConfig(c.CoreAPIServerClientConfig)
if err != nil {
return nil, err
}
Expand All @@ -125,19 +131,19 @@ func (c *AppsConfig) newV1RESTStorage() (map[string]rest.Storage, error) {
}
dcInstantiateStorage := deployconfiginstantiate.NewREST(
*deployConfigStorage.Store,
deprecatedOpenshiftClient,
openshiftInternalImageClient,
kubeInternalClient,
externalVersionCodec,
c.GenericConfig.AdmissionControl,
)
deployConfigRollbackStorage := deployrollback.NewREST(deprecatedOpenshiftClient, kubeInternalClient, externalVersionCodec)
deployConfigRollbackStorage := deployrollback.NewREST(openshiftInternalAppsClient, kubeInternalClient, externalVersionCodec)

v1Storage := map[string]rest.Storage{}
v1Storage["deploymentConfigs"] = deployConfigStorage
v1Storage["deploymentConfigs/scale"] = deployConfigScaleStorage
v1Storage["deploymentConfigs/status"] = deployConfigStatusStorage
v1Storage["deploymentConfigs/rollback"] = deployConfigRollbackStorage
v1Storage["deploymentConfigs/log"] = deploylogregistry.NewREST(deprecatedOpenshiftClient, kubeInternalClient.Core(), kubeInternalClient.Core(), nodeConnectionInfoGetter)
v1Storage["deploymentConfigs/log"] = deploylogregistry.NewREST(openshiftInternalAppsClient, kubeInternalClient.Core(), kubeInternalClient.Core(), nodeConnectionInfoGetter)
v1Storage["deploymentConfigs/instantiate"] = dcInstantiateStorage
return v1Storage, nil
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ import (
"k8s.io/kubernetes/pkg/client/retry"
kcontroller "k8s.io/kubernetes/pkg/controller"

osclient "github.com/openshift/origin/pkg/client"
oscache "github.com/openshift/origin/pkg/client/cache"
deployapi "github.com/openshift/origin/pkg/deploy/apis/apps"
appsclient "github.com/openshift/origin/pkg/deploy/generated/internalclientset/typed/apps/internalversion"
deployutil "github.com/openshift/origin/pkg/deploy/util"
)

Expand Down Expand Up @@ -56,7 +56,7 @@ func (e fatalError) Error() string {
// running deployments.
type DeploymentConfigController struct {
// dn provides access to deploymentconfigs.
dn osclient.DeploymentConfigsNamespacer
dn appsclient.DeploymentConfigsGetter
// rn provides access to replication controllers.
rn kcoreclient.ReplicationControllersGetter

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ import (
"k8s.io/kubernetes/pkg/client/clientset_generated/clientset/fake"
kinformers "k8s.io/kubernetes/pkg/client/informers/informers_generated/externalversions"

"github.com/openshift/origin/pkg/client/testclient"
deployapi "github.com/openshift/origin/pkg/deploy/apis/apps"
_ "github.com/openshift/origin/pkg/deploy/apis/apps/install"
deploytest "github.com/openshift/origin/pkg/deploy/apis/apps/test"
deployv1 "github.com/openshift/origin/pkg/deploy/apis/apps/v1"
appsfake "github.com/openshift/origin/pkg/deploy/generated/internalclientset/fake"
deployutil "github.com/openshift/origin/pkg/deploy/util"
)

Expand Down Expand Up @@ -347,7 +347,7 @@ func TestHandleScenarios(t *testing.T) {
toStore = append(toStore, deployment)
}

oc := &testclient.Fake{}
oc := &appsfake.Clientset{}
oc.AddReactor("update", "deploymentconfigs", func(action clientgotesting.Action) (handled bool, ret runtime.Object, err error) {
dc := action.(clientgotesting.UpdateAction).GetObject().(*deployapi.DeploymentConfig)
updatedConfig = dc
Expand All @@ -369,10 +369,10 @@ func TestHandleScenarios(t *testing.T) {
dcInformer := cache.NewSharedIndexInformer(
&cache.ListWatch{
ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
return oc.DeploymentConfigs(metav1.NamespaceAll).List(options)
return oc.Apps().DeploymentConfigs(metav1.NamespaceAll).List(options)
},
WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
return oc.DeploymentConfigs(metav1.NamespaceAll).Watch(options)
return oc.Apps().DeploymentConfigs(metav1.NamespaceAll).Watch(options)
},
},
&deployapi.DeploymentConfig{},
Expand Down
6 changes: 3 additions & 3 deletions pkg/deploy/controller/deploymentconfig/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ import (
kcoreinformers "k8s.io/kubernetes/pkg/client/informers/informers_generated/externalversions/core/v1"
kcontroller "k8s.io/kubernetes/pkg/controller"

osclient "github.com/openshift/origin/pkg/client"
deployapi "github.com/openshift/origin/pkg/deploy/apis/apps"
appsclient "github.com/openshift/origin/pkg/deploy/generated/internalclientset"
)

const (
Expand All @@ -34,7 +34,7 @@ const (
func NewDeploymentConfigController(
dcInformer cache.SharedIndexInformer,
rcInformer kcoreinformers.ReplicationControllerInformer,
oc osclient.Interface,
appsClientset appsclient.Interface,
kubeClientset kclientset.Interface,
codec runtime.Codec,
) *DeploymentConfigController {
Expand All @@ -43,7 +43,7 @@ func NewDeploymentConfigController(
recorder := eventBroadcaster.NewRecorder(kapi.Scheme, kclientv1.EventSource{Component: "deploymentconfig-controller"})

c := &DeploymentConfigController{
dn: oc,
dn: appsClientset.Apps(),
rn: kubeClientset.Core(),

queue: workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter()),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import (
kcorelister "k8s.io/kubernetes/pkg/client/listers/core/v1"

"github.com/golang/glog"
osclient "github.com/openshift/origin/pkg/client"
oscache "github.com/openshift/origin/pkg/client/cache"
deployapi "github.com/openshift/origin/pkg/deploy/apis/apps"
appsclient "github.com/openshift/origin/pkg/deploy/generated/internalclientset/typed/apps/internalversion"
)

const (
Expand All @@ -26,7 +26,7 @@ type DeploymentTriggerController struct {
triggerFromImages bool

// dn is used to update deployment configs.
dn osclient.DeploymentConfigsNamespacer
dn appsclient.DeploymentConfigsGetter

// queue contains deployment configs that need to be synced.
queue workqueue.RateLimitingInterface
Expand Down Expand Up @@ -59,7 +59,7 @@ func (c *DeploymentTriggerController) Handle(config *deployapi.DeploymentConfig)
request.ExcludeTriggers = []deployapi.DeploymentTriggerType{deployapi.DeploymentTriggerOnImageChange}
}

_, err := c.dn.DeploymentConfigs(config.Namespace).Instantiate(request)
_, err := c.dn.DeploymentConfigs(config.Namespace).Instantiate(config.Name, request)
return err
}

Expand Down
Loading