Commit c7e68a27 authored by deads2k's avatar deads2k

create apiserver for extensions

parent 72eef13e
/*
Copyright 2017 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 main
import (
"flag"
"os"
"runtime"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/apiserver/pkg/util/logs"
"k8s.io/kube-apiextensions-server/pkg/cmd/server"
)
func main() {
logs.InitLogs()
defer logs.FlushLogs()
if len(os.Getenv("GOMAXPROCS")) == 0 {
runtime.GOMAXPROCS(runtime.NumCPU())
}
cmd := server.NewCommandStartCustomResourcesServer(os.Stdout, os.Stderr, wait.NeverStop)
cmd.Flags().AddGoFlagSet(flag.CommandLine)
if err := cmd.Execute(); err != nil {
panic(err)
}
}
/*
Copyright 2017 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 apiserver
import (
"k8s.io/apimachinery/pkg/apimachinery/announced"
"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/apimachinery/pkg/version"
"k8s.io/apiserver/pkg/registry/rest"
genericapiserver "k8s.io/apiserver/pkg/server"
"k8s.io/kube-apiextensions-server/pkg/apis/apiextensions"
"k8s.io/kube-apiextensions-server/pkg/apis/apiextensions/install"
"k8s.io/kube-apiextensions-server/pkg/apis/apiextensions/v1alpha1"
"k8s.io/kube-apiextensions-server/pkg/registry/customresource"
// make sure the generated client works
_ "k8s.io/kube-apiextensions-server/pkg/client/clientset/clientset"
_ "k8s.io/kube-apiextensions-server/pkg/client/clientset/internalclientset"
_ "k8s.io/kube-apiextensions-server/pkg/client/informers/externalversions"
_ "k8s.io/kube-apiextensions-server/pkg/client/informers/internalversion"
)
var (
groupFactoryRegistry = make(announced.APIGroupFactoryRegistry)
registry = registered.NewOrDie("")
Scheme = runtime.NewScheme()
Codecs = serializer.NewCodecFactory(Scheme)
)
func init() {
install.Install(groupFactoryRegistry, registry, Scheme)
// we need to add the options to empty v1
metav1.AddToGroupVersion(Scheme, schema.GroupVersion{Group: "", Version: "v1"})
unversioned := schema.GroupVersion{Group: "", Version: "v1"}
Scheme.AddUnversionedTypes(unversioned,
&metav1.Status{},
&metav1.APIVersions{},
&metav1.APIGroupList{},
&metav1.APIGroup{},
&metav1.APIResourceList{},
)
}
type Config struct {
GenericConfig *genericapiserver.Config
}
type CustomResources struct {
GenericAPIServer *genericapiserver.GenericAPIServer
}
type completedConfig struct {
*Config
}
// Complete fills in any fields not set that are required to have valid data. It's mutating the receiver.
func (c *Config) Complete() completedConfig {
c.GenericConfig.Complete()
c.GenericConfig.Version = &version.Info{
Major: "0",
Minor: "1",
}
return completedConfig{c}
}
// SkipComplete provides a way to construct a server instance without config completion.
func (c *Config) SkipComplete() completedConfig {
return completedConfig{c}
}
// New returns a new instance of CustomResources from the given config.
func (c completedConfig) New() (*CustomResources, error) {
genericServer, err := c.Config.GenericConfig.SkipComplete().New() // completion is done in Complete, no need for a second time
if err != nil {
return nil, err
}
s := &CustomResources{
GenericAPIServer: genericServer,
}
apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(apiextensions.GroupName, registry, Scheme, metav1.ParameterCodec, Codecs)
apiGroupInfo.GroupMeta.GroupVersion = v1alpha1.SchemeGroupVersion
v1alpha1storage := map[string]rest.Storage{}
v1alpha1storage["customresources"] = customresource.NewREST(Scheme, c.GenericConfig.RESTOptionsGetter)
apiGroupInfo.VersionedResourcesStorageMap["v1alpha1"] = v1alpha1storage
if err := s.GenericAPIServer.InstallAPIGroup(&apiGroupInfo); err != nil {
return nil, err
}
return s, nil
}
/*
Copyright 2017 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 server
import (
"fmt"
"io"
"net"
"github.com/spf13/cobra"
genericapiserver "k8s.io/apiserver/pkg/server"
genericoptions "k8s.io/apiserver/pkg/server/options"
"k8s.io/kube-apiextensions-server/pkg/apis/apiextensions/v1alpha1"
"k8s.io/kube-apiextensions-server/pkg/apiserver"
)
const defaultEtcdPathPrefix = "/registry/apiextensions.kubernetes.io"
type CustomResourcesServerOptions struct {
RecommendedOptions *genericoptions.RecommendedOptions
StdOut io.Writer
StdErr io.Writer
}
func NewCustomResourcesServerOptions(out, errOut io.Writer) *CustomResourcesServerOptions {
o := &CustomResourcesServerOptions{
RecommendedOptions: genericoptions.NewRecommendedOptions(defaultEtcdPathPrefix, apiserver.Scheme, apiserver.Codecs.LegacyCodec(v1alpha1.SchemeGroupVersion)),
StdOut: out,
StdErr: errOut,
}
return o
}
func NewCommandStartCustomResourcesServer(out, errOut io.Writer, stopCh <-chan struct{}) *cobra.Command {
o := NewCustomResourcesServerOptions(out, errOut)
cmd := &cobra.Command{
Short: "Launch an API extensions API server",
Long: "Launch an API extensions API server",
RunE: func(c *cobra.Command, args []string) error {
if err := o.Complete(); err != nil {
return err
}
if err := o.Validate(args); err != nil {
return err
}
if err := o.RunCustomResourcesServer(stopCh); err != nil {
return err
}
return nil
},
}
flags := cmd.Flags()
o.RecommendedOptions.AddFlags(flags)
return cmd
}
func (o CustomResourcesServerOptions) Validate(args []string) error {
return nil
}
func (o *CustomResourcesServerOptions) Complete() error {
return nil
}
func (o CustomResourcesServerOptions) Config() (*apiserver.Config, error) {
// TODO have a "real" external address
if err := o.RecommendedOptions.SecureServing.MaybeDefaultWithSelfSignedCerts("localhost", nil, []net.IP{net.ParseIP("127.0.0.1")}); err != nil {
return nil, fmt.Errorf("error creating self-signed certificates: %v", err)
}
serverConfig := genericapiserver.NewConfig(apiserver.Codecs)
if err := o.RecommendedOptions.ApplyTo(serverConfig); err != nil {
return nil, err
}
config := &apiserver.Config{
GenericConfig: serverConfig,
}
return config, nil
}
func (o CustomResourcesServerOptions) RunCustomResourcesServer(stopCh <-chan struct{}) error {
config, err := o.Config()
if err != nil {
return err
}
server, err := config.Complete().New()
if err != nil {
return err
}
return server.GenericAPIServer.PrepareRun().Run(stopCh)
}
/*
Copyright 2017 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 customresource
import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/registry/generic"
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
"k8s.io/kube-apiextensions-server/pkg/apis/apiextensions"
)
// rest implements a RESTStorage for API services against etcd
type REST struct {
*genericregistry.Store
}
// NewREST returns a RESTStorage object that will work against API services.
func NewREST(scheme *runtime.Scheme, optsGetter generic.RESTOptionsGetter) *REST {
strategy := NewStrategy(scheme)
store := &genericregistry.Store{
Copier: scheme,
NewFunc: func() runtime.Object { return &apiextensions.CustomResource{} },
NewListFunc: func() runtime.Object { return &apiextensions.CustomResourceList{} },
ObjectNameFunc: func(obj runtime.Object) (string, error) {
return obj.(*apiextensions.CustomResource).Name, nil
},
PredicateFunc: MatchCustomResource,
QualifiedResource: apiextensions.Resource("customresources"),
CreateStrategy: strategy,
UpdateStrategy: strategy,
DeleteStrategy: strategy,
}
options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: GetAttrs}
if err := store.CompleteWithOptions(options); err != nil {
panic(err) // TODO: Propagate error up
}
return &REST{store}
}
/*
Copyright 2017 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 customresource
import (
"fmt"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/validation/field"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/generic"
"k8s.io/apiserver/pkg/storage"
"k8s.io/apiserver/pkg/storage/names"
"k8s.io/kube-apiextensions-server/pkg/apis/apiextensions"
)
type apiServerStrategy struct {
runtime.ObjectTyper
names.NameGenerator
}
func NewStrategy(typer runtime.ObjectTyper) apiServerStrategy {
return apiServerStrategy{typer, names.SimpleNameGenerator}
}
func (apiServerStrategy) NamespaceScoped() bool {
return false
}
func (apiServerStrategy) PrepareForCreate(ctx genericapirequest.Context, obj runtime.Object) {
}
func (apiServerStrategy) PrepareForUpdate(ctx genericapirequest.Context, obj, old runtime.Object) {
}
func (apiServerStrategy) Validate(ctx genericapirequest.Context, obj runtime.Object) field.ErrorList {
return field.ErrorList{}
}
func (apiServerStrategy) AllowCreateOnUpdate() bool {
return false
}
func (apiServerStrategy) AllowUnconditionalUpdate() bool {
return false
}
func (apiServerStrategy) Canonicalize(obj runtime.Object) {
}
func (apiServerStrategy) ValidateUpdate(ctx genericapirequest.Context, obj, old runtime.Object) field.ErrorList {
return field.ErrorList{}
}
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
apiserver, ok := obj.(*apiextensions.CustomResource)
if !ok {
return nil, nil, fmt.Errorf("given object is not a CustomResource.")
}
return labels.Set(apiserver.ObjectMeta.Labels), CustomResourceToSelectableFields(apiserver), nil
}
// MatchCustomResource is the filter used by the generic etcd backend to watch events
// from etcd to clients of the apiserver only interested in specific labels/fields.
func MatchCustomResource(label labels.Selector, field fields.Selector) storage.SelectionPredicate {
return storage.SelectionPredicate{
Label: label,
Field: field,
GetAttrs: GetAttrs,
}
}
// CustomResourceToSelectableFields returns a field set that represents the object.
func CustomResourceToSelectableFields(obj *apiextensions.CustomResource) fields.Set {
return generic.ObjectMetaFieldsSet(&obj.ObjectMeta, true)
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment