diff --git a/pkg/cmd/server/origin/openshift_apiserver.go b/pkg/cmd/server/origin/openshift_apiserver.go index 83bd6ae368fe..f9e37e198daa 100644 --- a/pkg/cmd/server/origin/openshift_apiserver.go +++ b/pkg/cmd/server/origin/openshift_apiserver.go @@ -34,6 +34,8 @@ import ( osclient "github.com/openshift/origin/pkg/client" configapi "github.com/openshift/origin/pkg/cmd/server/api" "github.com/openshift/origin/pkg/cmd/server/bootstrappolicy" + oappsapiv1 "github.com/openshift/origin/pkg/deploy/apis/apps/v1" + oappsapiserver "github.com/openshift/origin/pkg/deploy/apiserver" imageadmission "github.com/openshift/origin/pkg/image/admission" imageapi "github.com/openshift/origin/pkg/image/apis/image" "github.com/openshift/origin/pkg/oc/admin/policy" @@ -51,7 +53,6 @@ import ( authzapiv1 "github.com/openshift/origin/pkg/authorization/apis/authorization/v1" buildapiv1 "github.com/openshift/origin/pkg/build/apis/build/v1" - deployapiv1 "github.com/openshift/origin/pkg/deploy/apis/apps/v1" imageapiv1 "github.com/openshift/origin/pkg/image/apis/image/v1" oauthapiv1 "github.com/openshift/origin/pkg/oauth/apis/oauth/v1" projectapiv1 "github.com/openshift/origin/pkg/project/apis/project/v1" @@ -189,6 +190,12 @@ type legacyStorageMutator interface { mutate(map[schema.GroupVersion]map[string]rest.Storage) } +type legacyStorageMutatorFunc func(map[schema.GroupVersion]map[string]rest.Storage) + +func (l legacyStorageMutatorFunc) mutate(legacyStorage map[schema.GroupVersion]map[string]rest.Storage) { + l(legacyStorage) +} + type legacyStorageMutators []legacyStorageMutator func (l legacyStorageMutators) mutate(legacyStorage map[schema.GroupVersion]map[string]rest.Storage) { @@ -207,6 +214,32 @@ func (l *legacyStorageVersionMutator) mutate(legacyStorage map[schema.GroupVersi legacyStorage[l.version] = l.storage } +func (c *completedConfig) withAppsAPIServer(delegateAPIServer genericapiserver.DelegationTarget) (genericapiserver.DelegationTarget, legacyStorageMutator, error) { + config := &oappsapiserver.AppsConfig{ + GenericConfig: c.GenericConfig, + CoreAPIServerClientConfig: c.GenericConfig.LoopbackClientConfig, + KubeletClientConfig: c.KubeletClientConfig, + Codecs: kapi.Codecs, + Registry: kapi.Registry, + Scheme: kapi.Scheme, + } + server, err := config.Complete().New(delegateAPIServer) + if err != nil { + return nil, nil, err + } + storage, err := config.V1RESTStorage() + if err != nil { + return nil, nil, err + } + server.GenericAPIServer.PrepareRun() // this triggers openapi construction + + legacyDCRollbackMutator := oappsapiserver.LegacyLegacyDCRollbackMutator{ + CoreAPIServerClientConfig: config.CoreAPIServerClientConfig, + Version: v1.SchemeGroupVersion, + } + return server.GenericAPIServer, legacyStorageMutators{legacyStorageMutatorFunc(legacyDCRollbackMutator.Mutate), &legacyStorageVersionMutator{version: oappsapiv1.SchemeGroupVersion, storage: storage}}, nil +} + func (c *completedConfig) withTemplateAPIServer(delegateAPIServer genericapiserver.DelegationTarget) (genericapiserver.DelegationTarget, legacyStorageMutator, error) { config := &templateapiserver.TemplateConfig{ GenericConfig: c.GenericConfig, @@ -255,6 +288,12 @@ func (c completedConfig) New(delegationTarget genericapiserver.DelegationTarget) var currLegacyStorageMutator legacyStorageMutator var err error + delegateAPIServer, currLegacyStorageMutator, err = c.withAppsAPIServer(delegateAPIServer) + if err != nil { + return nil, err + } + legacyStorageModifier = append(legacyStorageModifier, currLegacyStorageMutator) + delegateAPIServer, currLegacyStorageMutator, err = c.withTemplateAPIServer(delegateAPIServer) if err != nil { return nil, err @@ -321,6 +360,9 @@ func (c completedConfig) New(delegationTarget genericapiserver.DelegationTarget) // after the old-style groupified storage is created, modify the storage map to include the already migrated storage // to be included in the legacy group + if _, ok := storage[v1.SchemeGroupVersion]; !ok { + storage[v1.SchemeGroupVersion] = map[string]rest.Storage{} + } legacyStorageModifier.mutate(storage) if err := s.GenericAPIServer.InstallLegacyAPIGroup(api.Prefix, apiLegacyV1(LegacyStorage(storage))); err != nil { @@ -434,7 +476,6 @@ var apiGroupsVersions = []apiGroupInfo{ {PreferredVersion: "v1", Versions: []schema.GroupVersion{networkapiv1.SchemeGroupVersion}}, {PreferredVersion: "v1", Versions: []schema.GroupVersion{routeapiv1.SchemeGroupVersion}}, {PreferredVersion: "v1", Versions: []schema.GroupVersion{imageapiv1.SchemeGroupVersion}}, - {PreferredVersion: "v1", Versions: []schema.GroupVersion{deployapiv1.SchemeGroupVersion}}, {PreferredVersion: "v1", Versions: []schema.GroupVersion{authzapiv1.SchemeGroupVersion}}, {PreferredVersion: "v1", Versions: []schema.GroupVersion{oauthapiv1.SchemeGroupVersion}}, } diff --git a/pkg/cmd/server/origin/storage.go b/pkg/cmd/server/origin/storage.go index a2600491eff7..3b6b93ba6b34 100644 --- a/pkg/cmd/server/origin/storage.go +++ b/pkg/cmd/server/origin/storage.go @@ -29,12 +29,6 @@ import ( "github.com/openshift/origin/pkg/build/webhook/generic" "github.com/openshift/origin/pkg/build/webhook/github" "github.com/openshift/origin/pkg/build/webhook/gitlab" - deployapiv1 "github.com/openshift/origin/pkg/deploy/apis/apps/v1" - 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" "github.com/openshift/origin/pkg/dockerregistry" imageapiv1 "github.com/openshift/origin/pkg/image/apis/image/v1" imageclient "github.com/openshift/origin/pkg/image/generated/internalclientset" @@ -76,7 +70,6 @@ import ( appliedclusterresourcequotaregistry "github.com/openshift/origin/pkg/quota/registry/appliedclusterresourcequota" clusterresourcequotaetcd "github.com/openshift/origin/pkg/quota/registry/clusterresourcequota/etcd" - "github.com/openshift/origin/pkg/api/v1" "github.com/openshift/origin/pkg/authorization/registry/clusterrole" "github.com/openshift/origin/pkg/authorization/registry/clusterrolebinding" "github.com/openshift/origin/pkg/authorization/registry/localresourceaccessreview" @@ -103,10 +96,6 @@ import ( // TODO this function needs to be broken apart with each API group owning their own storage, probably with two method // per API group to give us legacy and current storage func (c OpenshiftAPIConfig) GetRestStorage() (map[schema.GroupVersion]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"}) - //TODO/REBASE use something other than c.KubeClientsetInternal nodeConnectionInfoGetter, err := kubeletclient.NewNodeConnectionInfoGetter(c.KubeClientExternal.CoreV1().Nodes(), *c.KubeletClientConfig) if err != nil { @@ -137,20 +126,6 @@ func (c OpenshiftAPIConfig) GetRestStorage() (map[schema.GroupVersion]map[string return nil, fmt.Errorf("error building REST storage: %v", err) } - deployConfigStorage, deployConfigStatusStorage, deployConfigScaleStorage, err := deployconfigetcd.NewREST(c.GenericConfig.RESTOptionsGetter) - - dcInstantiateStorage := deployconfiginstantiate.NewREST( - *deployConfigStorage.Store, - c.DeprecatedOpenshiftClient, - c.KubeClientInternal, - externalVersionCodec, - c.GenericConfig.AdmissionControl, - ) - - if err != nil { - return nil, fmt.Errorf("error building REST storage: %v", err) - } - hostSubnetStorage, err := hostsubnetetcd.NewREST(c.GenericConfig.RESTOptionsGetter) if err != nil { return nil, fmt.Errorf("error building REST storage: %v", err) @@ -325,26 +300,7 @@ func (c OpenshiftAPIConfig) GetRestStorage() (map[schema.GroupVersion]map[string return nil, fmt.Errorf("error building REST storage: %v", err) } - originAppsClient, err := oappsclient.NewForConfig(c.GenericConfig.LoopbackClientConfig) - if err != nil { - return nil, err - } - coreClient, err := kclientset.NewForConfig(c.GenericConfig.LoopbackClientConfig) - if err != nil { - return nil, err - } - deployRollbackClient := deployrollback.Client{ - GRFn: deployrollback.NewRollbackGenerator().GenerateRollback, - DeploymentConfigGetter: originAppsClient.Apps(), - ReplicationControllerGetter: coreClient.Core(), - } - deployConfigRollbackStorage := deployrollback.NewREST(c.DeprecatedOpenshiftClient, c.KubeClientInternal, externalVersionCodec) - storage := map[schema.GroupVersion]map[string]rest.Storage{ - v1.SchemeGroupVersion: { - // TODO: Deprecate these - "deploymentConfigRollbacks": deployrollback.NewDeprecatedREST(deployRollbackClient, externalVersionCodec), - }, - } + storage := map[schema.GroupVersion]map[string]rest.Storage{} storage[quotaapiv1.SchemeGroupVersion] = map[string]rest.Storage{ "clusterResourceQuotas": clusterResourceQuotaStorage, @@ -398,15 +354,6 @@ func (c OpenshiftAPIConfig) GetRestStorage() (map[schema.GroupVersion]map[string "projectRequests": projectRequestStorage, } - storage[deployapiv1.SchemeGroupVersion] = map[string]rest.Storage{ - "deploymentConfigs": deployConfigStorage, - "deploymentConfigs/scale": deployConfigScaleStorage, - "deploymentConfigs/status": deployConfigStatusStorage, - "deploymentConfigs/rollback": deployConfigRollbackStorage, - "deploymentConfigs/log": deploylogregistry.NewREST(c.DeprecatedOpenshiftClient, c.KubeClientInternal.Core(), c.KubeClientInternal.Core(), nodeConnectionInfoGetter), - "deploymentConfigs/instantiate": dcInstantiateStorage, - } - storage[imageapiv1.SchemeGroupVersion] = map[string]rest.Storage{ "images": imageStorage, "imagesignatures": imageSignatureStorage, diff --git a/pkg/deploy/apiserver/apiserver.go b/pkg/deploy/apiserver/apiserver.go new file mode 100644 index 000000000000..a9532a9e3a11 --- /dev/null +++ b/pkg/deploy/apiserver/apiserver.go @@ -0,0 +1,162 @@ +package apiserver + +import ( + "fmt" + "sync" + + "k8s.io/apimachinery/pkg/apimachinery/registered" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/runtime/serializer" + "k8s.io/apiserver/pkg/registry/rest" + genericapiserver "k8s.io/apiserver/pkg/server" + restclient "k8s.io/client-go/rest" + kapi "k8s.io/kubernetes/pkg/api" + kclientsetexternal "k8s.io/kubernetes/pkg/client/clientset_generated/clientset" + 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" + 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" +) + +// AppsConfig is a non-serializeable config for running an apps.openshift.io apiserver +type AppsConfig struct { + GenericConfig *genericapiserver.Config + + CoreAPIServerClientConfig *restclient.Config + KubeletClientConfig *kubeletclient.KubeletClientConfig + + // TODO these should all become local eventually + Scheme *runtime.Scheme + Registry *registered.APIRegistrationManager + Codecs serializer.CodecFactory + + makeV1Storage sync.Once + v1Storage map[string]rest.Storage + v1StorageErr error +} + +type AppsServer struct { + GenericAPIServer *genericapiserver.GenericAPIServer +} + +type completedConfig struct { + *AppsConfig +} + +// Complete fills in any fields not set that are required to have valid data. It's mutating the receiver. +func (c *AppsConfig) Complete() completedConfig { + c.GenericConfig.Complete() + + return completedConfig{c} +} + +// SkipComplete provides a way to construct a server instance without config completion. +func (c *AppsConfig) SkipComplete() completedConfig { + return completedConfig{c} +} + +// New returns a new instance of AppsServer from the given config. +func (c completedConfig) New(delegationTarget genericapiserver.DelegationTarget) (*AppsServer, error) { + // completion is done in Complete, no need for a second time + genericServer, err := c.AppsConfig.GenericConfig.SkipComplete().New("apps.openshift.io-apiserver", delegationTarget) + if err != nil { + return nil, err + } + + s := &AppsServer{ + GenericAPIServer: genericServer, + } + + v1Storage, err := c.V1RESTStorage() + if err != nil { + return nil, err + } + + apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(appsapiv1.GroupName, c.Registry, c.Scheme, metav1.ParameterCodec, c.Codecs) + apiGroupInfo.GroupMeta.GroupVersion = appsapiv1.SchemeGroupVersion + apiGroupInfo.VersionedResourcesStorageMap[appsapiv1.SchemeGroupVersion.Version] = v1Storage + if err := s.GenericAPIServer.InstallAPIGroup(&apiGroupInfo); err != nil { + return nil, err + } + + return s, nil +} + +func (c *AppsConfig) V1RESTStorage() (map[string]rest.Storage, error) { + c.makeV1Storage.Do(func() { + c.v1Storage, c.v1StorageErr = c.newV1RESTStorage() + }) + + return c.v1Storage, c.v1StorageErr +} + +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) + if err != nil { + return nil, err + } + kubeInternalClient, err := kclientsetinternal.NewForConfig(c.CoreAPIServerClientConfig) + if err != nil { + return nil, err + } + kubeExternalClient, err := kclientsetexternal.NewForConfig(c.CoreAPIServerClientConfig) + if err != nil { + return nil, err + } + nodeConnectionInfoGetter, err := kubeletclient.NewNodeConnectionInfoGetter(kubeExternalClient.CoreV1().Nodes(), *c.KubeletClientConfig) + if err != nil { + return nil, fmt.Errorf("unable to configure the node connection info getter: %v", err) + } + + deployConfigStorage, deployConfigStatusStorage, deployConfigScaleStorage, err := deployconfigetcd.NewREST(c.GenericConfig.RESTOptionsGetter) + if err != nil { + return nil, err + } + dcInstantiateStorage := deployconfiginstantiate.NewREST( + *deployConfigStorage.Store, + deprecatedOpenshiftClient, + kubeInternalClient, + externalVersionCodec, + c.GenericConfig.AdmissionControl, + ) + deployConfigRollbackStorage := deployrollback.NewREST(deprecatedOpenshiftClient, 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/instantiate"] = dcInstantiateStorage + return v1Storage, nil +} + +// LegacyLegacyDCRollbackMutator allows us to inject a one-off endpoint into oapi +type LegacyLegacyDCRollbackMutator struct { + CoreAPIServerClientConfig *restclient.Config + Version schema.GroupVersion +} + +func (l LegacyLegacyDCRollbackMutator) Mutate(legacyStorage map[schema.GroupVersion]map[string]rest.Storage) { + externalVersionCodec := kapi.Codecs.LegacyCodec(schema.GroupVersion{Group: "", Version: "v1"}) + originAppsClient := oappsclient.NewForConfigOrDie(l.CoreAPIServerClientConfig) + kubeInternalClient := kclientsetinternal.NewForConfigOrDie(l.CoreAPIServerClientConfig) + deployRollbackClient := deployrollback.Client{ + GRFn: deployrollback.NewRollbackGenerator().GenerateRollback, + DeploymentConfigGetter: originAppsClient.Apps(), + ReplicationControllerGetter: kubeInternalClient.Core(), + } + // TODO: Deprecate this + legacyStorage[l.Version]["deploymentConfigRollbacks"] = deployrollback.NewDeprecatedREST(deployRollbackClient, externalVersionCodec) +}