Commit e633a160 authored by Kubernetes Submit Queue's avatar Kubernetes Submit Queue Committed by GitHub

Merge pull request #50789 from mtaufen/fix-scheme

Automatic merge from submit-queue Don't register the kubeletconfig group with the default Scheme See https://github.com/kubernetes/kubernetes/pull/49051#discussion_r132527078
parents b59ad9cb 0af9f756
......@@ -12,11 +12,10 @@ go_library(
"options.go",
],
deps = [
"//pkg/api:go_default_library",
"//pkg/apis/componentconfig:go_default_library",
"//pkg/features:go_default_library",
"//pkg/kubelet/apis/kubeletconfig:go_default_library",
"//pkg/kubelet/apis/kubeletconfig/install:go_default_library",
"//pkg/kubelet/apis/kubeletconfig/scheme:go_default_library",
"//pkg/kubelet/apis/kubeletconfig/v1alpha1:go_default_library",
"//pkg/kubelet/apis/kubeletconfig/validation:go_default_library",
"//pkg/util/taints:go_default_library",
......
......@@ -25,14 +25,12 @@ import (
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/apiserver/pkg/util/flag"
utilflag "k8s.io/apiserver/pkg/util/flag"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/componentconfig"
"k8s.io/kubernetes/pkg/features"
"k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig"
kubeletconfigvalidation "k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig/validation"
// Need to make sure the kubeletconfig api is installed so defaulting funcs work
_ "k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig/install"
kubeletscheme "k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig/scheme"
"k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig/v1alpha1"
kubeletconfigvalidation "k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig/validation"
utiltaints "k8s.io/kubernetes/pkg/util/taints"
"github.com/spf13/pflag"
......@@ -142,10 +140,14 @@ func ValidateKubeletFlags(f *KubeletFlags) error {
// NewKubeletConfiguration will create a new KubeletConfiguration with default values
func NewKubeletConfiguration() (*kubeletconfig.KubeletConfiguration, error) {
scheme, _, err := kubeletscheme.NewSchemeAndCodecs()
if err != nil {
return nil, err
}
versioned := &v1alpha1.KubeletConfiguration{}
api.Scheme.Default(versioned)
scheme.Default(versioned)
config := &kubeletconfig.KubeletConfiguration{}
if err := api.Scheme.Convert(versioned, config, nil); err != nil {
if err := scheme.Convert(versioned, config, nil); err != nil {
return nil, err
}
return config, nil
......
......@@ -756,7 +756,8 @@ func parseResourceList(m kubeletconfiginternal.ConfigurationMap) (v1.ResourceLis
}
// BootstrapKubeletConfigController constructs and bootstrap a configuration controller
func BootstrapKubeletConfigController(flags *options.KubeletFlags,
func BootstrapKubeletConfigController(
flags *options.KubeletFlags,
defaultConfig *kubeletconfiginternal.KubeletConfiguration) (*kubeletconfiginternal.KubeletConfiguration, *kubeletconfig.Controller, error) {
var err error
// Alpha Dynamic Configuration Implementation; this section only loads config from disk, it does not contact the API server
......@@ -777,7 +778,10 @@ func BootstrapKubeletConfigController(flags *options.KubeletFlags,
}
// get the latest KubeletConfiguration checkpoint from disk, or load the init or default config if no valid checkpoints exist
kubeletConfigController := kubeletconfig.NewController(initConfigDir, dynamicConfigDir, defaultConfig)
kubeletConfigController, err := kubeletconfig.NewController(initConfigDir, dynamicConfigDir, defaultConfig)
if err != nil {
return nil, nil, fmt.Errorf("failed to construct controller, error: %v", err)
}
kubeletConfig, err := kubeletConfigController.Bootstrap()
if err != nil {
return nil, nil, fmt.Errorf("failed to determine a valid configuration, error: %v", err)
......
......@@ -89,7 +89,10 @@ func main() {
}
// construct a KubeletServer from kubeletFlags and kubeletConfig
kubeletServer := &options.KubeletServer{KubeletFlags: *kubeletFlags, KubeletConfiguration: *kubeletConfig}
kubeletServer := &options.KubeletServer{
KubeletFlags: *kubeletFlags,
KubeletConfiguration: *kubeletConfig,
}
// use kubeletServer to construct the default KubeletDeps
kubeletDeps, err := app.UnsecuredDependencies(kubeletServer)
......
......@@ -36,7 +36,7 @@ filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//pkg/kubelet/apis/kubeletconfig/install:all-srcs",
"//pkg/kubelet/apis/kubeletconfig/scheme:all-srcs",
"//pkg/kubelet/apis/kubeletconfig/v1alpha1:all-srcs",
"//pkg/kubelet/apis/kubeletconfig/validation:all-srcs",
],
......
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["install.go"],
tags = ["automanaged"],
srcs = ["scheme.go"],
visibility = ["//visibility:public"],
deps = [
"//pkg/api:go_default_library",
"//pkg/kubelet/apis/kubeletconfig:go_default_library",
"//pkg/kubelet/apis/kubeletconfig/v1alpha1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apimachinery/announced:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apimachinery/registered:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
],
)
......@@ -32,4 +23,5 @@ filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
......@@ -14,36 +14,27 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
// Package install installs the experimental API group, making it available as
// an option to all of the API encoding/decoding machinery.
package install
package scheme
import (
"k8s.io/apimachinery/pkg/apimachinery/announced"
"k8s.io/apimachinery/pkg/apimachinery/registered"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/kubernetes/pkg/api"
"k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig"
"k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig/v1alpha1"
)
func init() {
// TODO(mtaufen): probably want to create a kubelet scheme rather than reusing the api scheme, but need to ask lavalamp
Install(api.GroupFactoryRegistry, api.Registry, api.Scheme)
}
// Utility functions for the Kubelet's kubeletconfig API group
// Install registers the API group and adds types to a scheme
func Install(groupFactoryRegistry announced.APIGroupFactoryRegistry, registry *registered.APIRegistrationManager, scheme *runtime.Scheme) {
if err := announced.NewGroupMetaFactory(
&announced.GroupMetaFactoryArgs{
GroupName: kubeletconfig.GroupName,
VersionPreferenceOrder: []string{v1alpha1.SchemeGroupVersion.Version},
AddInternalObjectsToScheme: kubeletconfig.AddToScheme,
},
announced.VersionToSchemeFunc{
v1alpha1.SchemeGroupVersion.Version: v1alpha1.AddToScheme,
},
).Announce(groupFactoryRegistry).RegisterAndEnable(registry, scheme); err != nil {
panic(err)
// NewSchemeAndCodecs is a utility funciton that returns a Scheme and CodecFactory
// that understand the types in the kubeletconfig API group.
func NewSchemeAndCodecs() (*runtime.Scheme, *serializer.CodecFactory, error) {
scheme := runtime.NewScheme()
if err := kubeletconfig.AddToScheme(scheme); err != nil {
return nil, nil, err
}
if err := v1alpha1.AddToScheme(scheme); err != nil {
return nil, nil, err
}
codecs := serializer.NewCodecFactory(scheme)
return scheme, &codecs, nil
}
......@@ -15,8 +15,8 @@ go_test(
],
library = ":go_default_library",
deps = [
"//pkg/api:go_default_library",
"//pkg/kubelet/apis/kubeletconfig:go_default_library",
"//pkg/kubelet/apis/kubeletconfig/scheme:go_default_library",
"//pkg/kubelet/apis/kubeletconfig/v1alpha1:go_default_library",
"//pkg/kubelet/kubeletconfig/util/codec:go_default_library",
"//pkg/kubelet/kubeletconfig/util/test:go_default_library",
......@@ -40,12 +40,14 @@ go_library(
deps = [
"//pkg/api:go_default_library",
"//pkg/kubelet/apis/kubeletconfig:go_default_library",
"//pkg/kubelet/apis/kubeletconfig/scheme:go_default_library",
"//pkg/kubelet/kubeletconfig/util/codec:go_default_library",
"//pkg/kubelet/kubeletconfig/util/log:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
],
)
......
......@@ -30,7 +30,7 @@ import (
type Checkpoint interface {
// UID returns the UID of the config source object behind the Checkpoint
UID() string
// Parse parses the checkpoint into the internal KubeletConfiguration type
// Parse extracts the KubeletConfiguration from the checkpoint, applies defaults, and converts to the internal type
Parse() (*kubeletconfig.KubeletConfiguration, error)
// Encode returns a []byte representation of the config source object behind the Checkpoint
Encode() ([]byte, error)
......@@ -56,6 +56,7 @@ func DecodeCheckpoint(data []byte) (Checkpoint, error) {
if err != nil {
return nil, fmt.Errorf("failed to convert decoded object into a v1 ConfigMap, error: %v", err)
}
return NewConfigMapCheckpoint(cm)
}
......
......@@ -30,20 +30,6 @@ import (
utiltest "k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/test"
)
// newUnsupportedEncoded returns an encoding of an object that does not have a Checkpoint implementation
func newUnsupportedEncoded(t *testing.T) []byte {
encoder, err := utilcodec.NewJSONEncoder(apiv1.GroupName)
if err != nil {
t.Fatalf("could not create an encoder, error: %v", err)
}
unsupported := &apiv1.Node{}
data, err := runtime.Encode(encoder, unsupported)
if err != nil {
t.Fatalf("could not encode object, error: %v", err)
}
return data
}
func TestDecodeCheckpoint(t *testing.T) {
// generate correct Checkpoint for v1/ConfigMap test case
cm, err := NewConfigMapCheckpoint(&apiv1.ConfigMap{ObjectMeta: metav1.ObjectMeta{UID: types.UID("uid")}})
......@@ -87,3 +73,17 @@ func TestDecodeCheckpoint(t *testing.T) {
}
}
}
// newUnsupportedEncoded returns an encoding of an object that does not have a Checkpoint implementation
func newUnsupportedEncoded(t *testing.T) []byte {
encoder, err := utilcodec.NewJSONEncoder(apiv1.GroupName)
if err != nil {
t.Fatalf("could not create an encoder, error: %v", err)
}
unsupported := &apiv1.Node{}
data, err := runtime.Encode(encoder, unsupported)
if err != nil {
t.Fatalf("could not encode object, error: %v", err)
}
return data
}
......@@ -21,7 +21,9 @@ import (
apiv1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig"
kubeletscheme "k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig/scheme"
utilcodec "k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/codec"
)
......@@ -29,7 +31,8 @@ const configMapConfigKey = "kubelet"
// configMapCheckpoint implements Checkpoint, backed by a v1/ConfigMap config source object
type configMapCheckpoint struct {
configMap *apiv1.ConfigMap
kubeletCodecs *serializer.CodecFactory // codecs for the KubeletConfiguration
configMap *apiv1.ConfigMap
}
// NewConfigMapCheckpoint returns a Checkpoint backed by `cm`. `cm` must be non-nil
......@@ -40,7 +43,13 @@ func NewConfigMapCheckpoint(cm *apiv1.ConfigMap) (Checkpoint, error) {
} else if len(cm.ObjectMeta.UID) == 0 {
return nil, fmt.Errorf("ConfigMap must have a UID to be treated as a Checkpoint")
}
return &configMapCheckpoint{cm}, nil
_, kubeletCodecs, err := kubeletscheme.NewSchemeAndCodecs()
if err != nil {
return nil, err
}
return &configMapCheckpoint{kubeletCodecs, cm}, nil
}
// UID returns the UID of a configMapCheckpoint
......@@ -48,25 +57,23 @@ func (c *configMapCheckpoint) UID() string {
return string(c.configMap.UID)
}
// implements Parse for v1/ConfigMap checkpoints
// Parse extracts the KubeletConfiguration from v1/ConfigMap checkpoints, applies defaults, and converts to the internal type
func (c *configMapCheckpoint) Parse() (*kubeletconfig.KubeletConfiguration, error) {
const emptyCfgErr = "config was empty, but some parameters are required"
cm := c.configMap
if len(cm.Data) == 0 {
if len(c.configMap.Data) == 0 {
return nil, fmt.Errorf(emptyCfgErr)
}
// TODO(mtaufen): Once the KubeletConfiguration type is decomposed, extend this to a key for each sub-object
config, ok := cm.Data[configMapConfigKey]
config, ok := c.configMap.Data[configMapConfigKey]
if !ok {
return nil, fmt.Errorf("key %q not found in ConfigMap", configMapConfigKey)
} else if len(config) == 0 {
return nil, fmt.Errorf(emptyCfgErr)
}
return utilcodec.DecodeKubeletConfiguration([]byte(config))
return utilcodec.DecodeKubeletConfiguration(c.kubeletCodecs, []byte(config))
}
// Encode encodes a configMapCheckpoint
......
......@@ -26,8 +26,8 @@ import (
apiequality "k8s.io/apimachinery/pkg/api/equality"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig"
kubeletscheme "k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig/scheme"
kubeletconfigv1alpha1 "k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig/v1alpha1"
utiltest "k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/test"
)
......@@ -66,9 +66,15 @@ func TestNewConfigMapCheckpoint(t *testing.T) {
}
func TestConfigMapCheckpointUID(t *testing.T) {
_, kubeletCodecs, err := kubeletscheme.NewSchemeAndCodecs()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
cases := []string{"", "uid", "376dfb73-56db-11e7-a01e-42010a800002"}
for _, uidIn := range cases {
cpt := &configMapCheckpoint{
kubeletCodecs,
&apiv1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{UID: types.UID(uidIn)},
},
......@@ -82,11 +88,16 @@ func TestConfigMapCheckpointUID(t *testing.T) {
}
func TestConfigMapCheckpointParse(t *testing.T) {
kubeletScheme, kubeletCodecs, err := kubeletscheme.NewSchemeAndCodecs()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
// get the built-in default configuration
external := &kubeletconfigv1alpha1.KubeletConfiguration{}
api.Scheme.Default(external)
kubeletScheme.Default(external)
defaultConfig := &kubeletconfig.KubeletConfiguration{}
err := api.Scheme.Convert(external, defaultConfig, nil)
err = kubeletScheme.Convert(external, defaultConfig, nil)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
......@@ -123,7 +134,7 @@ apiVersion: kubeletconfig/v1alpha1`}}, defaultConfig, ""},
"kubelet": `{"kind":"KubeletConfiguration","apiVersion":"kubeletconfig/v1alpha1"}`}}, defaultConfig, ""},
}
for _, c := range cases {
cpt := &configMapCheckpoint{c.cm}
cpt := &configMapCheckpoint{kubeletCodecs, c.cm}
kc, err := cpt.Parse()
if utiltest.SkipRest(t, c.desc, err, c.err) {
continue
......@@ -136,6 +147,11 @@ apiVersion: kubeletconfig/v1alpha1`}}, defaultConfig, ""},
}
func TestConfigMapCheckpointEncode(t *testing.T) {
_, kubeletCodecs, err := kubeletscheme.NewSchemeAndCodecs()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
// only one case, based on output from the existing encoder, and since
// this is hard to test (key order isn't guaranteed), we should probably
// just stick to this test case and mostly rely on the round-trip test.
......@@ -146,7 +162,7 @@ func TestConfigMapCheckpointEncode(t *testing.T) {
}{
// we expect Checkpoints to be encoded as a json representation of the underlying API object
{"one-key",
&configMapCheckpoint{&apiv1.ConfigMap{
&configMapCheckpoint{kubeletCodecs, &apiv1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{Name: "one-key"},
Data: map[string]string{"one": ""}}},
`{"kind":"ConfigMap","apiVersion":"v1","metadata":{"name":"one-key","creationTimestamp":null},"data":{"one":""}}
......@@ -166,6 +182,11 @@ func TestConfigMapCheckpointEncode(t *testing.T) {
}
func TestConfigMapCheckpointRoundTrip(t *testing.T) {
_, kubeletCodecs, err := kubeletscheme.NewSchemeAndCodecs()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
cases := []struct {
desc string
cpt *configMapCheckpoint
......@@ -173,7 +194,7 @@ func TestConfigMapCheckpointRoundTrip(t *testing.T) {
}{
// empty data
{"empty data",
&configMapCheckpoint{&apiv1.ConfigMap{
&configMapCheckpoint{kubeletCodecs, &apiv1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "empty-data-sha256-e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
UID: "uid",
......@@ -182,7 +203,7 @@ func TestConfigMapCheckpointRoundTrip(t *testing.T) {
""},
// two keys
{"two keys",
&configMapCheckpoint{&apiv1.ConfigMap{
&configMapCheckpoint{kubeletCodecs, &apiv1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "two-keys-sha256-2bff03d6249c8a9dc9a1436d087c124741361ccfac6615b81b67afcff5c42431",
UID: "uid",
......@@ -191,7 +212,7 @@ func TestConfigMapCheckpointRoundTrip(t *testing.T) {
""},
// missing uid
{"missing uid",
&configMapCheckpoint{&apiv1.ConfigMap{
&configMapCheckpoint{kubeletCodecs, &apiv1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "two-keys-sha256-2bff03d6249c8a9dc9a1436d087c124741361ccfac6615b81b67afcff5c42431",
UID: "",
......
......@@ -113,7 +113,6 @@ func (r *remoteConfigMap) UID() string {
func (r *remoteConfigMap) Download(client clientset.Interface) (Checkpoint, string, error) {
var reason string
uid := string(r.source.ConfigMapRef.UID)
utillog.Infof("attempting to download ConfigMap with UID %q", uid)
......@@ -131,8 +130,14 @@ func (r *remoteConfigMap) Download(client clientset.Interface) (Checkpoint, stri
return nil, reason, fmt.Errorf(reason)
}
checkpoint, err := NewConfigMapCheckpoint(cm)
if err != nil {
reason = fmt.Sprintf("invalid downloaded object")
return nil, reason, fmt.Errorf("%s, error: %v", reason, err)
}
utillog.Infof("successfully downloaded ConfigMap with UID %q", uid)
return &configMapCheckpoint{cm}, "", nil
return checkpoint, "", nil
}
func (r *remoteConfigMap) Encode() ([]byte, error) {
......
......@@ -26,6 +26,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
fakeclient "k8s.io/client-go/kubernetes/fake"
kubeletscheme "k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig/scheme"
utiltest "k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/test"
)
......@@ -92,6 +93,11 @@ func TestRemoteConfigMapUID(t *testing.T) {
}
func TestRemoteConfigMapDownload(t *testing.T) {
_, kubeletCodecs, err := kubeletscheme.NewSchemeAndCodecs()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
cm := &apiv1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "name",
......@@ -118,7 +124,7 @@ func TestRemoteConfigMapDownload(t *testing.T) {
// successful download
{"object exists and reference is correct",
&remoteConfigMap{&apiv1.NodeConfigSource{ConfigMapRef: &apiv1.ObjectReference{Name: "name", Namespace: "namespace", UID: "uid"}}},
&configMapCheckpoint{cm}, ""},
&configMapCheckpoint{kubeletCodecs, cm}, ""},
}
for _, c := range cases {
......
......@@ -10,8 +10,10 @@ go_library(
srcs = ["configfiles.go"],
deps = [
"//pkg/kubelet/apis/kubeletconfig:go_default_library",
"//pkg/kubelet/apis/kubeletconfig/scheme:go_default_library",
"//pkg/kubelet/kubeletconfig/util/codec:go_default_library",
"//pkg/kubelet/kubeletconfig/util/filesystem:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
],
)
......
......@@ -20,7 +20,9 @@ import (
"fmt"
"path/filepath"
"k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig"
kubeletscheme "k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig/scheme"
utilcodec "k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/codec"
utilfs "k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/filesystem"
)
......@@ -35,16 +37,24 @@ type Loader interface {
type fsLoader struct {
// fs is the filesystem where the config files exist; can be mocked for testing
fs utilfs.Filesystem
// kubeletCodecs is the scheme used to decode config files
kubeletCodecs *serializer.CodecFactory
// configDir is the absolute path to the directory containing the configuration files
configDir string
}
// NewFSLoader returns a Loader that loads a KubeletConfiguration from the files in `configDir`
func NewFSLoader(fs utilfs.Filesystem, configDir string) Loader {
return &fsLoader{
fs: fs,
configDir: configDir,
func NewFSLoader(fs utilfs.Filesystem, configDir string) (Loader, error) {
_, kubeletCodecs, err := kubeletscheme.NewSchemeAndCodecs()
if err != nil {
return nil, err
}
return &fsLoader{
fs: fs,
kubeletCodecs: kubeletCodecs,
configDir: configDir,
}, nil
}
func (loader *fsLoader) Load() (*kubeletconfig.KubeletConfiguration, error) {
......@@ -62,5 +72,5 @@ func (loader *fsLoader) Load() (*kubeletconfig.KubeletConfiguration, error) {
return nil, fmt.Errorf(errfmt, fmt.Errorf("config file was empty, but some parameters are required"))
}
return utilcodec.DecodeKubeletConfiguration(data)
return utilcodec.DecodeKubeletConfiguration(loader.kubeletCodecs, data)
}
......@@ -53,7 +53,7 @@ const (
// - validates configuration
// - monitors for potential crash-loops caused by new configurations
// - tracks the last-known-good configuration, and rolls-back to last-known-good when necessary
// For more information, see the proposal: https://github.com/kubernetes/kubernetes/pull/29459
// For more information, see the proposal: https://github.com/kubernetes/community/blob/master/contributors/design-proposals/dynamic-kubelet-configuration.md
type Controller struct {
// dynamicConfig, if true, indicates that we should sync config from the API server
dynamicConfig bool
......@@ -90,12 +90,19 @@ type Controller struct {
// If the `initConfigDir` is an empty string, skips trying to load the init config.
// If the `dynamicConfigDir` is an empty string, skips trying to load checkpoints or download new config,
// but will still sync the ConfigOK condition if you call StartSync with a non-nil client.
func NewController(initConfigDir string, dynamicConfigDir string, defaultConfig *kubeletconfig.KubeletConfiguration) *Controller {
func NewController(initConfigDir string,
dynamicConfigDir string,
defaultConfig *kubeletconfig.KubeletConfiguration) (*Controller, error) {
var err error
fs := utilfs.DefaultFs{}
var initLoader configfiles.Loader
if len(initConfigDir) > 0 {
initLoader = configfiles.NewFSLoader(fs, initConfigDir)
initLoader, err = configfiles.NewFSLoader(fs, initConfigDir)
if err != nil {
return nil, err
}
}
dynamicConfig := false
if len(dynamicConfigDir) > 0 {
......@@ -120,7 +127,7 @@ func NewController(initConfigDir string, dynamicConfigDir string, defaultConfig
badConfigTracker: badconfig.NewFsTracker(fs, filepath.Join(dynamicConfigDir, badConfigTrackingDir, kubeletVersion)),
startupTracker: startups.NewFsTracker(fs, filepath.Join(dynamicConfigDir, startupTrackingDir, kubeletVersion)),
initLoader: initLoader,
}
}, nil
}
// Bootstrap attempts to return a valid KubeletConfiguration based on the configuration of the Controller,
......
......@@ -12,9 +12,8 @@ go_library(
"//pkg/api:go_default_library",
"//pkg/api/install:go_default_library",
"//pkg/kubelet/apis/kubeletconfig:go_default_library",
"//pkg/kubelet/apis/kubeletconfig/install:go_default_library",
"//pkg/kubelet/apis/kubeletconfig/v1alpha1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
],
)
......
......@@ -21,13 +21,11 @@ import (
// ensure the core apis are installed
_ "k8s.io/kubernetes/pkg/api/install"
// ensure the kubeletconfig apis are installed
_ "k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig/install"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig"
kubeletconfigv1alpha1 "k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig/v1alpha1"
)
// TODO(mtaufen): allow an encoder to be injected into checkpoint objects at creation time? (then we could ultimately instantiate only one encoder)
......@@ -50,28 +48,18 @@ func NewJSONEncoder(groupName string) (runtime.Encoder, error) {
return api.Codecs.EncoderForVersion(info.Serializer, versions[0]), nil
}
// DecodeKubeletConfiguration decodes an encoded (v1alpha1) KubeletConfiguration object to the internal type
func DecodeKubeletConfiguration(data []byte) (*kubeletconfig.KubeletConfiguration, error) {
// decode the object, note we use the external version scheme to decode, because users provide the external version
obj, err := runtime.Decode(api.Codecs.UniversalDecoder(kubeletconfigv1alpha1.SchemeGroupVersion), data)
// DecodeKubeletConfiguration decodes a serialized KubeletConfiguration to the internal type
func DecodeKubeletConfiguration(kubeletCodecs *serializer.CodecFactory, data []byte) (*kubeletconfig.KubeletConfiguration, error) {
// the UniversalDecoder runs defaulting and returns the internal type by default
obj, gvk, err := kubeletCodecs.UniversalDecoder().Decode(data, nil, nil)
if err != nil {
return nil, fmt.Errorf("failed to decode, error: %v", err)
}
externalKC, ok := obj.(*kubeletconfigv1alpha1.KubeletConfiguration)
internalKC, ok := obj.(*kubeletconfig.KubeletConfiguration)
if !ok {
return nil, fmt.Errorf("failed to cast object to KubeletConfiguration, object: %#v", obj)
return nil, fmt.Errorf("failed to cast object to KubeletConfiguration, unexpected type: %v", gvk)
}
// TODO(mtaufen): confirm whether api.Codecs.UniversalDecoder runs the defaulting, which would make this redundant
// run the defaulter on the decoded configuration before converting to internal type
api.Scheme.Default(externalKC)
// convert to internal type
internalKC := &kubeletconfig.KubeletConfiguration{}
err = api.Scheme.Convert(externalKC, internalKC, nil)
if err != nil {
return nil, err
}
return internalKC, nil
}
......@@ -30,6 +30,7 @@ go_library(
"//pkg/kubelet/apis/cri:go_default_library",
"//pkg/kubelet/apis/cri/v1alpha1/runtime:go_default_library",
"//pkg/kubelet/apis/kubeletconfig:go_default_library",
"//pkg/kubelet/apis/kubeletconfig/scheme:go_default_library",
"//pkg/kubelet/apis/kubeletconfig/v1alpha1:go_default_library",
"//pkg/kubelet/apis/stats/v1alpha1:go_default_library",
"//pkg/kubelet/metrics:go_default_library",
......
......@@ -37,6 +37,7 @@ import (
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig"
kubeletscheme "k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig/scheme"
kubeletconfigv1alpha1 "k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig/v1alpha1"
stats "k8s.io/kubernetes/pkg/kubelet/apis/stats/v1alpha1"
kubeletmetrics "k8s.io/kubernetes/pkg/kubelet/metrics"
......@@ -262,7 +263,7 @@ func makeKubeletConfigMap(internalKC *kubeletconfig.KubeletConfiguration) *v1.Co
externalKC := &kubeletconfigv1alpha1.KubeletConfiguration{}
api.Scheme.Convert(internalKC, externalKC, nil)
encoder, err := newJSONEncoder(kubeletconfig.GroupName)
encoder, err := newKubeletConfigJSONEncoder()
framework.ExpectNoError(err)
data, err := runtime.Encode(encoder, externalKC)
......@@ -310,21 +311,18 @@ func logKubeletMetrics(metricKeys ...string) {
}
}
func newJSONEncoder(groupName string) (runtime.Encoder, error) {
// encode to json
func newKubeletConfigJSONEncoder() (runtime.Encoder, error) {
_, kubeletCodecs, err := kubeletscheme.NewSchemeAndCodecs()
if err != nil {
return nil, err
}
mediaType := "application/json"
info, ok := runtime.SerializerInfoForMediaType(api.Codecs.SupportedMediaTypes(), mediaType)
info, ok := runtime.SerializerInfoForMediaType(kubeletCodecs.SupportedMediaTypes(), mediaType)
if !ok {
return nil, fmt.Errorf("unsupported media type %q", mediaType)
}
versions := api.Registry.EnabledVersionsForGroup(groupName)
if len(versions) == 0 {
return nil, fmt.Errorf("no enabled versions for group %q", groupName)
}
// the "best" version supposedly comes first in the list returned from api.Registry.EnabledVersionsForGroup
return api.Codecs.EncoderForVersion(info.Serializer, versions[0]), nil
return kubeletCodecs.EncoderForVersion(info.Serializer, kubeletconfigv1alpha1.SchemeGroupVersion), nil
}
// runCommand runs the cmd and returns the combined stdout and stderr, or an
......
......@@ -434,10 +434,6 @@ var ephemeralWhiteList = createEphemeralWhiteList(
gvr("componentconfig", "v1alpha1", "kubeproxyconfigurations"), // not stored in etcd
// --
// k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig/v1alpha1
gvr("kubeletconfig", "v1alpha1", "kubeletconfigurations"), // not stored in etcd
// --
// k8s.io/kubernetes/pkg/apis/extensions/v1beta1
gvr("extensions", "v1beta1", "deploymentrollbacks"), // used to rollback deployment, not stored in etcd
gvr("extensions", "v1beta1", "replicationcontrollerdummies"), // not stored in etcd
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment