Skip to content

Commit

Permalink
Implement a kube-apiserver backed storage.Interface
Browse files Browse the repository at this point in the history
Signed-off-by: Aylei <rayingecho@gmail.com>
  • Loading branch information
aylei committed Oct 28, 2019
1 parent 92fad18 commit 209a6e1
Show file tree
Hide file tree
Showing 4 changed files with 674 additions and 0 deletions.
65 changes: 65 additions & 0 deletions pkg/apiserver/storage/apiserver.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright 2019. PingCAP, Inc.
//
// 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,
// See the License for the specific language governing permissions and
// limitations under the License.

package storage

import (
"fmt"

"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apiserver/pkg/registry/generic"
"k8s.io/apiserver/pkg/storage/storagebackend/factory"
"k8s.io/client-go/rest"

"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/storage"
"k8s.io/apiserver/pkg/storage/storagebackend"
)

// ApiServerRestOptionsFactory produce RestOptions for resources using kube-apiserver as storage
type ApiServerRestOptionsFactory struct {
RestConfig *rest.Config
StorageNamespace string
Codec runtime.Codec
}

// GetRestOptions fetch a new RestOptions from the factory
func (f *ApiServerRestOptionsFactory) GetRESTOptions(resource schema.GroupResource) (generic.RESTOptions, error) {
opts := generic.RESTOptions{
ResourcePrefix: getResourcePrefix(resource),
EnableGarbageCollection: true,
StorageConfig: storagebackend.NewDefaultConfig("", f.Codec),
Decorator: f.newApiServerStorageDecorator(),
}
return opts, nil
}

// newApiServerStorage returns a new storage decorator which creates a kube-apiserver backed storage interface
func (f *ApiServerRestOptionsFactory) newApiServerStorageDecorator() generic.StorageDecorator {
return func(
config *storagebackend.Config,
objectType runtime.Object,
resourcePrefix string,
keyFunc func(obj runtime.Object) (string, error),
newListFunc func() runtime.Object,
getAttrsFunc storage.AttrFunc,
trigger storage.TriggerPublisherFunc,
) (storage.Interface, factory.DestroyFunc) {
return NewApiServerStore(f.RestConfig, f.Codec, f.StorageNamespace, objectType, newListFunc)
}
}

// getResourcePrefix return a '/{group}/{kind}' prefix for resource key with '/{group}/{kind}/{ns}/{name}' format
func getResourcePrefix(resource schema.GroupResource) string {
return fmt.Sprintf("/%s/%s", resource.Group, resource.Resource)
}
116 changes: 116 additions & 0 deletions pkg/apiserver/storage/key.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// Copyright 2019. PingCAP, Inc.
//
// 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,
// See the License for the specific language governing permissions and
// limitations under the License.

package storage

import (
"fmt"
"strings"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
)

const (
labelDomain = "apiserver.pingcap.com"
)

var (
groupLabel = labelDomain + "/group"
kindLabel = labelDomain + "/kind"
namespaceLabel = labelDomain + "/namespace"
nameLabel = labelDomain + "/name"
)

// objKey is a structured representation of key '/{group}/{kind}/{ns}/{name}'
type objKey struct {
group string
kind string
namespace string
name string
}

func newObjectKey(key string) *objKey {
s := strings.Split(strings.TrimPrefix(key, "/"), "/")
if len(s) < 2 {
panic("expect a string key in /{group}/{kind}/{ns?}/{name?} format")
}
k := &objKey{
group: s[0],
kind: s[1],
}
if len(s) > 2 {
k.namespace = s[2]
}
if len(s) > 3 {
k.name = s[3]
}
return k
}

func (o *objKey) fullName() string {
sb := strings.Builder{}
sb.WriteString(strings.ReplaceAll(o.group, ".", "-"))
sb.WriteRune('-')
sb.WriteString(o.kind)
if o.namespace != "" {
sb.WriteRune('-')
sb.WriteString(o.namespace)
}
if o.name != "" {
sb.WriteRune('-')
sb.WriteString(o.name)
}
return sb.String()
}

func (o *objKey) labelMap() map[string]string {
m := map[string]string{
groupLabel: o.group,
kindLabel: o.kind,
}
if o.namespace != "" {
m[namespaceLabel] = o.namespace
}
if o.name != "" {
m[nameLabel] = o.name
}
return m
}

func (o *objKey) objectMeta() metav1.ObjectMeta {
return metav1.ObjectMeta{
Name: o.fullName(),
Labels: o.labelMap(),
}
}

func (o *objKey) selector() (labels.Selector, error) {
return metav1.LabelSelectorAsSelector(&metav1.LabelSelector{
MatchLabels: o.labelMap(),
})
}

func (o *objKey) labelSelectorStr() string {
sb := strings.Builder{}
sb.WriteString(fmt.Sprintf("%s=%s,%s=%s",
groupLabel, o.group,
kindLabel, o.kind))
if o.namespace != "" {
sb.WriteString(fmt.Sprintf(",%s=%s", namespaceLabel, o.namespace))
}
if o.name != "" {
sb.WriteString(fmt.Sprintf(",%s=%s", nameLabel, o.name))
}
return sb.String()
}
Loading

0 comments on commit 209a6e1

Please sign in to comment.