Skip to content

Commit e52ebc1

Browse files
committed
MFOJTIK: make project request endpoint use dynamic client instead of kubectl based client
1 parent 27b9ab6 commit e52ebc1

File tree

5 files changed

+90
-70
lines changed

5 files changed

+90
-70
lines changed

pkg/cmd/server/origin/admission/plugin_initializer.go

+14-32
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"fmt"
55
"io/ioutil"
66
"os"
7-
"time"
87

98
authorizationclient "github.com/openshift/client-go/authorization/clientset/versioned"
109
buildclient "github.com/openshift/client-go/build/clientset/versioned"
@@ -27,15 +26,11 @@ import (
2726
"k8s.io/apimachinery/pkg/api/meta"
2827
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2928
"k8s.io/apimachinery/pkg/runtime/schema"
30-
"k8s.io/apimachinery/pkg/util/wait"
3129
"k8s.io/apiserver/pkg/admission"
3230
"k8s.io/apiserver/pkg/admission/initializer"
3331
webhookconfig "k8s.io/apiserver/pkg/admission/plugin/webhook/config"
3432
webhookinitializer "k8s.io/apiserver/pkg/admission/plugin/webhook/initializer"
3533
"k8s.io/apiserver/pkg/authorization/authorizer"
36-
genericapiserver "k8s.io/apiserver/pkg/server"
37-
"k8s.io/client-go/discovery"
38-
cacheddiscovery "k8s.io/client-go/discovery/cached"
3934
kexternalinformers "k8s.io/client-go/informers"
4035
kubeclientgoclient "k8s.io/client-go/kubernetes"
4136
"k8s.io/client-go/rest"
@@ -63,39 +58,40 @@ func NewPluginInitializer(
6358
informers InformerAccess,
6459
authorizer authorizer.Authorizer,
6560
projectCache *projectcache.ProjectCache,
61+
restMapper meta.RESTMapper,
6662
clusterQuotaMappingController *clusterquotamapping.ClusterQuotaMappingController,
67-
) (admission.PluginInitializer, genericapiserver.PostStartHookFunc, error) {
63+
) (admission.PluginInitializer, error) {
6864
kubeInternalClient, err := kclientsetinternal.NewForConfig(privilegedLoopbackConfig)
6965
if err != nil {
70-
return nil, nil, err
66+
return nil, err
7167
}
7268
kubeClientGoClientSet, err := kubeclientgoclient.NewForConfig(privilegedLoopbackConfig)
7369
if err != nil {
74-
return nil, nil, err
70+
return nil, err
7571
}
7672
authorizationClient, err := authorizationclient.NewForConfig(privilegedLoopbackConfig)
7773
if err != nil {
78-
return nil, nil, err
74+
return nil, err
7975
}
8076
buildClient, err := buildclient.NewForConfig(privilegedLoopbackConfig)
8177
if err != nil {
82-
return nil, nil, err
78+
return nil, err
8379
}
8480
imageClient, err := imageclient.NewForConfig(privilegedLoopbackConfig)
8581
if err != nil {
86-
return nil, nil, err
82+
return nil, err
8783
}
8884
quotaClient, err := quotaclient.NewForConfig(privilegedLoopbackConfig)
8985
if err != nil {
90-
return nil, nil, err
86+
return nil, err
9187
}
9288
templateClient, err := templateclient.NewForConfig(privilegedLoopbackConfig)
9389
if err != nil {
94-
return nil, nil, err
90+
return nil, err
9591
}
9692
userClient, err := userclient.NewForConfig(privilegedLoopbackConfig)
9793
if err != nil {
98-
return nil, nil, err
94+
return nil, err
9995
}
10096

10197
// TODO make a union registry
@@ -112,26 +108,22 @@ func NewPluginInitializer(
112108
svcCache := service.NewServiceResolverCache(kubeInternalClient.Core().Services(metav1.NamespaceDefault).Get)
113109
defaultRegistryFunc, err := svcCache.Defer(defaultRegistry)
114110
if err != nil {
115-
return nil, nil, fmt.Errorf("OPENSHIFT_DEFAULT_REGISTRY variable is invalid %q: %v", defaultRegistry, err)
111+
return nil, fmt.Errorf("OPENSHIFT_DEFAULT_REGISTRY variable is invalid %q: %v", defaultRegistry, err)
116112
}
117113

118-
// Use a discovery client capable of being refreshed.
119-
discoveryClient := cacheddiscovery.NewMemCacheClient(kubeInternalClient.Discovery())
120-
restMapper := discovery.NewDeferredDiscoveryRESTMapper(discoveryClient, meta.InterfacesForUnstructured)
121-
122114
// punch through layers to build this in order to get a string for a cloud provider file
123115
// TODO refactor us into a forward building flow with a side channel like this
124116
kubeOptions, err := kubernetes.BuildKubeAPIserverOptions(options)
125117
if err != nil {
126-
return nil, nil, err
118+
return nil, err
127119
}
128120

129121
var cloudConfig []byte
130122
if kubeOptions.CloudProvider.CloudConfigFile != "" {
131123
var err error
132124
cloudConfig, err = ioutil.ReadFile(kubeOptions.CloudProvider.CloudConfigFile)
133125
if err != nil {
134-
return nil, nil, fmt.Errorf("Error reading from cloud configuration file %s: %v", kubeOptions.CloudProvider.CloudConfigFile, err)
126+
return nil, fmt.Errorf("Error reading from cloud configuration file %s: %v", kubeOptions.CloudProvider.CloudConfigFile, err)
135127
}
136128
}
137129
// note: we are passing a combined quota registry here...
@@ -190,17 +182,7 @@ func NewPluginInitializer(
190182
UserInformers: informers.GetUserInformers(),
191183
}
192184

193-
return admission.PluginInitializers{genericInitializer, webhookInitializer, kubePluginInitializer, openshiftPluginInitializer},
194-
func(context genericapiserver.PostStartHookContext) error {
195-
restMapper.Reset()
196-
go func() {
197-
wait.Until(func() {
198-
restMapper.Reset()
199-
}, 10*time.Second, context.StopCh)
200-
}()
201-
return nil
202-
},
203-
nil
185+
return admission.PluginInitializers{genericInitializer, webhookInitializer, kubePluginInitializer, openshiftPluginInitializer}, nil
204186
}
205187

206188
// env returns an environment variable, or the defaultValue if it is not set.

pkg/cmd/server/origin/master_config.go

+18-2
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@ package origin
22

33
import (
44
"fmt"
5+
"time"
56

67
"github.com/golang/glog"
78
"github.com/openshift/origin/pkg/admission/namespaceconditions"
9+
"k8s.io/apimachinery/pkg/api/meta"
10+
"k8s.io/apimachinery/pkg/util/wait"
11+
"k8s.io/client-go/restmapper"
812

913
kapierrors "k8s.io/apimachinery/pkg/api/errors"
1014
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -71,6 +75,7 @@ type MasterConfig struct {
7175
ProjectCache *projectcache.ProjectCache
7276
ClusterQuotaMappingController *clusterquotamapping.ClusterQuotaMappingController
7377
LimitVerifier imageadmission.LimitVerifier
78+
RESTMapper meta.RESTMapper
7479

7580
// RegistryHostnameRetriever retrieves the name of the integrated registry, or false if no such registry
7681
// is available.
@@ -162,7 +167,9 @@ func BuildMasterConfig(
162167
return nil, err
163168
}
164169
clusterQuotaMappingController := newClusterQuotaMappingController(informers)
165-
admissionInitializer, admissionPostStartHook, err := originadmission.NewPluginInitializer(options, privilegedLoopbackConfig, informers, authorizer, projectCache, clusterQuotaMappingController)
170+
discoveryClient := cacheddiscovery.NewMemCacheClient(kubeInternalClient.Discovery())
171+
restMapper := restmapper.NewDeferredDiscoveryRESTMapper(discoveryClient)
172+
admissionInitializer, err := originadmission.NewPluginInitializer(options, privilegedLoopbackConfig, informers, authorizer, projectCache, restMapper, clusterQuotaMappingController)
166173
if err != nil {
167174
return nil, err
168175
}
@@ -199,7 +206,15 @@ func BuildMasterConfig(
199206

200207
kubeAPIServerConfig: kubeAPIServerConfig,
201208
additionalPostStartHooks: map[string]genericapiserver.PostStartHookFunc{
202-
"openshift.io-AdmissionInit": admissionPostStartHook,
209+
"openshift.io-RESTMapper": func(context genericapiserver.PostStartHookContext) error {
210+
restMapper.Reset()
211+
go func() {
212+
wait.Until(func() {
213+
restMapper.Reset()
214+
}, 10*time.Second, context.StopCh)
215+
}()
216+
return nil
217+
},
203218
"openshift.io-StartInformers": func(context genericapiserver.PostStartHookContext) error {
204219
informers.Start(context.StopCh)
205220
return nil
@@ -218,6 +233,7 @@ func BuildMasterConfig(
218233
),
219234
ProjectCache: projectCache,
220235
ClusterQuotaMappingController: clusterQuotaMappingController,
236+
RESTMapper: restMapper,
221237

222238
RegistryHostnameRetriever: imageapi.DefaultRegistryHostnameRetriever(defaultRegistryFunc, options.ImagePolicyConfig.ExternalRegistryHostname, options.ImagePolicyConfig.InternalRegistryHostname),
223239

pkg/cmd/server/origin/openshift_apiserver.go

+6
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99

1010
restful "github.com/emicklei/go-restful"
1111
"github.com/golang/glog"
12+
"k8s.io/apimachinery/pkg/api/meta"
1213

1314
kapierror "k8s.io/apimachinery/pkg/api/errors"
1415
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -101,6 +102,7 @@ type OpenshiftAPIExtraConfig struct {
101102
ProjectCache *projectcache.ProjectCache
102103
ProjectRequestTemplate string
103104
ProjectRequestMessage string
105+
RESTMapper meta.RESTMapper
104106

105107
// oauth API server
106108
ServiceAccountMethod configapi.GrantHandlerType
@@ -155,6 +157,9 @@ func (c *OpenshiftAPIExtraConfig) Validate() error {
155157
if c.ClusterQuotaMappingController == nil {
156158
ret = append(ret, fmt.Errorf("ClusterQuotaMappingController is required"))
157159
}
160+
if c.RESTMapper == nil {
161+
ret = append(ret, fmt.Errorf("RESTMapper is required"))
162+
}
158163

159164
return utilerrors.NewAggregate(ret)
160165
}
@@ -369,6 +374,7 @@ func (c *completedConfig) withProjectAPIServer(delegateAPIServer genericapiserve
369374
ProjectCache: c.ExtraConfig.ProjectCache,
370375
ProjectRequestTemplate: c.ExtraConfig.ProjectRequestTemplate,
371376
ProjectRequestMessage: c.ExtraConfig.ProjectRequestMessage,
377+
RESTMapper: c.ExtraConfig.RESTMapper,
372378
Codecs: legacyscheme.Codecs,
373379
Scheme: legacyscheme.Scheme,
374380
},

pkg/project/apiserver/apiserver.go

+9-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import (
44
"sync"
55

66
"github.com/golang/glog"
7+
"k8s.io/apimachinery/pkg/api/meta"
8+
"k8s.io/client-go/dynamic"
79

810
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
911
"k8s.io/apimachinery/pkg/runtime"
@@ -32,6 +34,7 @@ type ExtraConfig struct {
3234
ProjectCache *projectcache.ProjectCache
3335
ProjectRequestTemplate string
3436
ProjectRequestMessage string
37+
RESTMapper meta.RESTMapper
3538

3639
// TODO these should all become local eventually
3740
Scheme *runtime.Scheme
@@ -122,6 +125,10 @@ func (c *completedConfig) newV1RESTStorage() (map[string]rest.Storage, error) {
122125
if err != nil {
123126
return nil, err
124127
}
128+
dynamicClient, err := dynamic.NewForConfig(c.ExtraConfig.KubeAPIServerClientConfig)
129+
if err != nil {
130+
return nil, err
131+
}
125132

126133
projectStorage := projectproxy.NewREST(kubeInternalClient.Core().Namespaces(), c.ExtraConfig.ProjectAuthorizationCache, c.ExtraConfig.ProjectAuthorizationCache, c.ExtraConfig.ProjectCache)
127134

@@ -137,7 +144,8 @@ func (c *completedConfig) newV1RESTStorage() (map[string]rest.Storage, error) {
137144
projectClient.Project(),
138145
templateClient,
139146
authorizationClient.SubjectAccessReviews(),
140-
c.ExtraConfig.KubeAPIServerClientConfig,
147+
dynamicClient,
148+
c.ExtraConfig.RESTMapper,
141149
c.ExtraConfig.KubeInternalInformers.Rbac().InternalVersion().RoleBindings().Lister(),
142150
)
143151

pkg/project/registry/projectrequest/delegated/delegated.go

+43-35
Original file line numberDiff line numberDiff line change
@@ -7,38 +7,36 @@ import (
77
"strings"
88

99
"github.com/golang/glog"
10-
1110
kapierror "k8s.io/apimachinery/pkg/api/errors"
11+
"k8s.io/apimachinery/pkg/api/meta"
1212
metainternal "k8s.io/apimachinery/pkg/apis/meta/internalversion"
1313
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
14+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
1415
"k8s.io/apimachinery/pkg/runtime"
1516
utilerrors "k8s.io/apimachinery/pkg/util/errors"
1617
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
1718
"k8s.io/apimachinery/pkg/util/sets"
1819
"k8s.io/apimachinery/pkg/util/wait"
1920
apirequest "k8s.io/apiserver/pkg/endpoints/request"
2021
"k8s.io/apiserver/pkg/registry/rest"
21-
restclient "k8s.io/client-go/rest"
22+
"k8s.io/client-go/dynamic"
2223
"k8s.io/client-go/util/retry"
2324
"k8s.io/kubernetes/pkg/api/legacyscheme"
2425
authorizationapi "k8s.io/kubernetes/pkg/apis/authorization"
2526
kapi "k8s.io/kubernetes/pkg/apis/core"
2627
"k8s.io/kubernetes/pkg/apis/rbac"
2728
authorizationclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/authorization/internalversion"
2829
rbaclisters "k8s.io/kubernetes/pkg/client/listers/rbac/internalversion"
29-
"k8s.io/kubernetes/pkg/kubectl/genericclioptions/resource"
3030

3131
osauthorizationapi "github.com/openshift/origin/pkg/authorization/apis/authorization"
3232
authorizationutil "github.com/openshift/origin/pkg/authorization/util"
33-
configcmd "github.com/openshift/origin/pkg/bulk"
3433
"github.com/openshift/origin/pkg/cmd/server/bootstrappolicy"
3534
projectapi "github.com/openshift/origin/pkg/project/apis/project"
3635
projectclientinternal "github.com/openshift/origin/pkg/project/generated/internalclientset/typed/project/internalversion"
3736
projectrequestregistry "github.com/openshift/origin/pkg/project/registry/projectrequest"
3837
templateapi "github.com/openshift/origin/pkg/template/apis/template"
3938
templateinternalclient "github.com/openshift/origin/pkg/template/client/internalversion"
4039
templateclient "github.com/openshift/origin/pkg/template/generated/internalclientset"
41-
restutil "github.com/openshift/origin/pkg/util/rest"
4240
)
4341

4442
type REST struct {
@@ -49,7 +47,8 @@ type REST struct {
4947
sarClient authorizationclient.SubjectAccessReviewInterface
5048
projectGetter projectclientinternal.ProjectsGetter
5149
templateClient templateclient.Interface
52-
restConfig *restclient.Config
50+
client dynamic.Interface
51+
restMapper meta.RESTMapper
5352

5453
// policyBindings is an auth cache that is shared with the authorizer for the API server.
5554
// we use this cache to detect when the authorizer has observed the change for the auth rules
@@ -59,15 +58,22 @@ type REST struct {
5958
var _ rest.Lister = &REST{}
6059
var _ rest.Creater = &REST{}
6160

62-
func NewREST(message, templateNamespace, templateName string, projectClient projectclientinternal.ProjectsGetter, templateClient templateclient.Interface, sarClient authorizationclient.SubjectAccessReviewInterface, restConfig *restclient.Config, roleBindings rbaclisters.RoleBindingLister) *REST {
61+
func NewREST(message, templateNamespace, templateName string,
62+
projectClient projectclientinternal.ProjectsGetter,
63+
templateClient templateclient.Interface,
64+
sarClient authorizationclient.SubjectAccessReviewInterface,
65+
client dynamic.Interface,
66+
restMapper meta.RESTMapper,
67+
roleBindings rbaclisters.RoleBindingLister) *REST {
6368
return &REST{
6469
message: message,
6570
templateNamespace: templateNamespace,
6671
templateName: templateName,
6772
projectGetter: projectClient,
6873
templateClient: templateClient,
6974
sarClient: sarClient,
70-
restConfig: restConfig,
75+
client: client,
76+
restMapper: restMapper,
7177
roleBindings: roleBindings,
7278
}
7379
}
@@ -190,36 +196,38 @@ func (r *REST) Create(ctx context.Context, obj runtime.Object, createValidation
190196
return nil, err
191197
}
192198

193-
// Stop on the first error, since we have to delete the whole project if any item in the template fails
194-
stopOnErr := configcmd.AfterFunc(func(info *resource.Info, err error) bool {
195-
// if a default role binding already exists, we're probably racing the controller. Don't die
196-
if gvk := info.Mapping.GroupVersionKind; kapierror.IsAlreadyExists(err) &&
197-
gvk.Kind == roleBindingKind && roleBindingGroups.Has(gvk.Group) && defaultRoleBindingNames.Has(info.Name) {
198-
return false
199+
// TODO, stop doing this crazy thing, but for now it's a very simple way to get the unstructured objects we need
200+
jsonBytes, err := runtime.Encode(legacyscheme.Codecs.LegacyCodec(legacyscheme.Scheme.PrioritizedVersionsAllGroups()...), objectsToCreate)
201+
if err != nil {
202+
return nil, kapierror.NewInternalError(err)
203+
}
204+
uncastList, err := runtime.Decode(unstructured.UnstructuredJSONScheme, jsonBytes)
205+
if err != nil {
206+
return nil, kapierror.NewInternalError(err)
207+
}
208+
toCreateList := uncastList.(*unstructured.UnstructuredList)
209+
210+
for _, toCreate := range toCreateList.Items {
211+
restMapping, mappingErr := r.restMapper.RESTMapping(toCreate.GroupVersionKind().GroupKind(), toCreate.GroupVersionKind().Version)
212+
if mappingErr != nil {
213+
utilruntime.HandleError(fmt.Errorf("error creating items in requested project %q: %v", createdProject.Name, mappingErr))
214+
return nil, kapierror.NewInternalError(mappingErr)
199215
}
200-
return err != nil
201-
})
202216

203-
bulk := configcmd.Bulk{
204-
Mapper: &resource.Mapper{
205-
RESTMapper: restutil.DefaultMultiRESTMapper(),
206-
ObjectTyper: legacyscheme.Scheme,
207-
ClientMapper: configcmd.ClientMapperFromConfig(r.restConfig),
208-
},
209-
IgnoreError: func(err error) bool {
210-
// it is safe to ignore all such errors since stopOnErr will only let these through for the default role bindings
211-
return kapierror.IsAlreadyExists(err)
212-
},
213-
After: stopOnErr,
214-
Op: configcmd.Create,
215-
}
216-
if err := utilerrors.NewAggregate(bulk.Run(objectsToCreate, createdProject.Name)); err != nil {
217-
utilruntime.HandleError(fmt.Errorf("error creating items in requested project %q: %v", createdProject.Name, err))
218-
// We have to clean up the project if any part of the project request template fails
219-
if deleteErr := r.projectGetter.Projects().Delete(createdProject.Name, &metav1.DeleteOptions{}); deleteErr != nil {
220-
utilruntime.HandleError(fmt.Errorf("error cleaning up requested project %q: %v", createdProject.Name, deleteErr))
217+
_, createErr := r.client.Resource(restMapping.Resource).Namespace(createdProject.Name).Create(&toCreate)
218+
// if a default role binding already exists, we're probably racing the controller. Don't die
219+
if gvk := restMapping.GroupVersionKind; kapierror.IsAlreadyExists(createErr) &&
220+
gvk.Kind == roleBindingKind && roleBindingGroups.Has(gvk.Group) && defaultRoleBindingNames.Has(toCreate.GetName()) {
221+
continue
222+
}
223+
// it is safe to ignore all such errors since stopOnErr will only let these through for the default role bindings
224+
if kapierror.IsAlreadyExists(createErr) {
225+
continue
226+
}
227+
if createErr != nil {
228+
utilruntime.HandleError(fmt.Errorf("error creating items in requested project %q: %v", createdProject.Name, createErr))
229+
return nil, kapierror.NewInternalError(createErr)
221230
}
222-
return nil, kapierror.NewInternalError(err)
223231
}
224232

225233
// wait for a rolebinding if we created one

0 commit comments

Comments
 (0)