mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-20 18:31:15 +00:00
Merge pull request #112789 from enj/enj/r/kms_load_once_v2
Load encryption config once (second approach)
This commit is contained in:
commit
01019770cf
@ -37,7 +37,6 @@ import (
|
|||||||
genericfeatures "k8s.io/apiserver/pkg/features"
|
genericfeatures "k8s.io/apiserver/pkg/features"
|
||||||
genericapiserver "k8s.io/apiserver/pkg/server"
|
genericapiserver "k8s.io/apiserver/pkg/server"
|
||||||
"k8s.io/apiserver/pkg/server/healthz"
|
"k8s.io/apiserver/pkg/server/healthz"
|
||||||
genericoptions "k8s.io/apiserver/pkg/server/options"
|
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
kubeexternalinformers "k8s.io/client-go/informers"
|
kubeexternalinformers "k8s.io/client-go/informers"
|
||||||
"k8s.io/client-go/tools/cache"
|
"k8s.io/client-go/tools/cache"
|
||||||
@ -91,11 +90,16 @@ func createAggregatorConfig(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// copy the etcd options so we don't mutate originals.
|
// copy the etcd options so we don't mutate originals.
|
||||||
|
// we assume that the etcd options have been completed already. avoid messing with anything outside
|
||||||
|
// of changes to StorageConfig as that may lead to unexpected behavior when the options are applied.
|
||||||
etcdOptions := *commandOptions.Etcd
|
etcdOptions := *commandOptions.Etcd
|
||||||
etcdOptions.StorageConfig.Paging = utilfeature.DefaultFeatureGate.Enabled(genericfeatures.APIListChunking)
|
etcdOptions.StorageConfig.Paging = utilfeature.DefaultFeatureGate.Enabled(genericfeatures.APIListChunking)
|
||||||
etcdOptions.StorageConfig.Codec = aggregatorscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion, v1beta1.SchemeGroupVersion)
|
etcdOptions.StorageConfig.Codec = aggregatorscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion, v1beta1.SchemeGroupVersion)
|
||||||
etcdOptions.StorageConfig.EncodeVersioner = runtime.NewMultiGroupVersioner(v1.SchemeGroupVersion, schema.GroupKind{Group: v1beta1.GroupName})
|
etcdOptions.StorageConfig.EncodeVersioner = runtime.NewMultiGroupVersioner(v1.SchemeGroupVersion, schema.GroupKind{Group: v1beta1.GroupName})
|
||||||
genericConfig.RESTOptionsGetter = &genericoptions.SimpleRestOptionsFactory{Options: etcdOptions}
|
etcdOptions.SkipHealthEndpoints = true // avoid double wiring of health checks
|
||||||
|
if err := etcdOptions.ApplyTo(&genericConfig); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// override MergedResourceConfig with aggregator defaults and registry
|
// override MergedResourceConfig with aggregator defaults and registry
|
||||||
if err := commandOptions.APIEnablement.ApplyTo(
|
if err := commandOptions.APIEnablement.ApplyTo(
|
||||||
|
@ -29,7 +29,6 @@ import (
|
|||||||
"k8s.io/apiserver/pkg/admission"
|
"k8s.io/apiserver/pkg/admission"
|
||||||
"k8s.io/apiserver/pkg/features"
|
"k8s.io/apiserver/pkg/features"
|
||||||
genericapiserver "k8s.io/apiserver/pkg/server"
|
genericapiserver "k8s.io/apiserver/pkg/server"
|
||||||
genericoptions "k8s.io/apiserver/pkg/server/options"
|
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
"k8s.io/apiserver/pkg/util/webhook"
|
"k8s.io/apiserver/pkg/util/webhook"
|
||||||
kubeexternalinformers "k8s.io/client-go/informers"
|
kubeexternalinformers "k8s.io/client-go/informers"
|
||||||
@ -64,13 +63,18 @@ func createAPIExtensionsConfig(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// copy the etcd options so we don't mutate originals.
|
// copy the etcd options so we don't mutate originals.
|
||||||
|
// we assume that the etcd options have been completed already. avoid messing with anything outside
|
||||||
|
// of changes to StorageConfig as that may lead to unexpected behavior when the options are applied.
|
||||||
etcdOptions := *commandOptions.Etcd
|
etcdOptions := *commandOptions.Etcd
|
||||||
etcdOptions.StorageConfig.Paging = utilfeature.DefaultFeatureGate.Enabled(features.APIListChunking)
|
etcdOptions.StorageConfig.Paging = utilfeature.DefaultFeatureGate.Enabled(features.APIListChunking)
|
||||||
// this is where the true decodable levels come from.
|
// this is where the true decodable levels come from.
|
||||||
etcdOptions.StorageConfig.Codec = apiextensionsapiserver.Codecs.LegacyCodec(v1beta1.SchemeGroupVersion, v1.SchemeGroupVersion)
|
etcdOptions.StorageConfig.Codec = apiextensionsapiserver.Codecs.LegacyCodec(v1beta1.SchemeGroupVersion, v1.SchemeGroupVersion)
|
||||||
// prefer the more compact serialization (v1beta1) for storage until https://issue.k8s.io/82292 is resolved for objects whose v1 serialization is too big but whose v1beta1 serialization can be stored
|
// prefer the more compact serialization (v1beta1) for storage until https://issue.k8s.io/82292 is resolved for objects whose v1 serialization is too big but whose v1beta1 serialization can be stored
|
||||||
etcdOptions.StorageConfig.EncodeVersioner = runtime.NewMultiGroupVersioner(v1beta1.SchemeGroupVersion, schema.GroupKind{Group: v1beta1.GroupName})
|
etcdOptions.StorageConfig.EncodeVersioner = runtime.NewMultiGroupVersioner(v1beta1.SchemeGroupVersion, schema.GroupKind{Group: v1beta1.GroupName})
|
||||||
genericConfig.RESTOptionsGetter = &genericoptions.SimpleRestOptionsFactory{Options: etcdOptions}
|
etcdOptions.SkipHealthEndpoints = true // avoid double wiring of health checks
|
||||||
|
if err := etcdOptions.ApplyTo(&genericConfig); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// override MergedResourceConfig with apiextensions defaults and registry
|
// override MergedResourceConfig with apiextensions defaults and registry
|
||||||
if err := commandOptions.APIEnablement.ApplyTo(
|
if err := commandOptions.APIEnablement.ApplyTo(
|
||||||
|
@ -397,24 +397,23 @@ func buildGenericConfig(
|
|||||||
kubeVersion := version.Get()
|
kubeVersion := version.Get()
|
||||||
genericConfig.Version = &kubeVersion
|
genericConfig.Version = &kubeVersion
|
||||||
|
|
||||||
storageFactoryConfig := kubeapiserver.NewStorageFactoryConfig()
|
|
||||||
storageFactoryConfig.APIResourceConfig = genericConfig.MergedResourceConfig
|
|
||||||
completedStorageFactoryConfig, err := storageFactoryConfig.Complete(s.Etcd)
|
|
||||||
if err != nil {
|
|
||||||
lastErr = err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
storageFactory, lastErr = completedStorageFactoryConfig.New(genericConfig.DrainedNotify())
|
|
||||||
if lastErr != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if genericConfig.EgressSelector != nil {
|
if genericConfig.EgressSelector != nil {
|
||||||
storageFactory.StorageConfig.Transport.EgressLookup = genericConfig.EgressSelector.Lookup
|
s.Etcd.StorageConfig.Transport.EgressLookup = genericConfig.EgressSelector.Lookup
|
||||||
}
|
}
|
||||||
if utilfeature.DefaultFeatureGate.Enabled(genericfeatures.APIServerTracing) {
|
if utilfeature.DefaultFeatureGate.Enabled(genericfeatures.APIServerTracing) {
|
||||||
storageFactory.StorageConfig.Transport.TracerProvider = genericConfig.TracerProvider
|
s.Etcd.StorageConfig.Transport.TracerProvider = genericConfig.TracerProvider
|
||||||
} else {
|
} else {
|
||||||
storageFactory.StorageConfig.Transport.TracerProvider = oteltrace.NewNoopTracerProvider()
|
s.Etcd.StorageConfig.Transport.TracerProvider = oteltrace.NewNoopTracerProvider()
|
||||||
|
}
|
||||||
|
if lastErr = s.Etcd.Complete(genericConfig.StorageObjectCountTracker, genericConfig.DrainedNotify()); lastErr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
storageFactoryConfig := kubeapiserver.NewStorageFactoryConfig()
|
||||||
|
storageFactoryConfig.APIResourceConfig = genericConfig.MergedResourceConfig
|
||||||
|
storageFactory, lastErr = storageFactoryConfig.Complete(s.Etcd).New()
|
||||||
|
if lastErr != nil {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if lastErr = s.Etcd.ApplyWithStorageFactoryTo(storageFactory, genericConfig); lastErr != nil {
|
if lastErr = s.Etcd.ApplyWithStorageFactoryTo(storageFactory, genericConfig); lastErr != nil {
|
||||||
return
|
return
|
||||||
|
@ -89,6 +89,9 @@ func setUp(t *testing.T) (*etcd3testing.EtcdTestServer, Config, *assert.Assertio
|
|||||||
etcdOptions := options.NewEtcdOptions(storageConfig)
|
etcdOptions := options.NewEtcdOptions(storageConfig)
|
||||||
// unit tests don't need watch cache and it leaks lots of goroutines with etcd testing functions during unit tests
|
// unit tests don't need watch cache and it leaks lots of goroutines with etcd testing functions during unit tests
|
||||||
etcdOptions.EnableWatchCache = false
|
etcdOptions.EnableWatchCache = false
|
||||||
|
if err := etcdOptions.Complete(config.GenericConfig.StorageObjectCountTracker, config.GenericConfig.DrainedNotify()); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
err := etcdOptions.ApplyWithStorageFactoryTo(storageFactory, config.GenericConfig)
|
err := etcdOptions.ApplyWithStorageFactoryTo(storageFactory, config.GenericConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -22,7 +22,6 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
serveroptions "k8s.io/apiserver/pkg/server/options"
|
serveroptions "k8s.io/apiserver/pkg/server/options"
|
||||||
"k8s.io/apiserver/pkg/server/options/encryptionconfig"
|
|
||||||
"k8s.io/apiserver/pkg/server/resourceconfig"
|
"k8s.io/apiserver/pkg/server/resourceconfig"
|
||||||
serverstorage "k8s.io/apiserver/pkg/server/storage"
|
serverstorage "k8s.io/apiserver/pkg/server/storage"
|
||||||
"k8s.io/apiserver/pkg/storage/storagebackend"
|
"k8s.io/apiserver/pkg/storage/storagebackend"
|
||||||
@ -58,7 +57,6 @@ func DefaultWatchCacheSizes() map[schema.GroupResource]int {
|
|||||||
|
|
||||||
// NewStorageFactoryConfig returns a new StorageFactoryConfig set up with necessary resource overrides.
|
// NewStorageFactoryConfig returns a new StorageFactoryConfig set up with necessary resource overrides.
|
||||||
func NewStorageFactoryConfig() *StorageFactoryConfig {
|
func NewStorageFactoryConfig() *StorageFactoryConfig {
|
||||||
|
|
||||||
resources := []schema.GroupVersionResource{
|
resources := []schema.GroupVersionResource{
|
||||||
// If a resource has to be stored in a version that is not the
|
// If a resource has to be stored in a version that is not the
|
||||||
// latest, then it can be listed here. Usually this is the case
|
// latest, then it can be listed here. Usually this is the case
|
||||||
@ -83,23 +81,22 @@ func NewStorageFactoryConfig() *StorageFactoryConfig {
|
|||||||
|
|
||||||
// StorageFactoryConfig is a configuration for creating storage factory.
|
// StorageFactoryConfig is a configuration for creating storage factory.
|
||||||
type StorageFactoryConfig struct {
|
type StorageFactoryConfig struct {
|
||||||
StorageConfig storagebackend.Config
|
StorageConfig storagebackend.Config
|
||||||
APIResourceConfig *serverstorage.ResourceConfig
|
APIResourceConfig *serverstorage.ResourceConfig
|
||||||
DefaultResourceEncoding *serverstorage.DefaultResourceEncodingConfig
|
DefaultResourceEncoding *serverstorage.DefaultResourceEncodingConfig
|
||||||
DefaultStorageMediaType string
|
DefaultStorageMediaType string
|
||||||
Serializer runtime.StorageSerializer
|
Serializer runtime.StorageSerializer
|
||||||
ResourceEncodingOverrides []schema.GroupVersionResource
|
ResourceEncodingOverrides []schema.GroupVersionResource
|
||||||
EtcdServersOverrides []string
|
EtcdServersOverrides []string
|
||||||
EncryptionProviderConfigFilepath string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Complete completes the StorageFactoryConfig with provided etcdOptions returning completedStorageFactoryConfig.
|
// Complete completes the StorageFactoryConfig with provided etcdOptions returning completedStorageFactoryConfig.
|
||||||
func (c *StorageFactoryConfig) Complete(etcdOptions *serveroptions.EtcdOptions) (*completedStorageFactoryConfig, error) {
|
// This method mutates the receiver (StorageFactoryConfig). It must never mutate the inputs.
|
||||||
|
func (c *StorageFactoryConfig) Complete(etcdOptions *serveroptions.EtcdOptions) *completedStorageFactoryConfig {
|
||||||
c.StorageConfig = etcdOptions.StorageConfig
|
c.StorageConfig = etcdOptions.StorageConfig
|
||||||
c.DefaultStorageMediaType = etcdOptions.DefaultStorageMediaType
|
c.DefaultStorageMediaType = etcdOptions.DefaultStorageMediaType
|
||||||
c.EtcdServersOverrides = etcdOptions.EtcdServersOverrides
|
c.EtcdServersOverrides = etcdOptions.EtcdServersOverrides
|
||||||
c.EncryptionProviderConfigFilepath = etcdOptions.EncryptionProviderConfigFilepath
|
return &completedStorageFactoryConfig{c}
|
||||||
return &completedStorageFactoryConfig{c}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// completedStorageFactoryConfig is a wrapper around StorageFactoryConfig completed with etcd options.
|
// completedStorageFactoryConfig is a wrapper around StorageFactoryConfig completed with etcd options.
|
||||||
@ -111,7 +108,7 @@ type completedStorageFactoryConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// New returns a new storage factory created from the completed storage factory configuration.
|
// New returns a new storage factory created from the completed storage factory configuration.
|
||||||
func (c *completedStorageFactoryConfig) New(stopCh <-chan struct{}) (*serverstorage.DefaultStorageFactory, error) {
|
func (c *completedStorageFactoryConfig) New() (*serverstorage.DefaultStorageFactory, error) {
|
||||||
resourceEncodingConfig := resourceconfig.MergeResourceEncodingConfigs(c.DefaultResourceEncoding, c.ResourceEncodingOverrides)
|
resourceEncodingConfig := resourceconfig.MergeResourceEncodingConfigs(c.DefaultResourceEncoding, c.ResourceEncodingOverrides)
|
||||||
storageFactory := serverstorage.NewDefaultStorageFactory(
|
storageFactory := serverstorage.NewDefaultStorageFactory(
|
||||||
c.StorageConfig,
|
c.StorageConfig,
|
||||||
@ -141,14 +138,5 @@ func (c *completedStorageFactoryConfig) New(stopCh <-chan struct{}) (*serverstor
|
|||||||
servers := strings.Split(tokens[1], ";")
|
servers := strings.Split(tokens[1], ";")
|
||||||
storageFactory.SetEtcdLocation(groupResource, servers)
|
storageFactory.SetEtcdLocation(groupResource, servers)
|
||||||
}
|
}
|
||||||
if len(c.EncryptionProviderConfigFilepath) != 0 {
|
|
||||||
transformerOverrides, err := encryptionconfig.GetTransformerOverrides(c.EncryptionProviderConfigFilepath, stopCh)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
for groupResource, transformer := range transformerOverrides {
|
|
||||||
storageFactory.SetTransformer(groupResource, transformer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return storageFactory, nil
|
return storageFactory, nil
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,6 @@ limitations under the License.
|
|||||||
package registrytest
|
package registrytest
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
@ -36,18 +35,12 @@ func NewEtcdStorage(t *testing.T, group string) (*storagebackend.ConfigForResour
|
|||||||
func NewEtcdStorageForResource(t *testing.T, resource schema.GroupResource) (*storagebackend.ConfigForResource, *etcd3testing.EtcdTestServer) {
|
func NewEtcdStorageForResource(t *testing.T, resource schema.GroupResource) (*storagebackend.ConfigForResource, *etcd3testing.EtcdTestServer) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
t.Cleanup(cancel)
|
|
||||||
|
|
||||||
server, config := etcd3testing.NewUnsecuredEtcd3TestClientServer(t)
|
server, config := etcd3testing.NewUnsecuredEtcd3TestClientServer(t)
|
||||||
|
|
||||||
options := options.NewEtcdOptions(config)
|
options := options.NewEtcdOptions(config)
|
||||||
completedConfig, err := kubeapiserver.NewStorageFactoryConfig().Complete(options)
|
completedConfig := kubeapiserver.NewStorageFactoryConfig().Complete(options)
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
completedConfig.APIResourceConfig = serverstorage.NewResourceConfig()
|
completedConfig.APIResourceConfig = serverstorage.NewResourceConfig()
|
||||||
factory, err := completedConfig.New(ctx.Done())
|
factory, err := completedConfig.New()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -28,13 +28,13 @@ import (
|
|||||||
|
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
|
|
||||||
|
"k8s.io/apiextensions-apiserver/pkg/apiserver"
|
||||||
"k8s.io/apiextensions-apiserver/pkg/cmd/server/options"
|
"k8s.io/apiextensions-apiserver/pkg/cmd/server/options"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
genericapiserver "k8s.io/apiserver/pkg/server"
|
genericapiserver "k8s.io/apiserver/pkg/server"
|
||||||
"k8s.io/apiserver/pkg/storage/storagebackend"
|
"k8s.io/apiserver/pkg/storage/storagebackend"
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
restclient "k8s.io/client-go/rest"
|
restclient "k8s.io/client-go/rest"
|
||||||
|
|
||||||
"k8s.io/klog/v2"
|
"k8s.io/klog/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -47,10 +47,11 @@ type TestServerInstanceOptions struct {
|
|||||||
|
|
||||||
// TestServer return values supplied by kube-test-ApiServer
|
// TestServer return values supplied by kube-test-ApiServer
|
||||||
type TestServer struct {
|
type TestServer struct {
|
||||||
ClientConfig *restclient.Config // Rest client config
|
ClientConfig *restclient.Config // Rest client config
|
||||||
ServerOpts *options.CustomResourceDefinitionsServerOptions // ServerOpts
|
ServerOpts *options.CustomResourceDefinitionsServerOptions // ServerOpts
|
||||||
TearDownFn TearDownFunc // TearDown function
|
TearDownFn TearDownFunc // TearDown function
|
||||||
TmpDir string // Temp Dir used, by the apiserver
|
TmpDir string // Temp Dir used, by the apiserver
|
||||||
|
CompletedConfig apiserver.CompletedConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
// Logger allows t.Testing and b.Testing to be passed to StartTestServer and StartTestServerOrDie
|
// Logger allows t.Testing and b.Testing to be passed to StartTestServer and StartTestServerOrDie
|
||||||
@ -144,7 +145,8 @@ func StartTestServer(t Logger, _ *TestServerInstanceOptions, customFlags []strin
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return result, fmt.Errorf("failed to create config from options: %v", err)
|
return result, fmt.Errorf("failed to create config from options: %v", err)
|
||||||
}
|
}
|
||||||
server, err := config.Complete().New(genericapiserver.NewEmptyDelegate())
|
completedConfig := config.Complete()
|
||||||
|
server, err := completedConfig.New(genericapiserver.NewEmptyDelegate())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return result, fmt.Errorf("failed to create server: %v", err)
|
return result, fmt.Errorf("failed to create server: %v", err)
|
||||||
}
|
}
|
||||||
@ -187,6 +189,7 @@ func StartTestServer(t Logger, _ *TestServerInstanceOptions, customFlags []strin
|
|||||||
result.ClientConfig = server.GenericAPIServer.LoopbackClientConfig
|
result.ClientConfig = server.GenericAPIServer.LoopbackClientConfig
|
||||||
result.ServerOpts = s
|
result.ServerOpts = s
|
||||||
result.TearDownFn = tearDown
|
result.TearDownFn = tearDown
|
||||||
|
result.CompletedConfig = completedConfig
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
@ -26,21 +26,38 @@ import (
|
|||||||
"go.etcd.io/etcd/client/pkg/v3/transport"
|
"go.etcd.io/etcd/client/pkg/v3/transport"
|
||||||
clientv3 "go.etcd.io/etcd/client/v3"
|
clientv3 "go.etcd.io/etcd/client/v3"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
serveroptions "k8s.io/apiextensions-apiserver/pkg/cmd/server/options"
|
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
||||||
|
|
||||||
|
"k8s.io/apiextensions-apiserver/pkg/apiserver"
|
||||||
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
||||||
|
serveroptions "k8s.io/apiextensions-apiserver/pkg/cmd/server/options"
|
||||||
servertesting "k8s.io/apiextensions-apiserver/pkg/cmd/server/testing"
|
servertesting "k8s.io/apiextensions-apiserver/pkg/cmd/server/testing"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"k8s.io/client-go/dynamic"
|
"k8s.io/client-go/dynamic"
|
||||||
"k8s.io/client-go/rest"
|
"k8s.io/client-go/rest"
|
||||||
)
|
)
|
||||||
|
|
||||||
// StartDefaultServer starts a test server.
|
// StartDefaultServer starts a test server.
|
||||||
func StartDefaultServer(t servertesting.Logger, flags ...string) (func(), *rest.Config, *serveroptions.CustomResourceDefinitionsServerOptions, error) {
|
func StartDefaultServer(t servertesting.Logger, flags ...string) (func(), *rest.Config, *serveroptions.CustomResourceDefinitionsServerOptions, error) {
|
||||||
|
tearDownFn, s, err := startDefaultServer(t, flags...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, err
|
||||||
|
}
|
||||||
|
return tearDownFn, s.ClientConfig, s.ServerOpts, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func StartDefaultServerWithConfigAccess(t servertesting.Logger, flags ...string) (func(), *rest.Config, apiserver.CompletedConfig, error) {
|
||||||
|
tearDownFn, s, err := startDefaultServer(t, flags...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, apiserver.CompletedConfig{}, err
|
||||||
|
}
|
||||||
|
return tearDownFn, s.ClientConfig, s.CompletedConfig, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func startDefaultServer(t servertesting.Logger, flags ...string) (func(), servertesting.TestServer, error) {
|
||||||
// create kubeconfig which will not actually be used. But authz/authn needs it to startup.
|
// create kubeconfig which will not actually be used. But authz/authn needs it to startup.
|
||||||
fakeKubeConfig, err := ioutil.TempFile("", "kubeconfig")
|
fakeKubeConfig, err := ioutil.TempFile("", "kubeconfig")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, err
|
return nil, servertesting.TestServer{}, err
|
||||||
}
|
}
|
||||||
fakeKubeConfig.WriteString(`
|
fakeKubeConfig.WriteString(`
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
@ -75,7 +92,7 @@ users:
|
|||||||
), nil)
|
), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
os.Remove(fakeKubeConfig.Name())
|
os.Remove(fakeKubeConfig.Name())
|
||||||
return nil, nil, nil, err
|
return nil, servertesting.TestServer{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
tearDownFn := func() {
|
tearDownFn := func() {
|
||||||
@ -83,7 +100,7 @@ users:
|
|||||||
s.TearDownFn()
|
s.TearDownFn()
|
||||||
}
|
}
|
||||||
|
|
||||||
return tearDownFn, s.ClientConfig, s.ServerOpts, nil
|
return tearDownFn, s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// StartDefaultServerWithClients starts a test server and returns clients for it.
|
// StartDefaultServerWithClients starts a test server and returns clients for it.
|
||||||
|
@ -298,7 +298,7 @@ func TestPruningStatus(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestPruningFromStorage(t *testing.T) {
|
func TestPruningFromStorage(t *testing.T) {
|
||||||
tearDown, config, options, err := fixtures.StartDefaultServer(t)
|
tearDown, config, completedConfig, err := fixtures.StartDefaultServerWithConfigAccess(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -314,11 +314,6 @@ func TestPruningFromStorage(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
serverConfig, err := options.Config()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
crd := pruningFixture.DeepCopy()
|
crd := pruningFixture.DeepCopy()
|
||||||
crd.Spec.Versions[0].Schema = &apiextensionsv1.CustomResourceValidation{}
|
crd.Spec.Versions[0].Schema = &apiextensionsv1.CustomResourceValidation{}
|
||||||
if err := yaml.Unmarshal([]byte(fooSchema), &crd.Spec.Versions[0].Schema.OpenAPIV3Schema); err != nil {
|
if err := yaml.Unmarshal([]byte(fooSchema), &crd.Spec.Versions[0].Schema.OpenAPIV3Schema); err != nil {
|
||||||
@ -330,7 +325,7 @@ func TestPruningFromStorage(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
restOptions, err := serverConfig.GenericConfig.RESTOptionsGetter.GetRESTOptions(schema.GroupResource{Group: crd.Spec.Group, Resource: crd.Spec.Names.Plural})
|
restOptions, err := completedConfig.GenericConfig.RESTOptionsGetter.GetRESTOptions(schema.GroupResource{Group: crd.Spec.Group, Resource: crd.Spec.Names.Plural})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -43,10 +43,12 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
aesKeySizes = []int{16, 24, 32}
|
|
||||||
// See https://golang.org/pkg/crypto/aes/#NewCipher for details on supported key sizes for AES.
|
// See https://golang.org/pkg/crypto/aes/#NewCipher for details on supported key sizes for AES.
|
||||||
secretBoxKeySizes = []int{32}
|
aesKeySizes = []int{16, 24, 32}
|
||||||
|
|
||||||
// See https://godoc.org/golang.org/x/crypto/nacl/secretbox#Open for details on the supported key sizes for Secretbox.
|
// See https://godoc.org/golang.org/x/crypto/nacl/secretbox#Open for details on the supported key sizes for Secretbox.
|
||||||
|
secretBoxKeySizes = []int{32}
|
||||||
|
|
||||||
root = field.NewPath("resources")
|
root = field.NewPath("resources")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -94,16 +94,6 @@ func (p *kmsv2PluginProbe) toHealthzCheck(idx int) healthz.HealthChecker {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetKMSPluginHealthzCheckers(filepath string, stopCh <-chan struct{}) ([]healthz.HealthChecker, error) {
|
|
||||||
_, kmsHealthChecks, err := LoadEncryptionConfig(filepath, stopCh)
|
|
||||||
return kmsHealthChecks, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetTransformerOverrides(filepath string, stopCh <-chan struct{}) (map[schema.GroupResource]value.Transformer, error) {
|
|
||||||
transformers, _, err := LoadEncryptionConfig(filepath, stopCh)
|
|
||||||
return transformers, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func LoadEncryptionConfig(filepath string, stopCh <-chan struct{}) (map[schema.GroupResource]value.Transformer, []healthz.HealthChecker, error) {
|
func LoadEncryptionConfig(filepath string, stopCh <-chan struct{}) (map[schema.GroupResource]value.Transformer, []healthz.HealthChecker, error) {
|
||||||
config, err := loadConfig(filepath)
|
config, err := loadConfig(filepath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -159,7 +149,7 @@ func getTransformerOverridesAndKMSPluginProbes(config *apiserverconfig.Encryptio
|
|||||||
for gr, transList := range resourceToPrefixTransformer {
|
for gr, transList := range resourceToPrefixTransformer {
|
||||||
gr := gr
|
gr := gr
|
||||||
transList := transList
|
transList := transList
|
||||||
transformers[gr] = value.NewMutableTransformer(value.NewPrefixTransformers(fmt.Errorf("no matching prefix found"), transList...))
|
transformers[gr] = value.NewPrefixTransformers(fmt.Errorf("no matching prefix found"), transList...)
|
||||||
}
|
}
|
||||||
|
|
||||||
return transformers, probes, nil
|
return transformers, probes, nil
|
||||||
@ -425,8 +415,8 @@ var (
|
|||||||
// The factory to create kms service. This is to make writing test easier.
|
// The factory to create kms service. This is to make writing test easier.
|
||||||
envelopeServiceFactory = envelope.NewGRPCService
|
envelopeServiceFactory = envelope.NewGRPCService
|
||||||
|
|
||||||
// The factory to create kmsv2 service.
|
// The factory to create kmsv2 service. Exported for integration tests.
|
||||||
envelopeKMSv2ServiceFactory = envelopekmsv2.NewGRPCService
|
EnvelopeKMSv2ServiceFactory = envelopekmsv2.NewGRPCService
|
||||||
)
|
)
|
||||||
|
|
||||||
func kmsPrefixTransformer(config *apiserverconfig.KMSConfiguration, stopCh <-chan struct{}) (value.PrefixTransformer, healthChecker, error) {
|
func kmsPrefixTransformer(config *apiserverconfig.KMSConfiguration, stopCh <-chan struct{}) (value.PrefixTransformer, healthChecker, error) {
|
||||||
@ -458,7 +448,7 @@ func kmsPrefixTransformer(config *apiserverconfig.KMSConfiguration, stopCh <-cha
|
|||||||
return value.PrefixTransformer{}, nil, fmt.Errorf("could not configure KMSv2 plugin %q, KMSv2 feature is not enabled", kmsName)
|
return value.PrefixTransformer{}, nil, fmt.Errorf("could not configure KMSv2 plugin %q, KMSv2 feature is not enabled", kmsName)
|
||||||
}
|
}
|
||||||
|
|
||||||
envelopeService, err := envelopeKMSv2ServiceFactory(ctx, config.Endpoint, config.Timeout.Duration)
|
envelopeService, err := EnvelopeKMSv2ServiceFactory(ctx, config.Endpoint, config.Timeout.Duration)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return value.PrefixTransformer{}, nil, fmt.Errorf("could not configure KMSv2-Plugin's probe %q, error: %v", kmsName, err)
|
return value.PrefixTransformer{}, nil, fmt.Errorf("could not configure KMSv2-Plugin's probe %q, error: %v", kmsName, err)
|
||||||
}
|
}
|
||||||
|
@ -163,12 +163,12 @@ func TestEncryptionProviderConfigCorrect(t *testing.T) {
|
|||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.KMSv2, true)()
|
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.KMSv2, true)()
|
||||||
// Set factory for mock envelope service
|
// Set factory for mock envelope service
|
||||||
factory := envelopeServiceFactory
|
factory := envelopeServiceFactory
|
||||||
factoryKMSv2 := envelopeKMSv2ServiceFactory
|
factoryKMSv2 := EnvelopeKMSv2ServiceFactory
|
||||||
envelopeServiceFactory = newMockEnvelopeService
|
envelopeServiceFactory = newMockEnvelopeService
|
||||||
envelopeKMSv2ServiceFactory = newMockEnvelopeKMSv2Service
|
EnvelopeKMSv2ServiceFactory = newMockEnvelopeKMSv2Service
|
||||||
defer func() {
|
defer func() {
|
||||||
envelopeServiceFactory = factory
|
envelopeServiceFactory = factory
|
||||||
envelopeKMSv2ServiceFactory = factoryKMSv2
|
EnvelopeKMSv2ServiceFactory = factoryKMSv2
|
||||||
}()
|
}()
|
||||||
|
|
||||||
ctx := testContext(t)
|
ctx := testContext(t)
|
||||||
|
@ -36,6 +36,7 @@ import (
|
|||||||
"k8s.io/apiserver/pkg/storage/storagebackend"
|
"k8s.io/apiserver/pkg/storage/storagebackend"
|
||||||
storagefactory "k8s.io/apiserver/pkg/storage/storagebackend/factory"
|
storagefactory "k8s.io/apiserver/pkg/storage/storagebackend/factory"
|
||||||
"k8s.io/apiserver/pkg/storage/value"
|
"k8s.io/apiserver/pkg/storage/value"
|
||||||
|
flowcontrolrequest "k8s.io/apiserver/pkg/util/flowcontrol/request"
|
||||||
"k8s.io/klog/v2"
|
"k8s.io/klog/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -59,6 +60,15 @@ type EtcdOptions struct {
|
|||||||
DefaultWatchCacheSize int
|
DefaultWatchCacheSize int
|
||||||
// WatchCacheSizes represents override to a given resource
|
// WatchCacheSizes represents override to a given resource
|
||||||
WatchCacheSizes []string
|
WatchCacheSizes []string
|
||||||
|
|
||||||
|
// complete guards fields that must be initialized via Complete before the Apply methods can be used.
|
||||||
|
complete bool
|
||||||
|
transformerOverrides map[schema.GroupResource]value.Transformer
|
||||||
|
kmsPluginHealthzChecks []healthz.HealthChecker
|
||||||
|
|
||||||
|
// SkipHealthEndpoints, when true, causes the Apply methods to not set up health endpoints.
|
||||||
|
// This allows multiple invocations of the Apply methods without duplication of said endpoints.
|
||||||
|
SkipHealthEndpoints bool
|
||||||
}
|
}
|
||||||
|
|
||||||
var storageTypes = sets.NewString(
|
var storageTypes = sets.NewString(
|
||||||
@ -190,39 +200,65 @@ func (s *EtcdOptions) AddFlags(fs *pflag.FlagSet) {
|
|||||||
"The time in seconds that each lease is reused. A lower value could avoid large number of objects reusing the same lease. Notice that a too small value may cause performance problems at storage layer.")
|
"The time in seconds that each lease is reused. A lower value could avoid large number of objects reusing the same lease. Notice that a too small value may cause performance problems at storage layer.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Complete must be called exactly once before using any of the Apply methods. It is responsible for setting
|
||||||
|
// up objects that must be created once and reused across multiple invocations such as storage transformers.
|
||||||
|
// This method mutates the receiver (EtcdOptions). It must never mutate the inputs.
|
||||||
|
func (s *EtcdOptions) Complete(storageObjectCountTracker flowcontrolrequest.StorageObjectCountTracker, stopCh <-chan struct{}) error {
|
||||||
|
if s == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.complete {
|
||||||
|
return fmt.Errorf("EtcdOptions.Complete called more than once")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(s.EncryptionProviderConfigFilepath) != 0 {
|
||||||
|
transformerOverrides, kmsPluginHealthzChecks, err := encryptionconfig.LoadEncryptionConfig(s.EncryptionProviderConfigFilepath, stopCh)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s.transformerOverrides = transformerOverrides
|
||||||
|
s.kmsPluginHealthzChecks = kmsPluginHealthzChecks
|
||||||
|
}
|
||||||
|
|
||||||
|
s.StorageConfig.StorageObjectCountTracker = storageObjectCountTracker
|
||||||
|
|
||||||
|
s.complete = true
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplyTo mutates the provided server.Config. It must never mutate the receiver (EtcdOptions).
|
||||||
func (s *EtcdOptions) ApplyTo(c *server.Config) error {
|
func (s *EtcdOptions) ApplyTo(c *server.Config) error {
|
||||||
if s == nil {
|
if s == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if err := s.addEtcdHealthEndpoint(c); err != nil {
|
|
||||||
return err
|
return s.ApplyWithStorageFactoryTo(&SimpleStorageFactory{StorageConfig: s.StorageConfig}, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplyWithStorageFactoryTo mutates the provided server.Config. It must never mutate the receiver (EtcdOptions).
|
||||||
|
func (s *EtcdOptions) ApplyWithStorageFactoryTo(factory serverstorage.StorageFactory, c *server.Config) error {
|
||||||
|
if s == nil {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
transformerOverrides := make(map[schema.GroupResource]value.Transformer)
|
|
||||||
if len(s.EncryptionProviderConfigFilepath) > 0 {
|
if !s.complete {
|
||||||
var err error
|
return fmt.Errorf("EtcdOptions.Apply called without completion")
|
||||||
transformerOverrides, err = encryptionconfig.GetTransformerOverrides(s.EncryptionProviderConfigFilepath, c.DrainedNotify())
|
}
|
||||||
if err != nil {
|
|
||||||
|
if !s.SkipHealthEndpoints {
|
||||||
|
if err := s.addEtcdHealthEndpoint(c); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// use the StorageObjectCountTracker interface instance from server.Config
|
if len(s.transformerOverrides) > 0 {
|
||||||
s.StorageConfig.StorageObjectCountTracker = c.StorageObjectCountTracker
|
factory = &transformerStorageFactory{
|
||||||
|
delegate: factory,
|
||||||
c.RESTOptionsGetter = &SimpleRestOptionsFactory{
|
transformerOverrides: s.transformerOverrides,
|
||||||
Options: *s,
|
}
|
||||||
TransformerOverrides: transformerOverrides,
|
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *EtcdOptions) ApplyWithStorageFactoryTo(factory serverstorage.StorageFactory, c *server.Config) error {
|
|
||||||
if err := s.addEtcdHealthEndpoint(c); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// use the StorageObjectCountTracker interface instance from server.Config
|
|
||||||
s.StorageConfig.StorageObjectCountTracker = c.StorageObjectCountTracker
|
|
||||||
|
|
||||||
c.RESTOptionsGetter = &StorageFactoryRestOptionsFactory{Options: *s, StorageFactory: factory}
|
c.RESTOptionsGetter = &StorageFactoryRestOptionsFactory{Options: *s, StorageFactory: factory}
|
||||||
return nil
|
return nil
|
||||||
@ -245,57 +281,11 @@ func (s *EtcdOptions) addEtcdHealthEndpoint(c *server.Config) error {
|
|||||||
return readyCheck()
|
return readyCheck()
|
||||||
}))
|
}))
|
||||||
|
|
||||||
if s.EncryptionProviderConfigFilepath != "" {
|
c.AddHealthChecks(s.kmsPluginHealthzChecks...)
|
||||||
kmsPluginHealthzChecks, err := encryptionconfig.GetKMSPluginHealthzCheckers(s.EncryptionProviderConfigFilepath, c.DrainedNotify())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
c.AddHealthChecks(kmsPluginHealthzChecks...)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type SimpleRestOptionsFactory struct {
|
|
||||||
Options EtcdOptions
|
|
||||||
TransformerOverrides map[schema.GroupResource]value.Transformer
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *SimpleRestOptionsFactory) GetRESTOptions(resource schema.GroupResource) (generic.RESTOptions, error) {
|
|
||||||
ret := generic.RESTOptions{
|
|
||||||
StorageConfig: f.Options.StorageConfig.ForResource(resource),
|
|
||||||
Decorator: generic.UndecoratedStorage,
|
|
||||||
EnableGarbageCollection: f.Options.EnableGarbageCollection,
|
|
||||||
DeleteCollectionWorkers: f.Options.DeleteCollectionWorkers,
|
|
||||||
ResourcePrefix: resource.Group + "/" + resource.Resource,
|
|
||||||
CountMetricPollPeriod: f.Options.StorageConfig.CountMetricPollPeriod,
|
|
||||||
StorageObjectCountTracker: f.Options.StorageConfig.StorageObjectCountTracker,
|
|
||||||
}
|
|
||||||
if f.TransformerOverrides != nil {
|
|
||||||
if transformer, ok := f.TransformerOverrides[resource]; ok {
|
|
||||||
ret.StorageConfig.Transformer = transformer
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if f.Options.EnableWatchCache {
|
|
||||||
sizes, err := ParseWatchCacheSizes(f.Options.WatchCacheSizes)
|
|
||||||
if err != nil {
|
|
||||||
return generic.RESTOptions{}, err
|
|
||||||
}
|
|
||||||
size, ok := sizes[resource]
|
|
||||||
if ok && size > 0 {
|
|
||||||
klog.Warningf("Dropping watch-cache-size for %v - watchCache size is now dynamic", resource)
|
|
||||||
}
|
|
||||||
if ok && size <= 0 {
|
|
||||||
klog.V(3).InfoS("Not using watch cache", "resource", resource)
|
|
||||||
ret.Decorator = generic.UndecoratedStorage
|
|
||||||
} else {
|
|
||||||
klog.V(3).InfoS("Using watch cache", "resource", resource)
|
|
||||||
ret.Decorator = genericregistry.StorageWithCacher()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type StorageFactoryRestOptionsFactory struct {
|
type StorageFactoryRestOptionsFactory struct {
|
||||||
Options EtcdOptions
|
Options EtcdOptions
|
||||||
StorageFactory serverstorage.StorageFactory
|
StorageFactory serverstorage.StorageFactory
|
||||||
@ -316,6 +306,7 @@ func (f *StorageFactoryRestOptionsFactory) GetRESTOptions(resource schema.GroupR
|
|||||||
CountMetricPollPeriod: f.Options.StorageConfig.CountMetricPollPeriod,
|
CountMetricPollPeriod: f.Options.StorageConfig.CountMetricPollPeriod,
|
||||||
StorageObjectCountTracker: f.Options.StorageConfig.StorageObjectCountTracker,
|
StorageObjectCountTracker: f.Options.StorageConfig.StorageObjectCountTracker,
|
||||||
}
|
}
|
||||||
|
|
||||||
if f.Options.EnableWatchCache {
|
if f.Options.EnableWatchCache {
|
||||||
sizes, err := ParseWatchCacheSizes(f.Options.WatchCacheSizes)
|
sizes, err := ParseWatchCacheSizes(f.Options.WatchCacheSizes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -326,8 +317,10 @@ func (f *StorageFactoryRestOptionsFactory) GetRESTOptions(resource schema.GroupR
|
|||||||
klog.Warningf("Dropping watch-cache-size for %v - watchCache size is now dynamic", resource)
|
klog.Warningf("Dropping watch-cache-size for %v - watchCache size is now dynamic", resource)
|
||||||
}
|
}
|
||||||
if ok && size <= 0 {
|
if ok && size <= 0 {
|
||||||
|
klog.V(3).InfoS("Not using watch cache", "resource", resource)
|
||||||
ret.Decorator = generic.UndecoratedStorage
|
ret.Decorator = generic.UndecoratedStorage
|
||||||
} else {
|
} else {
|
||||||
|
klog.V(3).InfoS("Using watch cache", "resource", resource)
|
||||||
ret.Decorator = genericregistry.StorageWithCacher()
|
ret.Decorator = genericregistry.StorageWithCacher()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -369,3 +362,60 @@ func WriteWatchCacheSizes(watchCacheSizes map[schema.GroupResource]int) ([]strin
|
|||||||
}
|
}
|
||||||
return cacheSizes, nil
|
return cacheSizes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _ serverstorage.StorageFactory = &SimpleStorageFactory{}
|
||||||
|
|
||||||
|
// SimpleStorageFactory provides a StorageFactory implementation that should be used when different
|
||||||
|
// resources essentially share the same storage config (as defined by the given storagebackend.Config).
|
||||||
|
// It assumes the resources are stored at a path that is purely based on the schema.GroupResource.
|
||||||
|
// Users that need flexibility and per resource overrides should use DefaultStorageFactory instead.
|
||||||
|
type SimpleStorageFactory struct {
|
||||||
|
StorageConfig storagebackend.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SimpleStorageFactory) NewConfig(resource schema.GroupResource) (*storagebackend.ConfigForResource, error) {
|
||||||
|
return s.StorageConfig.ForResource(resource), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SimpleStorageFactory) ResourcePrefix(resource schema.GroupResource) string {
|
||||||
|
return resource.Group + "/" + resource.Resource
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SimpleStorageFactory) Backends() []serverstorage.Backend {
|
||||||
|
// nothing should ever call this method but we still provide a functional implementation
|
||||||
|
return serverstorage.Backends(s.StorageConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ serverstorage.StorageFactory = &transformerStorageFactory{}
|
||||||
|
|
||||||
|
type transformerStorageFactory struct {
|
||||||
|
delegate serverstorage.StorageFactory
|
||||||
|
transformerOverrides map[schema.GroupResource]value.Transformer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *transformerStorageFactory) NewConfig(resource schema.GroupResource) (*storagebackend.ConfigForResource, error) {
|
||||||
|
config, err := t.delegate.NewConfig(resource)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
transformer, ok := t.transformerOverrides[resource]
|
||||||
|
if !ok {
|
||||||
|
return config, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
configCopy := *config
|
||||||
|
resourceConfig := configCopy.Config
|
||||||
|
resourceConfig.Transformer = transformer
|
||||||
|
configCopy.Config = resourceConfig
|
||||||
|
|
||||||
|
return &configCopy, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *transformerStorageFactory) ResourcePrefix(resource schema.GroupResource) string {
|
||||||
|
return t.delegate.ResourcePrefix(resource)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *transformerStorageFactory) Backends() []serverstorage.Backend {
|
||||||
|
return t.delegate.Backends()
|
||||||
|
}
|
||||||
|
@ -204,6 +204,7 @@ func TestKMSHealthzEndpoint(t *testing.T) {
|
|||||||
name string
|
name string
|
||||||
encryptionConfigPath string
|
encryptionConfigPath string
|
||||||
wantChecks []string
|
wantChecks []string
|
||||||
|
skipHealth bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "single kms-provider, expect single kms healthz check",
|
name: "single kms-provider, expect single kms healthz check",
|
||||||
@ -215,6 +216,12 @@ func TestKMSHealthzEndpoint(t *testing.T) {
|
|||||||
encryptionConfigPath: "testdata/encryption-configs/multiple-kms-providers.yaml",
|
encryptionConfigPath: "testdata/encryption-configs/multiple-kms-providers.yaml",
|
||||||
wantChecks: []string{"etcd", "kms-provider-0", "kms-provider-1"},
|
wantChecks: []string{"etcd", "kms-provider-0", "kms-provider-1"},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "two kms-providers with skip, expect zero kms healthz checks",
|
||||||
|
encryptionConfigPath: "testdata/encryption-configs/multiple-kms-providers.yaml",
|
||||||
|
wantChecks: nil,
|
||||||
|
skipHealth: true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
scheme := runtime.NewScheme()
|
scheme := runtime.NewScheme()
|
||||||
@ -225,8 +232,12 @@ func TestKMSHealthzEndpoint(t *testing.T) {
|
|||||||
serverConfig := server.NewConfig(codecs)
|
serverConfig := server.NewConfig(codecs)
|
||||||
etcdOptions := &EtcdOptions{
|
etcdOptions := &EtcdOptions{
|
||||||
EncryptionProviderConfigFilepath: tc.encryptionConfigPath,
|
EncryptionProviderConfigFilepath: tc.encryptionConfigPath,
|
||||||
|
SkipHealthEndpoints: tc.skipHealth,
|
||||||
}
|
}
|
||||||
if err := etcdOptions.addEtcdHealthEndpoint(serverConfig); err != nil {
|
if err := etcdOptions.Complete(serverConfig.StorageObjectCountTracker, serverConfig.DrainedNotify()); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := etcdOptions.ApplyTo(serverConfig); err != nil {
|
||||||
t.Fatalf("Failed to add healthz error: %v", err)
|
t.Fatalf("Failed to add healthz error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -244,12 +255,19 @@ func TestReadinessCheck(t *testing.T) {
|
|||||||
name string
|
name string
|
||||||
wantReadyzChecks []string
|
wantReadyzChecks []string
|
||||||
wantHealthzChecks []string
|
wantHealthzChecks []string
|
||||||
|
skipHealth bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "Readyz should have etcd-readiness check",
|
name: "Readyz should have etcd-readiness check",
|
||||||
wantReadyzChecks: []string{"etcd", "etcd-readiness"},
|
wantReadyzChecks: []string{"etcd", "etcd-readiness"},
|
||||||
wantHealthzChecks: []string{"etcd"},
|
wantHealthzChecks: []string{"etcd"},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "skip health, Readyz should not have etcd-readiness check",
|
||||||
|
wantReadyzChecks: nil,
|
||||||
|
wantHealthzChecks: nil,
|
||||||
|
skipHealth: true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
scheme := runtime.NewScheme()
|
scheme := runtime.NewScheme()
|
||||||
@ -258,8 +276,11 @@ func TestReadinessCheck(t *testing.T) {
|
|||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
serverConfig := server.NewConfig(codecs)
|
serverConfig := server.NewConfig(codecs)
|
||||||
etcdOptions := &EtcdOptions{}
|
etcdOptions := &EtcdOptions{SkipHealthEndpoints: tc.skipHealth}
|
||||||
if err := etcdOptions.addEtcdHealthEndpoint(serverConfig); err != nil {
|
if err := etcdOptions.Complete(serverConfig.StorageObjectCountTracker, serverConfig.DrainedNotify()); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := etcdOptions.ApplyTo(serverConfig); err != nil {
|
||||||
t.Fatalf("Failed to add healthz error: %v", err)
|
t.Fatalf("Failed to add healthz error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,6 +101,9 @@ func (o *RecommendedOptions) AddFlags(fs *pflag.FlagSet) {
|
|||||||
// ApplyTo adds RecommendedOptions to the server configuration.
|
// ApplyTo adds RecommendedOptions to the server configuration.
|
||||||
// pluginInitializers can be empty, it is only need for additional initializers.
|
// pluginInitializers can be empty, it is only need for additional initializers.
|
||||||
func (o *RecommendedOptions) ApplyTo(config *server.RecommendedConfig) error {
|
func (o *RecommendedOptions) ApplyTo(config *server.RecommendedConfig) error {
|
||||||
|
if err := o.Etcd.Complete(config.Config.StorageObjectCountTracker, config.Config.DrainedNotify()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if err := o.Etcd.ApplyTo(&config.Config); err != nil {
|
if err := o.Etcd.ApplyTo(&config.Config); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,6 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/util/sets"
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
"k8s.io/apiserver/pkg/features"
|
"k8s.io/apiserver/pkg/features"
|
||||||
"k8s.io/apiserver/pkg/storage/storagebackend"
|
"k8s.io/apiserver/pkg/storage/storagebackend"
|
||||||
"k8s.io/apiserver/pkg/storage/value"
|
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -112,8 +111,6 @@ type groupResourceOverrides struct {
|
|||||||
// decoderDecoratorFn is optional and may wrap the provided decoders (can add new decoders). The order of
|
// decoderDecoratorFn is optional and may wrap the provided decoders (can add new decoders). The order of
|
||||||
// returned decoders will be priority for attempt to decode.
|
// returned decoders will be priority for attempt to decode.
|
||||||
decoderDecoratorFn func([]runtime.Decoder) []runtime.Decoder
|
decoderDecoratorFn func([]runtime.Decoder) []runtime.Decoder
|
||||||
// transformer is optional and shall encrypt that resource at rest.
|
|
||||||
transformer value.Transformer
|
|
||||||
// disablePaging will prevent paging on the provided resource.
|
// disablePaging will prevent paging on the provided resource.
|
||||||
disablePaging bool
|
disablePaging bool
|
||||||
}
|
}
|
||||||
@ -139,9 +136,6 @@ func (o groupResourceOverrides) Apply(config *storagebackend.Config, options *St
|
|||||||
if o.decoderDecoratorFn != nil {
|
if o.decoderDecoratorFn != nil {
|
||||||
options.DecoderDecoratorFn = o.decoderDecoratorFn
|
options.DecoderDecoratorFn = o.decoderDecoratorFn
|
||||||
}
|
}
|
||||||
if o.transformer != nil {
|
|
||||||
config.Transformer = o.transformer
|
|
||||||
}
|
|
||||||
if o.disablePaging {
|
if o.disablePaging {
|
||||||
config.Paging = false
|
config.Paging = false
|
||||||
}
|
}
|
||||||
@ -210,12 +204,6 @@ func (s *DefaultStorageFactory) SetSerializer(groupResource schema.GroupResource
|
|||||||
s.Overrides[groupResource] = overrides
|
s.Overrides[groupResource] = overrides
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DefaultStorageFactory) SetTransformer(groupResource schema.GroupResource, transformer value.Transformer) {
|
|
||||||
overrides := s.Overrides[groupResource]
|
|
||||||
overrides.transformer = transformer
|
|
||||||
s.Overrides[groupResource] = overrides
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddCohabitatingResources links resources together the order of the slice matters! its the priority order of lookup for finding a storage location
|
// AddCohabitatingResources links resources together the order of the slice matters! its the priority order of lookup for finding a storage location
|
||||||
func (s *DefaultStorageFactory) AddCohabitatingResources(groupResources ...schema.GroupResource) {
|
func (s *DefaultStorageFactory) AddCohabitatingResources(groupResources ...schema.GroupResource) {
|
||||||
for _, groupResource := range groupResources {
|
for _, groupResource := range groupResources {
|
||||||
@ -291,25 +279,35 @@ func (s *DefaultStorageFactory) NewConfig(groupResource schema.GroupResource) (*
|
|||||||
// Backends returns all backends for all registered storage destinations.
|
// Backends returns all backends for all registered storage destinations.
|
||||||
// Used for getting all instances for health validations.
|
// Used for getting all instances for health validations.
|
||||||
func (s *DefaultStorageFactory) Backends() []Backend {
|
func (s *DefaultStorageFactory) Backends() []Backend {
|
||||||
servers := sets.NewString(s.StorageConfig.Transport.ServerList...)
|
return backends(s.StorageConfig, s.Overrides)
|
||||||
|
}
|
||||||
|
|
||||||
for _, overrides := range s.Overrides {
|
// Backends returns all backends for all registered storage destinations.
|
||||||
|
// Used for getting all instances for health validations.
|
||||||
|
func Backends(storageConfig storagebackend.Config) []Backend {
|
||||||
|
return backends(storageConfig, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func backends(storageConfig storagebackend.Config, grOverrides map[schema.GroupResource]groupResourceOverrides) []Backend {
|
||||||
|
servers := sets.NewString(storageConfig.Transport.ServerList...)
|
||||||
|
|
||||||
|
for _, overrides := range grOverrides {
|
||||||
servers.Insert(overrides.etcdLocation...)
|
servers.Insert(overrides.etcdLocation...)
|
||||||
}
|
}
|
||||||
|
|
||||||
tlsConfig := &tls.Config{
|
tlsConfig := &tls.Config{
|
||||||
InsecureSkipVerify: true,
|
InsecureSkipVerify: true,
|
||||||
}
|
}
|
||||||
if len(s.StorageConfig.Transport.CertFile) > 0 && len(s.StorageConfig.Transport.KeyFile) > 0 {
|
if len(storageConfig.Transport.CertFile) > 0 && len(storageConfig.Transport.KeyFile) > 0 {
|
||||||
cert, err := tls.LoadX509KeyPair(s.StorageConfig.Transport.CertFile, s.StorageConfig.Transport.KeyFile)
|
cert, err := tls.LoadX509KeyPair(storageConfig.Transport.CertFile, storageConfig.Transport.KeyFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
klog.Errorf("failed to load key pair while getting backends: %s", err)
|
klog.Errorf("failed to load key pair while getting backends: %s", err)
|
||||||
} else {
|
} else {
|
||||||
tlsConfig.Certificates = []tls.Certificate{cert}
|
tlsConfig.Certificates = []tls.Certificate{cert}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(s.StorageConfig.Transport.TrustedCAFile) > 0 {
|
if len(storageConfig.Transport.TrustedCAFile) > 0 {
|
||||||
if caCert, err := ioutil.ReadFile(s.StorageConfig.Transport.TrustedCAFile); err != nil {
|
if caCert, err := ioutil.ReadFile(storageConfig.Transport.TrustedCAFile); err != nil {
|
||||||
klog.Errorf("failed to read ca file while getting backends: %s", err)
|
klog.Errorf("failed to read ca file while getting backends: %s", err)
|
||||||
} else {
|
} else {
|
||||||
caPool := x509.NewCertPool()
|
caPool := x509.NewCertPool()
|
||||||
|
@ -48,7 +48,7 @@ import (
|
|||||||
"k8s.io/apiserver/pkg/storage/etcd3"
|
"k8s.io/apiserver/pkg/storage/etcd3"
|
||||||
"k8s.io/apiserver/pkg/storage/etcd3/metrics"
|
"k8s.io/apiserver/pkg/storage/etcd3/metrics"
|
||||||
"k8s.io/apiserver/pkg/storage/storagebackend"
|
"k8s.io/apiserver/pkg/storage/storagebackend"
|
||||||
"k8s.io/apiserver/pkg/storage/value"
|
"k8s.io/apiserver/pkg/storage/value/encrypt/identity"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
"k8s.io/component-base/metrics/legacyregistry"
|
"k8s.io/component-base/metrics/legacyregistry"
|
||||||
tracing "k8s.io/component-base/tracing"
|
tracing "k8s.io/component-base/tracing"
|
||||||
@ -395,7 +395,7 @@ func newETCD3Storage(c storagebackend.ConfigForResource, newFunc func() runtime.
|
|||||||
}
|
}
|
||||||
transformer := c.Transformer
|
transformer := c.Transformer
|
||||||
if transformer == nil {
|
if transformer == nil {
|
||||||
transformer = value.IdentityTransformer
|
transformer = identity.NewEncryptCheckTransformer()
|
||||||
}
|
}
|
||||||
return etcd3.New(client, c.Codec, newFunc, c.Prefix, c.GroupResource, transformer, c.Paging, c.LeaseManagerConfig), destroyFunc, nil
|
return etcd3.New(client, c.Codec, newFunc, c.Prefix, c.GroupResource, transformer, c.Paging, c.LeaseManagerConfig), destroyFunc, nil
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ import (
|
|||||||
"k8s.io/apiserver/pkg/storage/etcd3"
|
"k8s.io/apiserver/pkg/storage/etcd3"
|
||||||
etcd3testing "k8s.io/apiserver/pkg/storage/etcd3/testing"
|
etcd3testing "k8s.io/apiserver/pkg/storage/etcd3/testing"
|
||||||
storagetesting "k8s.io/apiserver/pkg/storage/testing"
|
storagetesting "k8s.io/apiserver/pkg/storage/testing"
|
||||||
"k8s.io/apiserver/pkg/storage/value"
|
"k8s.io/apiserver/pkg/storage/value/encrypt/identity"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
||||||
"k8s.io/utils/clock"
|
"k8s.io/utils/clock"
|
||||||
@ -107,7 +107,7 @@ func newPodList() runtime.Object { return &example.PodList{} }
|
|||||||
|
|
||||||
func newEtcdTestStorage(t *testing.T, prefix string) (*etcd3testing.EtcdTestServer, storage.Interface) {
|
func newEtcdTestStorage(t *testing.T, prefix string) (*etcd3testing.EtcdTestServer, storage.Interface) {
|
||||||
server, _ := etcd3testing.NewUnsecuredEtcd3TestClientServer(t)
|
server, _ := etcd3testing.NewUnsecuredEtcd3TestClientServer(t)
|
||||||
storage := etcd3.New(server.V3Client, apitesting.TestCodec(codecs, examplev1.SchemeGroupVersion), newPod, prefix, schema.GroupResource{Resource: "pods"}, value.IdentityTransformer, true, etcd3.NewDefaultLeaseManagerConfig())
|
storage := etcd3.New(server.V3Client, apitesting.TestCodec(codecs, examplev1.SchemeGroupVersion), newPod, prefix, schema.GroupResource{Resource: "pods"}, identity.NewEncryptCheckTransformer(), true, etcd3.NewDefaultLeaseManagerConfig())
|
||||||
return server, storage
|
return server, storage
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,6 +24,12 @@ import (
|
|||||||
"k8s.io/apiserver/pkg/storage/value"
|
"k8s.io/apiserver/pkg/storage/value"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
transformer = identityTransformer{}
|
||||||
|
encryptedPrefix = []byte("k8s:enc:")
|
||||||
|
errEncryptedData = fmt.Errorf("identity transformer tried to read encrypted data")
|
||||||
|
)
|
||||||
|
|
||||||
// identityTransformer performs no transformation on provided data, but validates
|
// identityTransformer performs no transformation on provided data, but validates
|
||||||
// that the data is not encrypted data during TransformFromStorage
|
// that the data is not encrypted data during TransformFromStorage
|
||||||
type identityTransformer struct{}
|
type identityTransformer struct{}
|
||||||
@ -31,7 +37,7 @@ type identityTransformer struct{}
|
|||||||
// NewEncryptCheckTransformer returns an identityTransformer which returns an error
|
// NewEncryptCheckTransformer returns an identityTransformer which returns an error
|
||||||
// on attempts to read encrypted data
|
// on attempts to read encrypted data
|
||||||
func NewEncryptCheckTransformer() value.Transformer {
|
func NewEncryptCheckTransformer() value.Transformer {
|
||||||
return identityTransformer{}
|
return transformer
|
||||||
}
|
}
|
||||||
|
|
||||||
// TransformFromStorage returns the input bytes if the data is not encrypted
|
// TransformFromStorage returns the input bytes if the data is not encrypted
|
||||||
@ -39,8 +45,8 @@ func (identityTransformer) TransformFromStorage(ctx context.Context, data []byte
|
|||||||
// identityTransformer has to return an error if the data is encoded using another transformer.
|
// identityTransformer has to return an error if the data is encoded using another transformer.
|
||||||
// JSON data starts with '{'. Protobuf data has a prefix 'k8s[\x00-\xFF]'.
|
// JSON data starts with '{'. Protobuf data has a prefix 'k8s[\x00-\xFF]'.
|
||||||
// Prefix 'k8s:enc:' is reserved for encrypted data on disk.
|
// Prefix 'k8s:enc:' is reserved for encrypted data on disk.
|
||||||
if bytes.HasPrefix(data, []byte("k8s:enc:")) {
|
if bytes.HasPrefix(data, encryptedPrefix) {
|
||||||
return []byte{}, false, fmt.Errorf("identity transformer tried to read encrypted data")
|
return nil, false, errEncryptedData
|
||||||
}
|
}
|
||||||
return data, false, nil
|
return data, false, nil
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,6 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/util/errors"
|
"k8s.io/apimachinery/pkg/util/errors"
|
||||||
@ -51,54 +50,11 @@ type Transformer interface {
|
|||||||
TransformToStorage(ctx context.Context, data []byte, dataCtx Context) (out []byte, err error)
|
TransformToStorage(ctx context.Context, data []byte, dataCtx Context) (out []byte, err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type identityTransformer struct{}
|
|
||||||
|
|
||||||
// IdentityTransformer performs no transformation of the provided data.
|
|
||||||
var IdentityTransformer Transformer = identityTransformer{}
|
|
||||||
|
|
||||||
func (identityTransformer) TransformFromStorage(ctx context.Context, data []byte, dataCtx Context) ([]byte, bool, error) {
|
|
||||||
return data, false, nil
|
|
||||||
}
|
|
||||||
func (identityTransformer) TransformToStorage(ctx context.Context, data []byte, dataCtx Context) ([]byte, error) {
|
|
||||||
return data, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DefaultContext is a simple implementation of Context for a slice of bytes.
|
// DefaultContext is a simple implementation of Context for a slice of bytes.
|
||||||
type DefaultContext []byte
|
type DefaultContext []byte
|
||||||
|
|
||||||
// AuthenticatedData returns itself.
|
// AuthenticatedData returns itself.
|
||||||
func (c DefaultContext) AuthenticatedData() []byte { return []byte(c) }
|
func (c DefaultContext) AuthenticatedData() []byte { return c }
|
||||||
|
|
||||||
// MutableTransformer allows a transformer to be changed safely at runtime.
|
|
||||||
type MutableTransformer struct {
|
|
||||||
lock sync.RWMutex
|
|
||||||
transformer Transformer
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMutableTransformer creates a transformer that can be updated at any time by calling Set()
|
|
||||||
func NewMutableTransformer(transformer Transformer) *MutableTransformer {
|
|
||||||
return &MutableTransformer{transformer: transformer}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set updates the nested transformer.
|
|
||||||
func (t *MutableTransformer) Set(transformer Transformer) {
|
|
||||||
t.lock.Lock()
|
|
||||||
t.transformer = transformer
|
|
||||||
t.lock.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *MutableTransformer) TransformFromStorage(ctx context.Context, data []byte, dataCtx Context) (out []byte, stale bool, err error) {
|
|
||||||
t.lock.RLock()
|
|
||||||
transformer := t.transformer
|
|
||||||
t.lock.RUnlock()
|
|
||||||
return transformer.TransformFromStorage(ctx, data, dataCtx)
|
|
||||||
}
|
|
||||||
func (t *MutableTransformer) TransformToStorage(ctx context.Context, data []byte, dataCtx Context) (out []byte, err error) {
|
|
||||||
t.lock.RLock()
|
|
||||||
transformer := t.transformer
|
|
||||||
t.lock.RUnlock()
|
|
||||||
return transformer.TransformToStorage(ctx, data, dataCtx)
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrefixTransformer holds a transformer interface and the prefix that the transformation is located under.
|
// PrefixTransformer holds a transformer interface and the prefix that the transformation is located under.
|
||||||
type PrefixTransformer struct {
|
type PrefixTransformer struct {
|
||||||
|
@ -29,17 +29,25 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gogo/protobuf/proto"
|
"github.com/gogo/protobuf/proto"
|
||||||
|
|
||||||
|
apiextensionsclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
||||||
|
"k8s.io/apimachinery/pkg/api/meta"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"k8s.io/apimachinery/pkg/util/uuid"
|
"k8s.io/apimachinery/pkg/util/uuid"
|
||||||
"k8s.io/apiserver/pkg/features"
|
"k8s.io/apiserver/pkg/features"
|
||||||
|
"k8s.io/apiserver/pkg/server/options/encryptionconfig"
|
||||||
"k8s.io/apiserver/pkg/storage/value"
|
"k8s.io/apiserver/pkg/storage/value"
|
||||||
aestransformer "k8s.io/apiserver/pkg/storage/value/encrypt/aes"
|
aestransformer "k8s.io/apiserver/pkg/storage/value/encrypt/aes"
|
||||||
|
"k8s.io/apiserver/pkg/storage/value/encrypt/envelope/kmsv2"
|
||||||
kmstypes "k8s.io/apiserver/pkg/storage/value/encrypt/envelope/kmsv2/v2alpha1"
|
kmstypes "k8s.io/apiserver/pkg/storage/value/encrypt/envelope/kmsv2/v2alpha1"
|
||||||
kmsv2mock "k8s.io/apiserver/pkg/storage/value/encrypt/envelope/testing/v2alpha1"
|
kmsv2mock "k8s.io/apiserver/pkg/storage/value/encrypt/envelope/testing/v2alpha1"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
|
"k8s.io/client-go/dynamic"
|
||||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
||||||
kmsv2api "k8s.io/kms/apis/v2alpha1"
|
kmsv2api "k8s.io/kms/apis/v2alpha1"
|
||||||
|
"k8s.io/kubernetes/test/integration/etcd"
|
||||||
)
|
)
|
||||||
|
|
||||||
type envelopekmsv2 struct {
|
type envelopekmsv2 struct {
|
||||||
@ -273,3 +281,81 @@ resources:
|
|||||||
mustBeHealthy(t, "kms-provider-0", test.kubeAPIServer.ClientConfig)
|
mustBeHealthy(t, "kms-provider-0", test.kubeAPIServer.ClientConfig)
|
||||||
mustBeUnHealthy(t, "kms-provider-1", test.kubeAPIServer.ClientConfig)
|
mustBeUnHealthy(t, "kms-provider-1", test.kubeAPIServer.ClientConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestKMSv2SingleService(t *testing.T) {
|
||||||
|
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.KMSv2, true)()
|
||||||
|
|
||||||
|
var kmsv2Calls int
|
||||||
|
origEnvelopeKMSv2ServiceFactory := encryptionconfig.EnvelopeKMSv2ServiceFactory
|
||||||
|
encryptionconfig.EnvelopeKMSv2ServiceFactory = func(ctx context.Context, endpoint string, callTimeout time.Duration) (kmsv2.Service, error) {
|
||||||
|
kmsv2Calls++
|
||||||
|
return origEnvelopeKMSv2ServiceFactory(ctx, endpoint, callTimeout)
|
||||||
|
}
|
||||||
|
t.Cleanup(func() {
|
||||||
|
encryptionconfig.EnvelopeKMSv2ServiceFactory = origEnvelopeKMSv2ServiceFactory
|
||||||
|
})
|
||||||
|
|
||||||
|
// check resources provided by the three servers that we have wired together
|
||||||
|
// - pods and config maps from KAS
|
||||||
|
// - CRDs and CRs from API extensions
|
||||||
|
// - API services from aggregator
|
||||||
|
encryptionConfig := `
|
||||||
|
kind: EncryptionConfiguration
|
||||||
|
apiVersion: apiserver.config.k8s.io/v1
|
||||||
|
resources:
|
||||||
|
- resources:
|
||||||
|
- pods
|
||||||
|
- configmaps
|
||||||
|
- customresourcedefinitions.apiextensions.k8s.io
|
||||||
|
- pandas.awesome.bears.com
|
||||||
|
- apiservices.apiregistration.k8s.io
|
||||||
|
providers:
|
||||||
|
- kms:
|
||||||
|
apiVersion: v2
|
||||||
|
name: kms-provider
|
||||||
|
cachesize: 1000
|
||||||
|
endpoint: unix:///@kms-provider.sock
|
||||||
|
`
|
||||||
|
|
||||||
|
pluginMock, err := kmsv2mock.NewBase64Plugin("@kms-provider.sock")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create mock of KMSv2 Plugin: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
go pluginMock.Start()
|
||||||
|
if err := kmsv2mock.WaitForBase64PluginToBeUp(pluginMock); err != nil {
|
||||||
|
t.Fatalf("Failed start plugin, err: %v", err)
|
||||||
|
}
|
||||||
|
t.Cleanup(pluginMock.CleanUp)
|
||||||
|
|
||||||
|
test, err := newTransformTest(t, encryptionConfig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to start KUBE API Server with encryptionConfig\n %s, error: %v", encryptionConfig, err)
|
||||||
|
}
|
||||||
|
t.Cleanup(test.cleanUp)
|
||||||
|
|
||||||
|
// the storage registry for CRs is dynamic so create one to exercise the wiring
|
||||||
|
etcd.CreateTestCRDs(t, apiextensionsclientset.NewForConfigOrDie(test.kubeAPIServer.ClientConfig), false, etcd.GetCustomResourceDefinitionData()...)
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||||
|
t.Cleanup(cancel)
|
||||||
|
|
||||||
|
gvr := schema.GroupVersionResource{Group: "awesome.bears.com", Version: "v1", Resource: "pandas"}
|
||||||
|
stub := etcd.GetEtcdStorageData()[gvr].Stub
|
||||||
|
dynamicClient, obj, err := etcd.JSONToUnstructured(stub, "", &meta.RESTMapping{
|
||||||
|
Resource: gvr,
|
||||||
|
GroupVersionKind: gvr.GroupVersion().WithKind("Panda"),
|
||||||
|
Scope: meta.RESTScopeRoot,
|
||||||
|
}, dynamic.NewForConfigOrDie(test.kubeAPIServer.ClientConfig))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
_, err = dynamicClient.Create(ctx, obj, metav1.CreateOptions{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if kmsv2Calls != 1 {
|
||||||
|
t.Fatalf("expected a single call to KMS v2 service factory: %v", kmsv2Calls)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user