This is a huge commit, it does following things: (#1942)

1. Remove ks-iam standalone binary, move it to ks-apiserver
2. Generate all devops apis inside kubesphere repository, no need to
import s2ioperator.
3. Reorganize ldap code, make it more flexible to use.
This commit is contained in:
zryfish
2020-03-10 13:50:17 +08:00
committed by GitHub
parent 7270307b66
commit 641615b299
235 changed files with 5538 additions and 38064 deletions

View File

@@ -1,215 +0,0 @@
/*
Copyright 2018 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 builder
import (
"fmt"
"strings"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
"sigs.k8s.io/controller-runtime/pkg/controller"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/predicate"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"sigs.k8s.io/controller-runtime/pkg/source"
)
// Supporting mocking out functions for testing
var newController = controller.New
var getGvk = apiutil.GVKForObject
// Builder builds a Controller.
type Builder struct {
apiType runtime.Object
mgr manager.Manager
predicates []predicate.Predicate
managedObjects []runtime.Object
watchRequest []watchRequest
config *rest.Config
ctrl controller.Controller
ctrlOptions controller.Options
name string
}
// ControllerManagedBy returns a new controller builder that will be started by the provided Manager
func ControllerManagedBy(m manager.Manager) *Builder {
return &Builder{mgr: m}
}
// ForType defines the type of Object being *reconciled*, and configures the ControllerManagedBy to respond to create / delete /
// update events by *reconciling the object*.
// This is the equivalent of calling
// Watches(&source.Kind{Type: apiType}, &handler.EnqueueRequestForObject{})
//
// Deprecated: Use For
func (blder *Builder) ForType(apiType runtime.Object) *Builder {
return blder.For(apiType)
}
// For defines the type of Object being *reconciled*, and configures the ControllerManagedBy to respond to create / delete /
// update events by *reconciling the object*.
// This is the equivalent of calling
// Watches(&source.Kind{Type: apiType}, &handler.EnqueueRequestForObject{})
func (blder *Builder) For(apiType runtime.Object) *Builder {
blder.apiType = apiType
return blder
}
// Owns defines types of Objects being *generated* by the ControllerManagedBy, and configures the ControllerManagedBy to respond to
// create / delete / update events by *reconciling the owner object*. This is the equivalent of calling
// Watches(&handler.EnqueueRequestForOwner{&source.Kind{Type: <ForType-apiType>}, &handler.EnqueueRequestForOwner{OwnerType: apiType, IsController: true})
func (blder *Builder) Owns(apiType runtime.Object) *Builder {
blder.managedObjects = append(blder.managedObjects, apiType)
return blder
}
type watchRequest struct {
src source.Source
eventhandler handler.EventHandler
}
// Watches exposes the lower-level ControllerManagedBy Watches functions through the builder. Consider using
// Owns or For instead of Watches directly.
func (blder *Builder) Watches(src source.Source, eventhandler handler.EventHandler) *Builder {
blder.watchRequest = append(blder.watchRequest, watchRequest{src: src, eventhandler: eventhandler})
return blder
}
// WithConfig sets the Config to use for configuring clients. Defaults to the in-cluster config or to ~/.kube/config.
//
// Deprecated: Use ControllerManagedBy(Manager) and this isn't needed.
func (blder *Builder) WithConfig(config *rest.Config) *Builder {
blder.config = config
return blder
}
// WithEventFilter sets the event filters, to filter which create/update/delete/generic events eventually
// trigger reconciliations. For example, filtering on whether the resource version has changed.
// Defaults to the empty list.
func (blder *Builder) WithEventFilter(p predicate.Predicate) *Builder {
blder.predicates = append(blder.predicates, p)
return blder
}
// WithOptions overrides the controller options use in doController. Defaults to empty.
func (blder *Builder) WithOptions(options controller.Options) *Builder {
blder.ctrlOptions = options
return blder
}
// Named sets the name of the controller to the given name. The name shows up
// in metrics, among other things, and thus should be a prometheus compatible name
// (underscores and alphanumeric characters only).
//
// By default, controllers are named using the lowercase version of their kind.
func (blder *Builder) Named(name string) *Builder {
blder.name = name
return blder
}
// Complete builds the Application ControllerManagedBy.
func (blder *Builder) Complete(r reconcile.Reconciler) error {
_, err := blder.Build(r)
return err
}
// Build builds the Application ControllerManagedBy and returns the Controller it created.
func (blder *Builder) Build(r reconcile.Reconciler) (controller.Controller, error) {
if r == nil {
return nil, fmt.Errorf("must provide a non-nil Reconciler")
}
if blder.mgr == nil {
return nil, fmt.Errorf("must provide a non-nil Manager")
}
// Set the Config
blder.loadRestConfig()
// Set the ControllerManagedBy
if err := blder.doController(r); err != nil {
return nil, err
}
// Set the Watch
if err := blder.doWatch(); err != nil {
return nil, err
}
return blder.ctrl, nil
}
func (blder *Builder) doWatch() error {
// Reconcile type
src := &source.Kind{Type: blder.apiType}
hdler := &handler.EnqueueRequestForObject{}
err := blder.ctrl.Watch(src, hdler, blder.predicates...)
if err != nil {
return err
}
// Watches the managed types
for _, obj := range blder.managedObjects {
src := &source.Kind{Type: obj}
hdler := &handler.EnqueueRequestForOwner{
OwnerType: blder.apiType,
IsController: true,
}
if err := blder.ctrl.Watch(src, hdler, blder.predicates...); err != nil {
return err
}
}
// Do the watch requests
for _, w := range blder.watchRequest {
if err := blder.ctrl.Watch(w.src, w.eventhandler, blder.predicates...); err != nil {
return err
}
}
return nil
}
func (blder *Builder) loadRestConfig() {
if blder.config == nil {
blder.config = blder.mgr.GetConfig()
}
}
func (blder *Builder) getControllerName() (string, error) {
if blder.name != "" {
return blder.name, nil
}
gvk, err := getGvk(blder.apiType, blder.mgr.GetScheme())
if err != nil {
return "", err
}
return strings.ToLower(gvk.Kind), nil
}
func (blder *Builder) doController(r reconcile.Reconciler) error {
name, err := blder.getControllerName()
if err != nil {
return err
}
ctrlOptions := blder.ctrlOptions
ctrlOptions.Reconciler = r
blder.ctrl, err = newController(name, blder.mgr, ctrlOptions)
return err
}

View File

@@ -1,28 +0,0 @@
/*
Copyright 2018 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 builder provides wraps other controller-runtime libraries and exposes simple
// patterns for building common Controllers.
//
// Projects built with the builder package can trivially be rebased on top of the underlying
// packages if the project requires more customized behavior in the future.
package builder
import (
logf "sigs.k8s.io/controller-runtime/pkg/internal/log"
)
var log = logf.RuntimeLog.WithName("builder")

View File

@@ -1,166 +0,0 @@
/*
Copyright 2019 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 builder
import (
"net/http"
"net/url"
"strings"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
"sigs.k8s.io/controller-runtime/pkg/webhook/conversion"
)
// WebhookBuilder builds a Webhook.
type WebhookBuilder struct {
apiType runtime.Object
gvk schema.GroupVersionKind
mgr manager.Manager
config *rest.Config
}
func WebhookManagedBy(m manager.Manager) *WebhookBuilder {
return &WebhookBuilder{mgr: m}
}
// TODO(droot): update the GoDoc for conversion.
// For takes a runtime.Object which should be a CR.
// If the given object implements the admission.Defaulter interface, a MutatingWebhook will be wired for this type.
// If the given object implements the admission.Validator interface, a ValidatingWebhook will be wired for this type.
func (blder *WebhookBuilder) For(apiType runtime.Object) *WebhookBuilder {
blder.apiType = apiType
return blder
}
// Complete builds the webhook.
func (blder *WebhookBuilder) Complete() error {
// Set the Config
blder.loadRestConfig()
// Set the Webhook if needed
return blder.registerWebhooks()
}
func (blder *WebhookBuilder) loadRestConfig() {
if blder.config == nil {
blder.config = blder.mgr.GetConfig()
}
}
func (blder *WebhookBuilder) registerWebhooks() error {
// Create webhook(s) for each type
var err error
blder.gvk, err = apiutil.GVKForObject(blder.apiType, blder.mgr.GetScheme())
if err != nil {
return err
}
blder.registerDefaultingWebhook()
blder.registerValidatingWebhook()
err = blder.registerConversionWebhook()
if err != nil {
return err
}
return nil
}
// registerDefaultingWebhook registers a defaulting webhook if th
func (blder *WebhookBuilder) registerDefaultingWebhook() {
defaulter, isDefaulter := blder.apiType.(admission.Defaulter)
if !isDefaulter {
log.Info("skip registering a mutating webhook, admission.Defaulter interface is not implemented", "GVK", blder.gvk)
return
}
mwh := admission.DefaultingWebhookFor(defaulter)
if mwh != nil {
path := generateMutatePath(blder.gvk)
// Checking if the path is already registered.
// If so, just skip it.
if !blder.isAlreadyHandled(path) {
log.Info("Registering a mutating webhook",
"GVK", blder.gvk,
"path", path)
blder.mgr.GetWebhookServer().Register(path, mwh)
}
}
}
func (blder *WebhookBuilder) registerValidatingWebhook() {
validator, isValidator := blder.apiType.(admission.Validator)
if !isValidator {
log.Info("skip registering a validating webhook, admission.Validator interface is not implemented", "GVK", blder.gvk)
return
}
vwh := admission.ValidatingWebhookFor(validator)
if vwh != nil {
path := generateValidatePath(blder.gvk)
// Checking if the path is already registered.
// If so, just skip it.
if !blder.isAlreadyHandled(path) {
log.Info("Registering a validating webhook",
"GVK", blder.gvk,
"path", path)
blder.mgr.GetWebhookServer().Register(path, vwh)
}
}
}
func (blder *WebhookBuilder) registerConversionWebhook() error {
ok, err := conversion.IsConvertible(blder.mgr.GetScheme(), blder.apiType)
if err != nil {
log.Error(err, "conversion check failed", "object", blder.apiType)
return err
}
if ok {
if !blder.isAlreadyHandled("/convert") {
blder.mgr.GetWebhookServer().Register("/convert", &conversion.Webhook{})
}
log.Info("conversion webhook enabled", "object", blder.apiType)
}
return nil
}
func (blder *WebhookBuilder) isAlreadyHandled(path string) bool {
if blder.mgr.GetWebhookServer().WebhookMux == nil {
return false
}
h, p := blder.mgr.GetWebhookServer().WebhookMux.Handler(&http.Request{URL: &url.URL{Path: path}})
if p == path && h != nil {
return true
}
return false
}
func generateMutatePath(gvk schema.GroupVersionKind) string {
return "/mutate-" + strings.Replace(gvk.Group, ".", "-", -1) + "-" +
gvk.Version + "-" + strings.ToLower(gvk.Kind)
}
func generateValidatePath(gvk schema.GroupVersionKind) string {
return "/validate-" + strings.Replace(gvk.Group, ".", "-", -1) + "-" +
gvk.Version + "-" + strings.ToLower(gvk.Kind)
}

View File

@@ -1,40 +0,0 @@
/*
Copyright 2019 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 conversion provides interface definitions that an API Type needs to
implement for it to be supported by the generic conversion webhook handler
defined under pkg/webhook/conversion.
*/
package conversion
import "k8s.io/apimachinery/pkg/runtime"
// Convertible defines capability of a type to convertible i.e. it can be converted to/from a hub type.
type Convertible interface {
runtime.Object
ConvertTo(dst Hub) error
ConvertFrom(src Hub) error
}
// Hub marks that a given type is the hub type for conversion. This means that
// all conversions will first convert to the hub type, then convert from the hub
// type to the destination type. All types besides the hub type should implement
// Convertible.
type Hub interface {
runtime.Object
Hub()
}

View File

@@ -1,127 +0,0 @@
/*
Copyright 2019 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 zap
import (
"fmt"
"go.uber.org/zap/buffer"
"go.uber.org/zap/zapcore"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
)
// KubeAwareEncoder is a Kubernetes-aware Zap Encoder.
// Instead of trying to force Kubernetes objects to implement
// ObjectMarshaller, we just implement a wrapper around a normal
// ObjectMarshaller that checks for Kubernetes objects.
type KubeAwareEncoder struct {
// Encoder is the zapcore.Encoder that this encoder delegates to
zapcore.Encoder
// Verbose controls whether or not the full object is printed.
// If false, only name, namespace, api version, and kind are printed.
// Otherwise, the full object is logged.
Verbose bool
}
// namespacedNameWrapper is a zapcore.ObjectMarshaler for Kubernetes NamespacedName
type namespacedNameWrapper struct {
types.NamespacedName
}
func (w namespacedNameWrapper) MarshalLogObject(enc zapcore.ObjectEncoder) error {
if w.Namespace != "" {
enc.AddString("namespace", w.Namespace)
}
enc.AddString("name", w.Name)
return nil
}
// kubeObjectWrapper is a zapcore.ObjectMarshaler for Kubernetes objects.
type kubeObjectWrapper struct {
obj runtime.Object
}
// MarshalLogObject implements zapcore.ObjectMarshaler
func (w kubeObjectWrapper) MarshalLogObject(enc zapcore.ObjectEncoder) error {
// TODO(directxman12): log kind and apiversion if not set explicitly (common case)
// -- needs an a scheme to convert to the GVK.
gvk := w.obj.GetObjectKind().GroupVersionKind()
if gvk.Version != "" {
enc.AddString("apiVersion", gvk.GroupVersion().String())
enc.AddString("kind", gvk.Kind)
}
objMeta, err := meta.Accessor(w.obj)
if err != nil {
return fmt.Errorf("got runtime.Object without object metadata: %v", w.obj)
}
ns := objMeta.GetNamespace()
if ns != "" {
enc.AddString("namespace", ns)
}
enc.AddString("name", objMeta.GetName())
return nil
}
// NB(directxman12): can't just override AddReflected, since the encoder calls AddReflected on itself directly
// Clone implements zapcore.Encoder
func (k *KubeAwareEncoder) Clone() zapcore.Encoder {
return &KubeAwareEncoder{
Encoder: k.Encoder.Clone(),
}
}
// EncodeEntry implements zapcore.Encoder
func (k *KubeAwareEncoder) EncodeEntry(entry zapcore.Entry, fields []zapcore.Field) (*buffer.Buffer, error) {
if k.Verbose {
// Kubernetes objects implement fmt.Stringer, so if we
// want verbose output, just delegate to that.
return k.Encoder.EncodeEntry(entry, fields)
}
for i, field := range fields {
// intercept stringer fields that happen to be Kubernetes runtime.Object or
// types.NamespacedName values (Kubernetes runtime.Objects commonly
// implement String, apparently).
if field.Type == zapcore.StringerType {
switch val := field.Interface.(type) {
case runtime.Object:
fields[i] = zapcore.Field{
Type: zapcore.ObjectMarshalerType,
Key: field.Key,
Interface: kubeObjectWrapper{obj: val},
}
case types.NamespacedName:
fields[i] = zapcore.Field{
Type: zapcore.ObjectMarshalerType,
Key: field.Key,
Interface: namespacedNameWrapper{NamespacedName: val},
}
}
}
}
return k.Encoder.EncodeEntry(entry, fields)
}

View File

@@ -1,209 +0,0 @@
/*
Copyright 2019 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 zap contains helpers for setting up a new logr.Logger instance
// using the Zap logging framework.
package zap
import (
"io"
"os"
"time"
"github.com/go-logr/logr"
"github.com/go-logr/zapr"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
// New returns a brand new Logger configured with Opts. It
// uses KubeAwareEncoder which adds Type information and
// Namespace/Name to the log.
func New(opts ...Opts) logr.Logger {
return zapr.NewLogger(NewRaw(opts...))
}
// Logger is a Logger implementation.
// If development is true, a Zap development config will be used
// (stacktraces on warnings, no sampling), otherwise a Zap production
// config will be used (stacktraces on errors, sampling).
//
// Deprecated: use New() and the functional opts pattern instead:
//
// New(UseDevMode(development))
func Logger(development bool) logr.Logger {
return LoggerTo(os.Stderr, development)
}
// LoggerTo returns a new Logger implementation using Zap which logs
// to the given destination, instead of stderr. It otherwise behaves like
// ZapLogger.
//
// Deprecated: use New() and the functional opts pattern instead:
//
// New(UseDevMode(development), WriteTo(writer))
func LoggerTo(destWriter io.Writer, development bool) logr.Logger {
return zapr.NewLogger(RawLoggerTo(destWriter, development))
}
// RawLoggerTo returns a new zap.Logger configured with KubeAwareEncoder
// which logs to a given destination
//
// Deprecated: use NewRaw() and the functional opts pattern instead:
//
// NewRaw(UseDevMode(development), WriteTo(destWriter), RawZapOpts(opts...))
func RawLoggerTo(destWriter io.Writer, development bool, opts ...zap.Option) *zap.Logger {
return NewRaw(UseDevMode(development), WriteTo(destWriter), RawZapOpts(opts...))
}
// Opts allows to manipulate Options
type Opts func(*Options)
// UseDevMode sets the logger to use (or not use) development mode (more
// human-readable output, extra stack traces and logging information, etc).
// See Options.Development
func UseDevMode(enabled bool) Opts {
return func(o *Options) {
o.Development = enabled
}
}
// WriteTo configures the logger to write to the given io.Writer, instead of standard error.
// See Options.DestWritter
func WriteTo(out io.Writer) Opts {
return func(o *Options) {
o.DestWritter = out
}
}
// Encoder configures how the logger will encode the output e.g JSON or console.
// See Options.Encoder
func Encoder(encoder zapcore.Encoder) func(o *Options) {
return func(o *Options) {
o.Encoder = encoder
}
}
// Level sets the the minimum enabled logging level e.g Debug, Info
// See Options.Level
func Level(level *zap.AtomicLevel) func(o *Options) {
return func(o *Options) {
o.Level = level
}
}
// StacktraceLevel configures the logger to record a stack trace for all messages at
// or above a given level.
// See Options.StacktraceLevel
func StacktraceLevel(stacktraceLevel *zap.AtomicLevel) func(o *Options) {
return func(o *Options) {
o.StacktraceLevel = stacktraceLevel
}
}
// RawZapOpts allows appending arbitrary zap.Options to configure the underlying zap logger.
// See Options.ZapOpts
func RawZapOpts(zapOpts ...zap.Option) func(o *Options) {
return func(o *Options) {
o.ZapOpts = append(o.ZapOpts, zapOpts...)
}
}
// Options contains all possible settings
type Options struct {
// Development configures the logger to use a Zap development config
// (stacktraces on warnings, no sampling), otherwise a Zap production
// config will be used (stacktraces on errors, sampling).
Development bool
// Encoder configures how Zap will encode the output. Defaults to
// console when Development is true and JSON otherwise
Encoder zapcore.Encoder
// DestWritter controls the destination of the log output. Defaults to
// os.Stderr.
DestWritter io.Writer
// Level configures the verbosity of the logging. Defaults to Debug when
// Development is true and Info otherwise
Level *zap.AtomicLevel
// StacktraceLevel is the level at and above which stacktraces will
// be recorded for all messages. Defaults to Warn when Development
// is true and Error otherwise
StacktraceLevel *zap.AtomicLevel
// ZapOpts allows passing arbitrary zap.Options to configure on the
// underlying Zap logger.
ZapOpts []zap.Option
}
// addDefaults adds defaults to the Options
func (o *Options) addDefaults() {
if o.DestWritter == nil {
o.DestWritter = os.Stderr
}
if o.Development {
if o.Encoder == nil {
encCfg := zap.NewDevelopmentEncoderConfig()
o.Encoder = zapcore.NewConsoleEncoder(encCfg)
}
if o.Level == nil {
lvl := zap.NewAtomicLevelAt(zap.DebugLevel)
o.Level = &lvl
}
if o.StacktraceLevel == nil {
lvl := zap.NewAtomicLevelAt(zap.WarnLevel)
o.StacktraceLevel = &lvl
}
o.ZapOpts = append(o.ZapOpts, zap.Development())
} else {
if o.Encoder == nil {
encCfg := zap.NewProductionEncoderConfig()
o.Encoder = zapcore.NewJSONEncoder(encCfg)
}
if o.Level == nil {
lvl := zap.NewAtomicLevelAt(zap.InfoLevel)
o.Level = &lvl
}
if o.StacktraceLevel == nil {
lvl := zap.NewAtomicLevelAt(zap.ErrorLevel)
o.StacktraceLevel = &lvl
}
o.ZapOpts = append(o.ZapOpts,
zap.WrapCore(func(core zapcore.Core) zapcore.Core {
return zapcore.NewSampler(core, time.Second, 100, 100)
}))
}
o.ZapOpts = append(o.ZapOpts, zap.AddStacktrace(o.StacktraceLevel))
}
// NewRaw returns a new zap.Logger configured with the passed Opts
// or their defaults. It uses KubeAwareEncoder which adds Type
// information and Namespace/Name to the log.
func NewRaw(opts ...Opts) *zap.Logger {
o := &Options{}
for _, opt := range opts {
opt(o)
}
o.addDefaults()
// this basically mimics New<type>Config, but with a custom sink
sink := zapcore.AddSync(o.DestWritter)
o.ZapOpts = append(o.ZapOpts, zap.AddCallerSkip(1), zap.ErrorOutput(sink))
log := zap.New(zapcore.NewCore(&KubeAwareEncoder{Encoder: o.Encoder, Verbose: o.Development}, sink, *o.Level))
log = log.WithOptions(o.ZapOpts...)
return log
}

View File

@@ -1,58 +0,0 @@
/*
Copyright 2018 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 log contains (deprecated) utilities for fetching a new logger when
// one is not already available.
//
// Deprecated: use pkg/log
package log
import (
"github.com/go-logr/logr"
"sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
)
var (
// ZapLogger is a Logger implementation.
// If development is true, a Zap development config will be used
// (stacktraces on warnings, no sampling), otherwise a Zap production
// config will be used (stacktraces on errors, sampling).
ZapLogger = zap.Logger
// ZapLoggerTo returns a new Logger implementation using Zap which logs
// to the given destination, instead of stderr. It otherwise behaves like
// ZapLogger.
ZapLoggerTo = zap.LoggerTo
// SetLogger sets a concrete logging implementation for all deferred Loggers.
SetLogger = log.SetLogger
// Log is the base logger used by kubebuilder. It delegates
// to another logr.Logger. You *must* call SetLogger to
// get any actual logging.
Log = log.Log
// KBLog is a base parent logger for use inside controller-runtime.
// Deprecated: don't use this outside controller-runtime
// (inside CR, use pkg/internal/log.RuntimeLog)
KBLog logr.Logger
)
func init() {
KBLog = log.Log.WithName("controller-runtime")
}

View File

@@ -1,349 +0,0 @@
/*
Copyright 2019 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 conversion provides implementation for CRD conversion webhook that implements handler for version conversion requests for types that are convertible.
See pkg/conversion for interface definitions required to ensure an API Type is convertible.
*/
package conversion
import (
"encoding/json"
"fmt"
"net/http"
apix "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/controller-runtime/pkg/conversion"
logf "sigs.k8s.io/controller-runtime/pkg/log"
)
var (
log = logf.Log.WithName("conversion-webhook")
)
// Webhook implements a CRD conversion webhook HTTP handler.
type Webhook struct {
scheme *runtime.Scheme
decoder *Decoder
}
// InjectScheme injects a scheme into the webhook, in order to construct a Decoder.
func (wh *Webhook) InjectScheme(s *runtime.Scheme) error {
var err error
wh.scheme = s
wh.decoder, err = NewDecoder(s)
if err != nil {
return err
}
return nil
}
// ensure Webhook implements http.Handler
var _ http.Handler = &Webhook{}
func (wh *Webhook) ServeHTTP(w http.ResponseWriter, r *http.Request) {
convertReview := &apix.ConversionReview{}
err := json.NewDecoder(r.Body).Decode(convertReview)
if err != nil {
log.Error(err, "failed to read conversion request")
w.WriteHeader(http.StatusBadRequest)
return
}
// TODO(droot): may be move the conversion logic to a separate module to
// decouple it from the http layer ?
resp, err := wh.handleConvertRequest(convertReview.Request)
if err != nil {
log.Error(err, "failed to convert", "request", convertReview.Request.UID)
convertReview.Response = errored(err)
} else {
convertReview.Response = resp
}
convertReview.Response.UID = convertReview.Request.UID
convertReview.Request = nil
err = json.NewEncoder(w).Encode(convertReview)
if err != nil {
log.Error(err, "failed to write response")
return
}
}
// handles a version conversion request.
func (wh *Webhook) handleConvertRequest(req *apix.ConversionRequest) (*apix.ConversionResponse, error) {
if req == nil {
return nil, fmt.Errorf("conversion request is nil")
}
var objects []runtime.RawExtension
for _, obj := range req.Objects {
src, gvk, err := wh.decoder.Decode(obj.Raw)
if err != nil {
return nil, err
}
dst, err := wh.allocateDstObject(req.DesiredAPIVersion, gvk.Kind)
if err != nil {
return nil, err
}
err = wh.convertObject(src, dst)
if err != nil {
return nil, err
}
objects = append(objects, runtime.RawExtension{Object: dst})
}
return &apix.ConversionResponse{
UID: req.UID,
ConvertedObjects: objects,
Result: metav1.Status{
Status: metav1.StatusSuccess,
},
}, nil
}
// convertObject will convert given a src object to dst object.
// Note(droot): couldn't find a way to reduce the cyclomatic complexity under 10
// without compromising readability, so disabling gocyclo linter
func (wh *Webhook) convertObject(src, dst runtime.Object) error {
srcGVK := src.GetObjectKind().GroupVersionKind()
dstGVK := dst.GetObjectKind().GroupVersionKind()
if srcGVK.GroupKind() != dstGVK.GroupKind() {
return fmt.Errorf("src %T and dst %T does not belong to same API Group", src, dst)
}
if srcGVK == dstGVK {
return fmt.Errorf("conversion is not allowed between same type %T", src)
}
srcIsHub, dstIsHub := isHub(src), isHub(dst)
srcIsConvertible, dstIsConvertible := isConvertible(src), isConvertible(dst)
switch {
case srcIsHub && dstIsConvertible:
return dst.(conversion.Convertible).ConvertFrom(src.(conversion.Hub))
case dstIsHub && srcIsConvertible:
return src.(conversion.Convertible).ConvertTo(dst.(conversion.Hub))
case srcIsConvertible && dstIsConvertible:
return wh.convertViaHub(src.(conversion.Convertible), dst.(conversion.Convertible))
default:
return fmt.Errorf("%T is not convertible to %T", src, dst)
}
}
func (wh *Webhook) convertViaHub(src, dst conversion.Convertible) error {
hub, err := wh.getHub(src)
if err != nil {
return err
}
if hub == nil {
return fmt.Errorf("%s does not have any Hub defined", src)
}
err = src.ConvertTo(hub)
if err != nil {
return fmt.Errorf("%T failed to convert to hub version %T : %v", src, hub, err)
}
err = dst.ConvertFrom(hub)
if err != nil {
return fmt.Errorf("%T failed to convert from hub version %T : %v", dst, hub, err)
}
return nil
}
// getHub returns an instance of the Hub for passed-in object's group/kind.
func (wh *Webhook) getHub(obj runtime.Object) (conversion.Hub, error) {
gvks, err := objectGVKs(wh.scheme, obj)
if err != nil {
return nil, err
}
if len(gvks) == 0 {
return nil, fmt.Errorf("error retrieving gvks for object : %v", obj)
}
var hub conversion.Hub
var hubFoundAlready bool
for _, gvk := range gvks {
instance, err := wh.scheme.New(gvk)
if err != nil {
return nil, fmt.Errorf("failed to allocate an instance for gvk %v %v", gvk, err)
}
if val, isHub := instance.(conversion.Hub); isHub {
if hubFoundAlready {
return nil, fmt.Errorf("multiple hub version defined for %T", obj)
}
hubFoundAlready = true
hub = val
}
}
return hub, nil
}
// allocateDstObject returns an instance for a given GVK.
func (wh *Webhook) allocateDstObject(apiVersion, kind string) (runtime.Object, error) {
gvk := schema.FromAPIVersionAndKind(apiVersion, kind)
obj, err := wh.scheme.New(gvk)
if err != nil {
return obj, err
}
t, err := meta.TypeAccessor(obj)
if err != nil {
return obj, err
}
t.SetAPIVersion(apiVersion)
t.SetKind(kind)
return obj, nil
}
// IsConvertible determines if given type is convertible or not. For a type
// to be convertible, the group-kind needs to have a Hub type defined and all
// non-hub types must be able to convert to/from Hub.
func IsConvertible(scheme *runtime.Scheme, obj runtime.Object) (bool, error) {
var hubs, spokes, nonSpokes []runtime.Object
gvks, err := objectGVKs(scheme, obj)
if err != nil {
return false, err
}
if len(gvks) == 0 {
return false, fmt.Errorf("error retrieving gvks for object : %v", obj)
}
for _, gvk := range gvks {
instance, err := scheme.New(gvk)
if err != nil {
return false, fmt.Errorf("failed to allocate an instance for gvk %v %v", gvk, err)
}
if isHub(instance) {
hubs = append(hubs, instance)
continue
}
if !isConvertible(instance) {
nonSpokes = append(nonSpokes, instance)
continue
}
spokes = append(spokes, instance)
}
if len(gvks) == 1 {
return false, nil // single version
}
if len(hubs) == 0 && len(spokes) == 0 {
// multiple version detected with no conversion implementation. This is
// true for multi-version built-in types.
return false, nil
}
if len(hubs) == 1 && len(nonSpokes) == 0 { // convertible
spokeVersions := []string{}
for _, sp := range spokes {
spokeVersions = append(spokeVersions, sp.GetObjectKind().GroupVersionKind().String())
}
return true, nil
}
return false, PartialImplementationError{
hubs: hubs,
nonSpokes: nonSpokes,
spokes: spokes,
}
}
// objectGVKs returns all (Group,Version,Kind) for the Group/Kind of given object.
func objectGVKs(scheme *runtime.Scheme, obj runtime.Object) ([]schema.GroupVersionKind, error) {
// NB: we should not use `obj.GetObjectKind().GroupVersionKind()` to get the
// GVK here, since it is parsed from apiVersion and kind fields and it may
// return empty GVK if obj is an uninitialized object.
objGVKs, _, err := scheme.ObjectKinds(obj)
if err != nil {
return nil, err
}
if len(objGVKs) != 1 {
return nil, fmt.Errorf("expect to get only one GVK for %v", obj)
}
objGVK := objGVKs[0]
knownTypes := scheme.AllKnownTypes()
var gvks []schema.GroupVersionKind
for gvk := range knownTypes {
if objGVK.GroupKind() == gvk.GroupKind() {
gvks = append(gvks, gvk)
}
}
return gvks, nil
}
// PartialImplementationError represents an error due to partial conversion
// implementation such as hub without spokes, multiple hubs or spokes without hub.
type PartialImplementationError struct {
gvk schema.GroupVersionKind
hubs []runtime.Object
nonSpokes []runtime.Object
spokes []runtime.Object
}
func (e PartialImplementationError) Error() string {
if len(e.hubs) == 0 {
return fmt.Sprintf("no hub defined for gvk %s", e.gvk)
}
if len(e.hubs) > 1 {
return fmt.Sprintf("multiple(%d) hubs defined for group-kind '%s' ",
len(e.hubs), e.gvk.GroupKind())
}
if len(e.nonSpokes) > 0 {
return fmt.Sprintf("%d inconvertible types detected for group-kind '%s'",
len(e.nonSpokes), e.gvk.GroupKind())
}
return ""
}
// isHub determines if passed-in object is a Hub or not.
func isHub(obj runtime.Object) bool {
_, yes := obj.(conversion.Hub)
return yes
}
// isConvertible determines if passed-in object is a convertible.
func isConvertible(obj runtime.Object) bool {
_, yes := obj.(conversion.Convertible)
return yes
}
// helper to construct error response.
func errored(err error) *apix.ConversionResponse {
return &apix.ConversionResponse{
Result: metav1.Status{
Status: metav1.StatusFailure,
Message: err.Error(),
},
}
}

View File

@@ -1,31 +0,0 @@
package conversion
import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer"
)
// Decoder knows how to decode the contents of a CRD version conversion
// request into a concrete object.
// TODO(droot): consider reusing decoder from admission pkg for this.
type Decoder struct {
codecs serializer.CodecFactory
}
// NewDecoder creates a Decoder given the runtime.Scheme
func NewDecoder(scheme *runtime.Scheme) (*Decoder, error) {
return &Decoder{codecs: serializer.NewCodecFactory(scheme)}, nil
}
// Decode decodes the inlined object.
func (d *Decoder) Decode(content []byte) (runtime.Object, *schema.GroupVersionKind, error) {
deserializer := d.codecs.UniversalDeserializer()
return deserializer.Decode(content, nil, nil)
}
// DecodeInto decodes the inlined object in the into the passed-in runtime.Object.
func (d *Decoder) DecodeInto(content []byte, into runtime.Object) error {
deserializer := d.codecs.UniversalDeserializer()
return runtime.DecodeInto(deserializer, content, into)
}