diff --git a/cmd/kubelet/app/options/BUILD b/cmd/kubelet/app/options/BUILD index 9b19054d13..2cb6e59bdb 100644 --- a/cmd/kubelet/app/options/BUILD +++ b/cmd/kubelet/app/options/BUILD @@ -12,10 +12,11 @@ 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/scheme:go_default_library", + "//pkg/kubelet/apis/kubeletconfig/install: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", diff --git a/cmd/kubelet/app/options/options.go b/cmd/kubelet/app/options/options.go index ad60d5fa83..118d9c620a 100644 --- a/cmd/kubelet/app/options/options.go +++ b/cmd/kubelet/app/options/options.go @@ -25,12 +25,14 @@ 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" - 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" + // Need to make sure the kubeletconfig api is installed so defaulting funcs work + _ "k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig/install" + "k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig/v1alpha1" utiltaints "k8s.io/kubernetes/pkg/util/taints" "github.com/spf13/pflag" @@ -140,14 +142,10 @@ 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{} - scheme.Default(versioned) + api.Scheme.Default(versioned) config := &kubeletconfig.KubeletConfiguration{} - if err := scheme.Convert(versioned, config, nil); err != nil { + if err := api.Scheme.Convert(versioned, config, nil); err != nil { return nil, err } return config, nil diff --git a/cmd/kubelet/app/server.go b/cmd/kubelet/app/server.go index bade6b9907..69a2a5d8c1 100644 --- a/cmd/kubelet/app/server.go +++ b/cmd/kubelet/app/server.go @@ -756,8 +756,7 @@ 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 @@ -778,10 +777,7 @@ func BootstrapKubeletConfigController( } // get the latest KubeletConfiguration checkpoint from disk, or load the init or default config if no valid checkpoints exist - kubeletConfigController, err := kubeletconfig.NewController(initConfigDir, dynamicConfigDir, defaultConfig) - if err != nil { - return nil, nil, fmt.Errorf("failed to construct controller, error: %v", err) - } + kubeletConfigController := kubeletconfig.NewController(initConfigDir, dynamicConfigDir, defaultConfig) kubeletConfig, err := kubeletConfigController.Bootstrap() if err != nil { return nil, nil, fmt.Errorf("failed to determine a valid configuration, error: %v", err) diff --git a/cmd/kubelet/kubelet.go b/cmd/kubelet/kubelet.go index f63526f5c2..d26d213e5e 100644 --- a/cmd/kubelet/kubelet.go +++ b/cmd/kubelet/kubelet.go @@ -89,10 +89,7 @@ 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) diff --git a/pkg/kubelet/apis/kubeletconfig/BUILD b/pkg/kubelet/apis/kubeletconfig/BUILD index 07c85a597d..92d82340d2 100644 --- a/pkg/kubelet/apis/kubeletconfig/BUILD +++ b/pkg/kubelet/apis/kubeletconfig/BUILD @@ -36,7 +36,7 @@ filegroup( name = "all-srcs", srcs = [ ":package-srcs", - "//pkg/kubelet/apis/kubeletconfig/scheme:all-srcs", + "//pkg/kubelet/apis/kubeletconfig/install:all-srcs", "//pkg/kubelet/apis/kubeletconfig/v1alpha1:all-srcs", "//pkg/kubelet/apis/kubeletconfig/validation:all-srcs", ], diff --git a/pkg/kubelet/apis/kubeletconfig/scheme/BUILD b/pkg/kubelet/apis/kubeletconfig/install/BUILD similarity index 55% rename from pkg/kubelet/apis/kubeletconfig/scheme/BUILD rename to pkg/kubelet/apis/kubeletconfig/install/BUILD index 44660bf981..111801fa0b 100644 --- a/pkg/kubelet/apis/kubeletconfig/scheme/BUILD +++ b/pkg/kubelet/apis/kubeletconfig/install/BUILD @@ -1,14 +1,23 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +load( + "@io_bazel_rules_go//go:def.bzl", + "go_library", +) go_library( name = "go_default_library", - srcs = ["scheme.go"], - visibility = ["//visibility:public"], + srcs = ["install.go"], + tags = ["automanaged"], 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", ], ) @@ -23,5 +32,4 @@ filegroup( name = "all-srcs", srcs = [":package-srcs"], tags = ["automanaged"], - visibility = ["//visibility:public"], ) diff --git a/pkg/kubelet/apis/kubeletconfig/install/install.go b/pkg/kubelet/apis/kubeletconfig/install/install.go new file mode 100644 index 0000000000..731791f9d0 --- /dev/null +++ b/pkg/kubelet/apis/kubeletconfig/install/install.go @@ -0,0 +1,49 @@ +/* +Copyright 2017 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package install installs the experimental API group, making it available as +// an option to all of the API encoding/decoding machinery. +package install + +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/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) +} + +// 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) + } +} diff --git a/pkg/kubelet/apis/kubeletconfig/scheme/scheme.go b/pkg/kubelet/apis/kubeletconfig/scheme/scheme.go deleted file mode 100644 index 76f042818e..0000000000 --- a/pkg/kubelet/apis/kubeletconfig/scheme/scheme.go +++ /dev/null @@ -1,40 +0,0 @@ -/* -Copyright 2017 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package scheme - -import ( - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/serializer" - "k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig" - "k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig/v1alpha1" -) - -// Utility functions for the Kubelet's kubeletconfig API group - -// 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 -} diff --git a/pkg/kubelet/kubeletconfig/checkpoint/BUILD b/pkg/kubelet/kubeletconfig/checkpoint/BUILD index c4d1fa6f88..8e2844f81c 100644 --- a/pkg/kubelet/kubeletconfig/checkpoint/BUILD +++ b/pkg/kubelet/kubeletconfig/checkpoint/BUILD @@ -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,14 +40,12 @@ 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", ], ) diff --git a/pkg/kubelet/kubeletconfig/checkpoint/checkpoint.go b/pkg/kubelet/kubeletconfig/checkpoint/checkpoint.go index 852ea71488..e9a6c29d70 100644 --- a/pkg/kubelet/kubeletconfig/checkpoint/checkpoint.go +++ b/pkg/kubelet/kubeletconfig/checkpoint/checkpoint.go @@ -30,7 +30,7 @@ import ( type Checkpoint interface { // UID returns the UID of the config source object behind the Checkpoint UID() string - // Parse extracts the KubeletConfiguration from the checkpoint, applies defaults, and converts to the internal type + // Parse parses the checkpoint into the internal KubeletConfiguration type Parse() (*kubeletconfig.KubeletConfiguration, error) // Encode returns a []byte representation of the config source object behind the Checkpoint Encode() ([]byte, error) @@ -56,7 +56,6 @@ 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) } diff --git a/pkg/kubelet/kubeletconfig/checkpoint/checkpoint_test.go b/pkg/kubelet/kubeletconfig/checkpoint/checkpoint_test.go index abcf9981b1..d9cc259524 100644 --- a/pkg/kubelet/kubeletconfig/checkpoint/checkpoint_test.go +++ b/pkg/kubelet/kubeletconfig/checkpoint/checkpoint_test.go @@ -30,6 +30,20 @@ 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")}}) @@ -73,17 +87,3 @@ 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 -} diff --git a/pkg/kubelet/kubeletconfig/checkpoint/configmap.go b/pkg/kubelet/kubeletconfig/checkpoint/configmap.go index 7472384889..b0a8ecdea9 100644 --- a/pkg/kubelet/kubeletconfig/checkpoint/configmap.go +++ b/pkg/kubelet/kubeletconfig/checkpoint/configmap.go @@ -21,9 +21,7 @@ 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" ) @@ -31,8 +29,7 @@ const configMapConfigKey = "kubelet" // configMapCheckpoint implements Checkpoint, backed by a v1/ConfigMap config source object type configMapCheckpoint struct { - kubeletCodecs *serializer.CodecFactory // codecs for the KubeletConfiguration - configMap *apiv1.ConfigMap + configMap *apiv1.ConfigMap } // NewConfigMapCheckpoint returns a Checkpoint backed by `cm`. `cm` must be non-nil @@ -43,13 +40,7 @@ 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") } - - _, kubeletCodecs, err := kubeletscheme.NewSchemeAndCodecs() - if err != nil { - return nil, err - } - - return &configMapCheckpoint{kubeletCodecs, cm}, nil + return &configMapCheckpoint{cm}, nil } // UID returns the UID of a configMapCheckpoint @@ -57,23 +48,25 @@ func (c *configMapCheckpoint) UID() string { return string(c.configMap.UID) } -// Parse extracts the KubeletConfiguration from v1/ConfigMap checkpoints, applies defaults, and converts to the internal type +// implements Parse for v1/ConfigMap checkpoints func (c *configMapCheckpoint) Parse() (*kubeletconfig.KubeletConfiguration, error) { const emptyCfgErr = "config was empty, but some parameters are required" - if len(c.configMap.Data) == 0 { + cm := c.configMap + + if len(cm.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 := c.configMap.Data[configMapConfigKey] + config, ok := cm.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(c.kubeletCodecs, []byte(config)) + return utilcodec.DecodeKubeletConfiguration([]byte(config)) } // Encode encodes a configMapCheckpoint diff --git a/pkg/kubelet/kubeletconfig/checkpoint/configmap_test.go b/pkg/kubelet/kubeletconfig/checkpoint/configmap_test.go index 98d8a37eb2..07092e4446 100644 --- a/pkg/kubelet/kubeletconfig/checkpoint/configmap_test.go +++ b/pkg/kubelet/kubeletconfig/checkpoint/configmap_test.go @@ -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,15 +66,9 @@ 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)}, }, @@ -88,16 +82,11 @@ 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{} - kubeletScheme.Default(external) + api.Scheme.Default(external) defaultConfig := &kubeletconfig.KubeletConfiguration{} - err = kubeletScheme.Convert(external, defaultConfig, nil) + err := api.Scheme.Convert(external, defaultConfig, nil) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -134,7 +123,7 @@ apiVersion: kubeletconfig/v1alpha1`}}, defaultConfig, ""}, "kubelet": `{"kind":"KubeletConfiguration","apiVersion":"kubeletconfig/v1alpha1"}`}}, defaultConfig, ""}, } for _, c := range cases { - cpt := &configMapCheckpoint{kubeletCodecs, c.cm} + cpt := &configMapCheckpoint{c.cm} kc, err := cpt.Parse() if utiltest.SkipRest(t, c.desc, err, c.err) { continue @@ -147,11 +136,6 @@ 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. @@ -162,7 +146,7 @@ func TestConfigMapCheckpointEncode(t *testing.T) { }{ // we expect Checkpoints to be encoded as a json representation of the underlying API object {"one-key", - &configMapCheckpoint{kubeletCodecs, &apiv1.ConfigMap{ + &configMapCheckpoint{&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":""}} @@ -182,11 +166,6 @@ 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 @@ -194,7 +173,7 @@ func TestConfigMapCheckpointRoundTrip(t *testing.T) { }{ // empty data {"empty data", - &configMapCheckpoint{kubeletCodecs, &apiv1.ConfigMap{ + &configMapCheckpoint{&apiv1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: "empty-data-sha256-e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", UID: "uid", @@ -203,7 +182,7 @@ func TestConfigMapCheckpointRoundTrip(t *testing.T) { ""}, // two keys {"two keys", - &configMapCheckpoint{kubeletCodecs, &apiv1.ConfigMap{ + &configMapCheckpoint{&apiv1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: "two-keys-sha256-2bff03d6249c8a9dc9a1436d087c124741361ccfac6615b81b67afcff5c42431", UID: "uid", @@ -212,7 +191,7 @@ func TestConfigMapCheckpointRoundTrip(t *testing.T) { ""}, // missing uid {"missing uid", - &configMapCheckpoint{kubeletCodecs, &apiv1.ConfigMap{ + &configMapCheckpoint{&apiv1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: "two-keys-sha256-2bff03d6249c8a9dc9a1436d087c124741361ccfac6615b81b67afcff5c42431", UID: "", diff --git a/pkg/kubelet/kubeletconfig/checkpoint/download.go b/pkg/kubelet/kubeletconfig/checkpoint/download.go index 14374a4cbf..b61b394ab4 100644 --- a/pkg/kubelet/kubeletconfig/checkpoint/download.go +++ b/pkg/kubelet/kubeletconfig/checkpoint/download.go @@ -113,6 +113,7 @@ 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) @@ -130,14 +131,8 @@ 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 checkpoint, "", nil + return &configMapCheckpoint{cm}, "", nil } func (r *remoteConfigMap) Encode() ([]byte, error) { diff --git a/pkg/kubelet/kubeletconfig/checkpoint/download_test.go b/pkg/kubelet/kubeletconfig/checkpoint/download_test.go index da3f60e343..496353919f 100644 --- a/pkg/kubelet/kubeletconfig/checkpoint/download_test.go +++ b/pkg/kubelet/kubeletconfig/checkpoint/download_test.go @@ -26,7 +26,6 @@ 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" ) @@ -93,11 +92,6 @@ 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", @@ -124,7 +118,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{kubeletCodecs, cm}, ""}, + &configMapCheckpoint{cm}, ""}, } for _, c := range cases { diff --git a/pkg/kubelet/kubeletconfig/configfiles/BUILD b/pkg/kubelet/kubeletconfig/configfiles/BUILD index 2884d0cfbf..94a32a7191 100644 --- a/pkg/kubelet/kubeletconfig/configfiles/BUILD +++ b/pkg/kubelet/kubeletconfig/configfiles/BUILD @@ -10,10 +10,8 @@ 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", ], ) diff --git a/pkg/kubelet/kubeletconfig/configfiles/configfiles.go b/pkg/kubelet/kubeletconfig/configfiles/configfiles.go index c3ad50e22f..449c5c8112 100644 --- a/pkg/kubelet/kubeletconfig/configfiles/configfiles.go +++ b/pkg/kubelet/kubeletconfig/configfiles/configfiles.go @@ -20,9 +20,7 @@ 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" ) @@ -37,24 +35,16 @@ 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, error) { - _, kubeletCodecs, err := kubeletscheme.NewSchemeAndCodecs() - if err != nil { - return nil, err - } - +func NewFSLoader(fs utilfs.Filesystem, configDir string) Loader { return &fsLoader{ - fs: fs, - kubeletCodecs: kubeletCodecs, - configDir: configDir, - }, nil + fs: fs, + configDir: configDir, + } } func (loader *fsLoader) Load() (*kubeletconfig.KubeletConfiguration, error) { @@ -72,5 +62,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(loader.kubeletCodecs, data) + return utilcodec.DecodeKubeletConfiguration(data) } diff --git a/pkg/kubelet/kubeletconfig/controller.go b/pkg/kubelet/kubeletconfig/controller.go index afee768085..f6f0c34e6d 100644 --- a/pkg/kubelet/kubeletconfig/controller.go +++ b/pkg/kubelet/kubeletconfig/controller.go @@ -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/community/blob/master/contributors/design-proposals/dynamic-kubelet-configuration.md +// For more information, see the proposal: https://github.com/kubernetes/kubernetes/pull/29459 type Controller struct { // dynamicConfig, if true, indicates that we should sync config from the API server dynamicConfig bool @@ -90,19 +90,12 @@ 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, error) { - var err error - +func NewController(initConfigDir string, dynamicConfigDir string, defaultConfig *kubeletconfig.KubeletConfiguration) *Controller { fs := utilfs.DefaultFs{} var initLoader configfiles.Loader if len(initConfigDir) > 0 { - initLoader, err = configfiles.NewFSLoader(fs, initConfigDir) - if err != nil { - return nil, err - } + initLoader = configfiles.NewFSLoader(fs, initConfigDir) } dynamicConfig := false if len(dynamicConfigDir) > 0 { @@ -127,7 +120,7 @@ func NewController(initConfigDir string, 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, diff --git a/pkg/kubelet/kubeletconfig/util/codec/BUILD b/pkg/kubelet/kubeletconfig/util/codec/BUILD index df4916b26c..9a55c12caf 100644 --- a/pkg/kubelet/kubeletconfig/util/codec/BUILD +++ b/pkg/kubelet/kubeletconfig/util/codec/BUILD @@ -12,8 +12,9 @@ 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", ], ) diff --git a/pkg/kubelet/kubeletconfig/util/codec/codec.go b/pkg/kubelet/kubeletconfig/util/codec/codec.go index 148967caba..14432dae6e 100644 --- a/pkg/kubelet/kubeletconfig/util/codec/codec.go +++ b/pkg/kubelet/kubeletconfig/util/codec/codec.go @@ -21,11 +21,13 @@ 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) @@ -48,18 +50,28 @@ func NewJSONEncoder(groupName string) (runtime.Encoder, error) { return api.Codecs.EncoderForVersion(info.Serializer, versions[0]), nil } -// 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) +// 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) if err != nil { return nil, fmt.Errorf("failed to decode, error: %v", err) } - internalKC, ok := obj.(*kubeletconfig.KubeletConfiguration) + externalKC, ok := obj.(*kubeletconfigv1alpha1.KubeletConfiguration) if !ok { - return nil, fmt.Errorf("failed to cast object to KubeletConfiguration, unexpected type: %v", gvk) + return nil, fmt.Errorf("failed to cast object to KubeletConfiguration, object: %#v", obj) } + // 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 } diff --git a/test/e2e_node/BUILD b/test/e2e_node/BUILD index 719f488102..fad63ad1cb 100644 --- a/test/e2e_node/BUILD +++ b/test/e2e_node/BUILD @@ -30,7 +30,6 @@ 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", diff --git a/test/e2e_node/util.go b/test/e2e_node/util.go index 80cf72a4aa..3cbca9dd85 100644 --- a/test/e2e_node/util.go +++ b/test/e2e_node/util.go @@ -37,7 +37,6 @@ 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" @@ -263,7 +262,7 @@ func makeKubeletConfigMap(internalKC *kubeletconfig.KubeletConfiguration) *v1.Co externalKC := &kubeletconfigv1alpha1.KubeletConfiguration{} api.Scheme.Convert(internalKC, externalKC, nil) - encoder, err := newKubeletConfigJSONEncoder() + encoder, err := newJSONEncoder(kubeletconfig.GroupName) framework.ExpectNoError(err) data, err := runtime.Encode(encoder, externalKC) @@ -311,18 +310,21 @@ func logKubeletMetrics(metricKeys ...string) { } } -func newKubeletConfigJSONEncoder() (runtime.Encoder, error) { - _, kubeletCodecs, err := kubeletscheme.NewSchemeAndCodecs() - if err != nil { - return nil, err - } - +func newJSONEncoder(groupName string) (runtime.Encoder, error) { + // encode to json mediaType := "application/json" - info, ok := runtime.SerializerInfoForMediaType(kubeletCodecs.SupportedMediaTypes(), mediaType) + info, ok := runtime.SerializerInfoForMediaType(api.Codecs.SupportedMediaTypes(), mediaType) if !ok { return nil, fmt.Errorf("unsupported media type %q", mediaType) } - return kubeletCodecs.EncoderForVersion(info.Serializer, kubeletconfigv1alpha1.SchemeGroupVersion), nil + + 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 } // runCommand runs the cmd and returns the combined stdout and stderr, or an diff --git a/test/integration/etcd/etcd_storage_path_test.go b/test/integration/etcd/etcd_storage_path_test.go index d94be2fe09..b3462acdb0 100644 --- a/test/integration/etcd/etcd_storage_path_test.go +++ b/test/integration/etcd/etcd_storage_path_test.go @@ -434,6 +434,10 @@ 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