github.com/docker/compose-on-kubernetes@v0.5.0/internal/apiserver/apiserver.go (about)

     1  package apiserver
     2  
     3  import (
     4  	"net/http"
     5  	"reflect"
     6  	"unsafe"
     7  
     8  	"github.com/docker/compose-on-kubernetes/api/compose/v1alpha3"
     9  	"github.com/docker/compose-on-kubernetes/api/compose/v1beta1"
    10  	"github.com/docker/compose-on-kubernetes/api/compose/v1beta2"
    11  	"github.com/docker/compose-on-kubernetes/internal/conversions"
    12  	"github.com/docker/compose-on-kubernetes/internal/internalversion"
    13  	composeregistry "github.com/docker/compose-on-kubernetes/internal/registry"
    14  	"github.com/docker/compose-on-kubernetes/internal/requestaddons"
    15  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    16  	"k8s.io/apimachinery/pkg/runtime"
    17  	"k8s.io/apimachinery/pkg/runtime/schema"
    18  	"k8s.io/apimachinery/pkg/runtime/serializer"
    19  	"k8s.io/apimachinery/pkg/util/sets"
    20  	"k8s.io/apimachinery/pkg/version"
    21  	"k8s.io/apiserver/pkg/registry/rest"
    22  	genericapiserver "k8s.io/apiserver/pkg/server"
    23  	genericfilters "k8s.io/apiserver/pkg/server/filters"
    24  	"k8s.io/client-go/tools/clientcmd"
    25  )
    26  
    27  // Internal variables
    28  var (
    29  	Scheme = runtime.NewScheme()
    30  	Codecs = serializer.NewCodecFactory(Scheme)
    31  )
    32  
    33  func init() {
    34  	metav1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"})
    35  	unversioned := schema.GroupVersion{Group: "", Version: "v1"}
    36  	Scheme.AddUnversionedTypes(unversioned,
    37  		&metav1.Status{},
    38  		&metav1.APIVersions{},
    39  		&metav1.APIGroupList{},
    40  		&metav1.APIGroup{},
    41  		&metav1.APIResourceList{},
    42  	)
    43  	v1beta1.AddToScheme(Scheme)
    44  	v1beta2.AddToScheme(Scheme)
    45  	v1alpha3.AddToScheme(Scheme)
    46  	Scheme.SetVersionPriority(v1alpha3.SchemeGroupVersion, v1beta2.SchemeGroupVersion, v1beta1.SchemeGroupVersion)
    47  	internalversion.AddStorageToScheme(Scheme)
    48  	internalversion.AddInternalToScheme(Scheme)
    49  	if err := conversions.RegisterV1beta1Conversions(Scheme); err != nil {
    50  		panic(err)
    51  	}
    52  	if err := conversions.RegisterV1alpha3Conversions(Scheme); err != nil {
    53  		panic(err)
    54  	}
    55  	if err := conversions.RegisterV1beta2Conversions(Scheme); err != nil {
    56  		panic(err)
    57  	}
    58  	// We do not support protobuf serialization as the `Stack` struct has
    59  	// fields with unsupported types (e.g.: map[string]*string). This causes
    60  	// issues like https://github.com/docker/compose-on-kubernetes/issues/150.
    61  	// The workaround is to remove protobuf from the advertised supported codecs.
    62  	removeProtobufMediaType(&Codecs)
    63  }
    64  
    65  // removeProtobufMediaType removes protobuf from the list of accepted media
    66  // types for the given CodecFactory.
    67  func removeProtobufMediaType(c *serializer.CodecFactory) {
    68  	codecsPtr := reflect.Indirect(reflect.ValueOf(c))
    69  	accepts := codecsPtr.FieldByName("accepts")
    70  	acceptsPtr := (*[]runtime.SerializerInfo)(unsafe.Pointer(accepts.UnsafeAddr()))
    71  	for i, v := range *acceptsPtr {
    72  		if v.MediaType == runtime.ContentTypeProtobuf {
    73  			*acceptsPtr = append((*acceptsPtr)[0:i], (*acceptsPtr)[i+1:]...)
    74  			break
    75  		}
    76  	}
    77  }
    78  
    79  // Config is the API server config
    80  type Config struct {
    81  	GenericConfig *genericapiserver.RecommendedConfig
    82  }
    83  
    84  type completedConfig struct {
    85  	GenericConfig genericapiserver.CompletedConfig
    86  }
    87  
    88  // CompletedConfig is the complete API server config
    89  type CompletedConfig struct {
    90  	// Embed a private pointer that cannot be instantiated outside of this package.
    91  	*completedConfig
    92  }
    93  
    94  // Complete fills in any fields not set that are required to have valid data. It's mutating the receiver.
    95  func (cfg *Config) Complete() CompletedConfig {
    96  	cfg.GenericConfig.BuildHandlerChainFunc = func(apiHandler http.Handler, c *genericapiserver.Config) (secure http.Handler) {
    97  		handler := requestaddons.WithSkipValidationHandler(apiHandler)
    98  		return genericapiserver.DefaultBuildHandlerChain(handler, c)
    99  	}
   100  	c := completedConfig{
   101  		GenericConfig: cfg.GenericConfig.Complete(),
   102  	}
   103  
   104  	c.GenericConfig.Version = &version.Info{
   105  		Major: "1",
   106  		Minor: "0",
   107  	}
   108  
   109  	c.GenericConfig.LongRunningFunc = genericfilters.BasicLongRunningRequestCheck(sets.NewString("watch"), sets.NewString("log"))
   110  
   111  	return CompletedConfig{&c}
   112  }
   113  
   114  // ComposeServer is the compose api server
   115  type ComposeServer struct {
   116  	*genericapiserver.GenericAPIServer
   117  }
   118  
   119  // New returns a new instance of ComposeServer from the given config.
   120  func (c completedConfig) New(kubeconfig string) (*ComposeServer, error) {
   121  	config, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
   122  	if err != nil {
   123  		return nil, err
   124  	}
   125  	genericServer, err := c.GenericConfig.New("compose-apiserver", genericapiserver.NewEmptyDelegate())
   126  	if err != nil {
   127  		return nil, err
   128  	}
   129  
   130  	s := &ComposeServer{
   131  		GenericAPIServer: genericServer,
   132  	}
   133  	apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(v1beta2.GroupName, Scheme, metav1.ParameterCodec, Codecs)
   134  
   135  	stackRegistry1, err := composeregistry.NewStackREST(composeregistry.APIV1beta1, Scheme, c.GenericConfig.RESTOptionsGetter, config)
   136  	if err != nil {
   137  		return nil, err
   138  	}
   139  
   140  	stackRegistry2, err := composeregistry.NewStackREST(composeregistry.APIV1beta2, Scheme, c.GenericConfig.RESTOptionsGetter, config)
   141  	if err != nil {
   142  		return nil, err
   143  	}
   144  	stackOwnerRegistry1 := composeregistry.NewStackOwnerRest(stackRegistry1, composeregistry.APIV1beta1)
   145  	v1beta1storage := map[string]rest.Storage{}
   146  	v1beta1storage["stacks"] = stackRegistry1
   147  	v1beta1storage["stacks/owner"] = stackOwnerRegistry1
   148  	apiGroupInfo.VersionedResourcesStorageMap["v1beta1"] = v1beta1storage
   149  
   150  	stackOwnerRegistry2 := composeregistry.NewStackOwnerRest(stackRegistry2, composeregistry.APIV1beta2)
   151  	stackComposeFileRegistry := composeregistry.NewStackComposeFileRest(stackRegistry1)
   152  	stackScaleRegistry := composeregistry.NewStackScaleRest(stackRegistry2, config)
   153  	stackLogRegistry := composeregistry.NewStackLogRest(config)
   154  	v1beta2storage := map[string]rest.Storage{}
   155  	v1beta2storage["stacks"] = stackRegistry2
   156  	v1beta2storage["stacks/owner"] = stackOwnerRegistry2
   157  	v1beta2storage["stacks/composeFile"] = stackComposeFileRegistry
   158  	v1beta2storage["stacks/scale"] = stackScaleRegistry
   159  	v1beta2storage["stacks/log"] = stackLogRegistry
   160  	apiGroupInfo.VersionedResourcesStorageMap["v1beta2"] = v1beta2storage
   161  
   162  	// v1alpha3 has exactly the same implementation as v1beta2
   163  	apiGroupInfo.VersionedResourcesStorageMap["v1alpha3"] = v1beta2storage
   164  
   165  	if err := s.GenericAPIServer.InstallAPIGroup(&apiGroupInfo); err != nil {
   166  		return nil, err
   167  	}
   168  
   169  	return s, nil
   170  }