mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-14 02:43:36 +00:00
vendor files
This commit is contained in:
61
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/BUILD
generated
vendored
Normal file
61
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/BUILD
generated
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"configsync.go",
|
||||
"controller.go",
|
||||
"rollback.go",
|
||||
"watch.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/kubelet/kubeletconfig",
|
||||
deps = [
|
||||
"//pkg/kubelet/apis/kubeletconfig:go_default_library",
|
||||
"//pkg/kubelet/apis/kubeletconfig/validation:go_default_library",
|
||||
"//pkg/kubelet/kubeletconfig/checkpoint:go_default_library",
|
||||
"//pkg/kubelet/kubeletconfig/checkpoint/store:go_default_library",
|
||||
"//pkg/kubelet/kubeletconfig/configfiles:go_default_library",
|
||||
"//pkg/kubelet/kubeletconfig/status:go_default_library",
|
||||
"//pkg/kubelet/kubeletconfig/util/equal:go_default_library",
|
||||
"//pkg/kubelet/kubeletconfig/util/log:go_default_library",
|
||||
"//pkg/kubelet/kubeletconfig/util/panic:go_default_library",
|
||||
"//pkg/util/filesystem:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/fields:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/watch:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/cache:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [
|
||||
":package-srcs",
|
||||
"//pkg/kubelet/kubeletconfig/checkpoint:all-srcs",
|
||||
"//pkg/kubelet/kubeletconfig/configfiles:all-srcs",
|
||||
"//pkg/kubelet/kubeletconfig/status:all-srcs",
|
||||
"//pkg/kubelet/kubeletconfig/util/codec:all-srcs",
|
||||
"//pkg/kubelet/kubeletconfig/util/equal:all-srcs",
|
||||
"//pkg/kubelet/kubeletconfig/util/files:all-srcs",
|
||||
"//pkg/kubelet/kubeletconfig/util/log:all-srcs",
|
||||
"//pkg/kubelet/kubeletconfig/util/panic:all-srcs",
|
||||
"//pkg/kubelet/kubeletconfig/util/test:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
)
|
5
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/OWNERS
generated
vendored
Normal file
5
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/OWNERS
generated
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
approvers:
|
||||
- mtaufen
|
||||
- dchen1107
|
||||
reviewers:
|
||||
- sig-node-reviewers
|
72
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/checkpoint/BUILD
generated
vendored
Normal file
72
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/checkpoint/BUILD
generated
vendored
Normal file
@ -0,0 +1,72 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"checkpoint_test.go",
|
||||
"configmap_test.go",
|
||||
"download_test.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/kubelet/kubeletconfig/checkpoint",
|
||||
library = ":go_default_library",
|
||||
deps = [
|
||||
"//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",
|
||||
"//vendor/github.com/davecgh/go-spew/spew: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/types:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes/fake:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"checkpoint.go",
|
||||
"configmap.go",
|
||||
"download.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/kubelet/kubeletconfig/checkpoint",
|
||||
deps = [
|
||||
"//pkg/api/legacyscheme:go_default_library",
|
||||
"//pkg/kubelet/apis/kubeletconfig:go_default_library",
|
||||
"//pkg/kubelet/apis/kubeletconfig/scheme:go_default_library",
|
||||
"//pkg/kubelet/kubeletconfig/status: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",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [
|
||||
":package-srcs",
|
||||
"//pkg/kubelet/kubeletconfig/checkpoint/store:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
)
|
72
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/checkpoint/checkpoint.go
generated
vendored
Normal file
72
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/checkpoint/checkpoint.go
generated
vendored
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
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 checkpoint
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
"k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig"
|
||||
)
|
||||
|
||||
// Checkpoint represents a local copy of a config source (payload) object
|
||||
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() (*kubeletconfig.KubeletConfiguration, error)
|
||||
// Encode returns a []byte representation of the config source object behind the Checkpoint
|
||||
Encode() ([]byte, error)
|
||||
|
||||
// object returns the underlying checkpointed object. If you want to compare sources for equality, use EqualCheckpoints,
|
||||
// which compares the underlying checkpointed objects for semantic API equality.
|
||||
object() interface{}
|
||||
}
|
||||
|
||||
// DecodeCheckpoint is a helper for using the apimachinery to decode serialized checkpoints
|
||||
func DecodeCheckpoint(data []byte) (Checkpoint, error) {
|
||||
// decode the checkpoint
|
||||
obj, err := runtime.Decode(legacyscheme.Codecs.UniversalDecoder(), data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode, error: %v", err)
|
||||
}
|
||||
|
||||
// TODO(mtaufen): for now we assume we are trying to load a ConfigMap checkpoint, may need to extend this if we allow other checkpoint types
|
||||
|
||||
// convert it to the external ConfigMap type, so we're consistently working with the external type outside of the on-disk representation
|
||||
cm := &apiv1.ConfigMap{}
|
||||
err = legacyscheme.Scheme.Convert(obj, cm, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to convert decoded object into a v1 ConfigMap, error: %v", err)
|
||||
}
|
||||
|
||||
return NewConfigMapCheckpoint(cm)
|
||||
}
|
||||
|
||||
// EqualCheckpoints compares two Checkpoints for equality, if their underlying objects are equal, so are the Checkpoints
|
||||
func EqualCheckpoints(a, b Checkpoint) bool {
|
||||
if a != nil && b != nil {
|
||||
return apiequality.Semantic.DeepEqual(a.object(), b.object())
|
||||
}
|
||||
if a == nil && b == nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
89
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/checkpoint/checkpoint_test.go
generated
vendored
Normal file
89
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/checkpoint/checkpoint_test.go
generated
vendored
Normal file
@ -0,0 +1,89 @@
|
||||
/*
|
||||
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 checkpoint
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
utilcodec "k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/codec"
|
||||
utiltest "k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/test"
|
||||
)
|
||||
|
||||
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")}})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// generate unsupported object encoding for unsupported type test case
|
||||
unsupported := newUnsupportedEncoded(t)
|
||||
|
||||
// test cases
|
||||
cases := []struct {
|
||||
desc string
|
||||
data []byte
|
||||
expect Checkpoint // expect a deeply-equal Checkpoint to be returned from Decode
|
||||
err string // expect error to contain this substring
|
||||
}{
|
||||
// v1/ConfigMap
|
||||
{"v1/ConfigMap", []byte(`{"apiVersion": "v1","kind": "ConfigMap","metadata": {"uid": "uid"}}`), cm, ""},
|
||||
// malformed
|
||||
{"malformed", []byte("malformed"), nil, "failed to decode"},
|
||||
// no UID
|
||||
{"no UID", []byte(`{"apiVersion": "v1","kind": "ConfigMap"}`), nil, "ConfigMap must have a UID"},
|
||||
// well-formed, but unsupported type
|
||||
{"well-formed, but unsupported encoded type", unsupported, nil, "failed to convert"},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
cpt, err := DecodeCheckpoint(c.data)
|
||||
if utiltest.SkipRest(t, c.desc, err, c.err) {
|
||||
continue
|
||||
}
|
||||
// Unfortunately reflect.DeepEqual treats nil data structures as != empty data structures, so
|
||||
// we have to settle for semantic equality of the underlying checkpointed API objects.
|
||||
// If additional fields are added to the object that implements the Checkpoint interface,
|
||||
// they should be added to a named sub-object to facilitate a DeepEquals comparison
|
||||
// of the extra fields.
|
||||
// decoded checkpoint should match expected checkpoint
|
||||
if !apiequality.Semantic.DeepEqual(cpt.object(), c.expect.object()) {
|
||||
t.Errorf("case %q, expect checkpoint %s but got %s", c.desc, spew.Sdump(c.expect), spew.Sdump(cpt))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
95
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/checkpoint/configmap.go
generated
vendored
Normal file
95
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/checkpoint/configmap.go
generated
vendored
Normal file
@ -0,0 +1,95 @@
|
||||
/*
|
||||
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 checkpoint
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
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"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// NewConfigMapCheckpoint returns a Checkpoint backed by `cm`. `cm` must be non-nil
|
||||
// and have a non-empty ObjectMeta.UID, or an error will be returned.
|
||||
func NewConfigMapCheckpoint(cm *apiv1.ConfigMap) (Checkpoint, error) {
|
||||
if cm == nil {
|
||||
return nil, fmt.Errorf("ConfigMap must be non-nil to be treated as a Checkpoint")
|
||||
} 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
|
||||
}
|
||||
|
||||
// UID returns the UID of a configMapCheckpoint
|
||||
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
|
||||
func (c *configMapCheckpoint) Parse() (*kubeletconfig.KubeletConfiguration, error) {
|
||||
const emptyCfgErr = "config was empty, but some parameters are required"
|
||||
|
||||
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 := 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(c.kubeletCodecs, []byte(config))
|
||||
}
|
||||
|
||||
// Encode encodes a configMapCheckpoint
|
||||
func (c *configMapCheckpoint) Encode() ([]byte, error) {
|
||||
cm := c.configMap
|
||||
encoder, err := utilcodec.NewJSONEncoder(apiv1.GroupName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data, err := runtime.Encode(encoder, cm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (c *configMapCheckpoint) object() interface{} {
|
||||
return c.configMap
|
||||
}
|
237
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/checkpoint/configmap_test.go
generated
vendored
Normal file
237
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/checkpoint/configmap_test.go
generated
vendored
Normal file
@ -0,0 +1,237 @@
|
||||
/*
|
||||
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 checkpoint
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"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"
|
||||
)
|
||||
|
||||
func TestNewConfigMapCheckpoint(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
cm *apiv1.ConfigMap
|
||||
err string
|
||||
}{
|
||||
{"nil v1/ConfigMap", nil, "must be non-nil"},
|
||||
{"empty v1/ConfigMap", &apiv1.ConfigMap{}, "must have a UID"},
|
||||
{"populated v1/ConfigMap",
|
||||
&apiv1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "name",
|
||||
UID: types.UID("uid"),
|
||||
},
|
||||
Data: map[string]string{
|
||||
"key1": "value1",
|
||||
"key2": "value2",
|
||||
},
|
||||
}, ""},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
cpt, err := NewConfigMapCheckpoint(c.cm)
|
||||
if utiltest.SkipRest(t, c.desc, err, c.err) {
|
||||
continue
|
||||
}
|
||||
// underlying object should match the object passed in
|
||||
if !apiequality.Semantic.DeepEqual(cpt.object(), c.cm) {
|
||||
t.Errorf("case %q, expect Checkpoint %s but got %s", c.desc, spew.Sdump(c.cm), spew.Sdump(cpt))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)},
|
||||
},
|
||||
}
|
||||
// UID method should return the correct value of the UID
|
||||
uidOut := cpt.UID()
|
||||
if uidIn != uidOut {
|
||||
t.Errorf("expect UID() to return %q, but got %q", uidIn, uidOut)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
defaultConfig := &kubeletconfig.KubeletConfiguration{}
|
||||
err = kubeletScheme.Convert(external, defaultConfig, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
cm *apiv1.ConfigMap
|
||||
expect *kubeletconfig.KubeletConfiguration
|
||||
err string
|
||||
}{
|
||||
{"empty data", &apiv1.ConfigMap{}, nil, "config was empty"},
|
||||
// missing kubelet key
|
||||
{"missing kubelet key", &apiv1.ConfigMap{Data: map[string]string{
|
||||
"bogus": "stuff"}}, nil, fmt.Sprintf("key %q not found", configMapConfigKey)},
|
||||
// invalid format
|
||||
{"invalid yaml", &apiv1.ConfigMap{Data: map[string]string{
|
||||
"kubelet": "*"}}, nil, "failed to decode"},
|
||||
{"invalid json", &apiv1.ConfigMap{Data: map[string]string{
|
||||
"kubelet": "{*"}}, nil, "failed to decode"},
|
||||
// invalid object
|
||||
{"missing kind", &apiv1.ConfigMap{Data: map[string]string{
|
||||
"kubelet": `{"apiVersion":"kubeletconfig/v1alpha1"}`}}, nil, "failed to decode"},
|
||||
{"missing version", &apiv1.ConfigMap{Data: map[string]string{
|
||||
"kubelet": `{"kind":"KubeletConfiguration"}`}}, nil, "failed to decode"},
|
||||
{"unregistered kind", &apiv1.ConfigMap{Data: map[string]string{
|
||||
"kubelet": `{"kind":"BogusKind","apiVersion":"kubeletconfig/v1alpha1"}`}}, nil, "failed to decode"},
|
||||
{"unregistered version", &apiv1.ConfigMap{Data: map[string]string{
|
||||
"kubelet": `{"kind":"KubeletConfiguration","apiVersion":"bogusversion"}`}}, nil, "failed to decode"},
|
||||
// empty object with correct kind and version should result in the defaults for that kind and version
|
||||
{"default from yaml", &apiv1.ConfigMap{Data: map[string]string{
|
||||
"kubelet": `kind: KubeletConfiguration
|
||||
apiVersion: kubeletconfig/v1alpha1`}}, defaultConfig, ""},
|
||||
{"default from json", &apiv1.ConfigMap{Data: map[string]string{
|
||||
"kubelet": `{"kind":"KubeletConfiguration","apiVersion":"kubeletconfig/v1alpha1"}`}}, defaultConfig, ""},
|
||||
}
|
||||
for _, c := range cases {
|
||||
cpt := &configMapCheckpoint{kubeletCodecs, c.cm}
|
||||
kc, err := cpt.Parse()
|
||||
if utiltest.SkipRest(t, c.desc, err, c.err) {
|
||||
continue
|
||||
}
|
||||
// we expect the parsed configuration to match what we described in the ConfigMap
|
||||
if !apiequality.Semantic.DeepEqual(c.expect, kc) {
|
||||
t.Errorf("case %q, expect config %s but got %s", c.desc, spew.Sdump(c.expect), spew.Sdump(kc))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
cases := []struct {
|
||||
desc string
|
||||
cpt *configMapCheckpoint
|
||||
expect string
|
||||
}{
|
||||
// we expect Checkpoints to be encoded as a json representation of the underlying API object
|
||||
{"one-key",
|
||||
&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":""}}
|
||||
`},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
data, err := c.cpt.Encode()
|
||||
// we don't expect any errors from encoding
|
||||
if utiltest.SkipRest(t, c.desc, err, "") {
|
||||
continue
|
||||
}
|
||||
if string(data) != c.expect {
|
||||
t.Errorf("case %q, expect encoding %q but got %q", c.desc, c.expect, string(data))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigMapCheckpointRoundTrip(t *testing.T) {
|
||||
_, kubeletCodecs, err := kubeletscheme.NewSchemeAndCodecs()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
cpt *configMapCheckpoint
|
||||
decodeErr string
|
||||
}{
|
||||
// empty data
|
||||
{"empty data",
|
||||
&configMapCheckpoint{kubeletCodecs, &apiv1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "empty-data-sha256-e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
UID: "uid",
|
||||
},
|
||||
Data: map[string]string{}}},
|
||||
""},
|
||||
// two keys
|
||||
{"two keys",
|
||||
&configMapCheckpoint{kubeletCodecs, &apiv1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "two-keys-sha256-2bff03d6249c8a9dc9a1436d087c124741361ccfac6615b81b67afcff5c42431",
|
||||
UID: "uid",
|
||||
},
|
||||
Data: map[string]string{"one": "", "two": "2"}}},
|
||||
""},
|
||||
// missing uid
|
||||
{"missing uid",
|
||||
&configMapCheckpoint{kubeletCodecs, &apiv1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "two-keys-sha256-2bff03d6249c8a9dc9a1436d087c124741361ccfac6615b81b67afcff5c42431",
|
||||
UID: "",
|
||||
},
|
||||
Data: map[string]string{"one": "", "two": "2"}}},
|
||||
"must have a UID"},
|
||||
}
|
||||
for _, c := range cases {
|
||||
// we don't expect any errors from encoding
|
||||
data, err := c.cpt.Encode()
|
||||
if utiltest.SkipRest(t, c.desc, err, "") {
|
||||
continue
|
||||
}
|
||||
after, err := DecodeCheckpoint(data)
|
||||
if utiltest.SkipRest(t, c.desc, err, c.decodeErr) {
|
||||
continue
|
||||
}
|
||||
if !apiequality.Semantic.DeepEqual(c.cpt.object(), after.object()) {
|
||||
t.Errorf("case %q, expect round-trip result %s but got %s", c.desc, spew.Sdump(c.cpt), spew.Sdump(after))
|
||||
}
|
||||
}
|
||||
}
|
156
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/checkpoint/download.go
generated
vendored
Normal file
156
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/checkpoint/download.go
generated
vendored
Normal file
@ -0,0 +1,156 @@
|
||||
/*
|
||||
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 checkpoint
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
"k8s.io/kubernetes/pkg/kubelet/kubeletconfig/status"
|
||||
utilcodec "k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/codec"
|
||||
utillog "k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/log"
|
||||
)
|
||||
|
||||
// RemoteConfigSource represents a remote config source object that can be downloaded as a Checkpoint
|
||||
type RemoteConfigSource interface {
|
||||
// UID returns the UID of the remote config source object
|
||||
UID() string
|
||||
// Download downloads the remote config source object returns a Checkpoint backed by the object,
|
||||
// or a sanitized failure reason and error if the download fails
|
||||
Download(client clientset.Interface) (Checkpoint, string, error)
|
||||
// Encode returns a []byte representation of the object behind the RemoteConfigSource
|
||||
Encode() ([]byte, error)
|
||||
|
||||
// object returns the underlying source object. If you want to compare sources for equality, use EqualRemoteConfigSources,
|
||||
// which compares the underlying source objects for semantic API equality.
|
||||
object() interface{}
|
||||
}
|
||||
|
||||
// NewRemoteConfigSource constructs a RemoteConfigSource from a v1/NodeConfigSource object, or returns
|
||||
// a sanitized failure reason and an error if the `source` is blatantly invalid.
|
||||
// You should only call this with a non-nil config source.
|
||||
func NewRemoteConfigSource(source *apiv1.NodeConfigSource) (RemoteConfigSource, string, error) {
|
||||
// exactly one subfield of the config source must be non-nil, toady ConfigMapRef is the only reference
|
||||
if source.ConfigMapRef == nil {
|
||||
return nil, status.FailSyncReasonAllNilSubfields, fmt.Errorf("%s, NodeConfigSource was: %#v", status.FailSyncReasonAllNilSubfields, source)
|
||||
}
|
||||
|
||||
// validate the NodeConfigSource:
|
||||
|
||||
// at this point we know we're using the ConfigMapRef subfield
|
||||
ref := source.ConfigMapRef
|
||||
|
||||
// name, namespace, and UID must all be non-empty for ConfigMapRef
|
||||
if ref.Name == "" || ref.Namespace == "" || string(ref.UID) == "" {
|
||||
return nil, status.FailSyncReasonPartialObjectReference, fmt.Errorf("%s, ObjectReference was: %#v", status.FailSyncReasonPartialObjectReference, ref)
|
||||
}
|
||||
|
||||
return &remoteConfigMap{source}, "", nil
|
||||
}
|
||||
|
||||
// DecodeRemoteConfigSource is a helper for using the apimachinery to decode serialized RemoteConfigSources;
|
||||
// e.g. the objects stored in the .cur and .lkg files by checkpoint/store/fsstore.go
|
||||
func DecodeRemoteConfigSource(data []byte) (RemoteConfigSource, error) {
|
||||
// decode the remote config source
|
||||
obj, err := runtime.Decode(legacyscheme.Codecs.UniversalDecoder(), data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode, error: %v", err)
|
||||
}
|
||||
|
||||
// for now we assume we are trying to load an apiv1.NodeConfigSource,
|
||||
// this may need to be extended if e.g. a new version of the api is born
|
||||
|
||||
// convert it to the external NodeConfigSource type, so we're consistently working with the external type outside of the on-disk representation
|
||||
cs := &apiv1.NodeConfigSource{}
|
||||
err = legacyscheme.Scheme.Convert(obj, cs, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to convert decoded object into a v1 NodeConfigSource, error: %v", err)
|
||||
}
|
||||
source, _, err := NewRemoteConfigSource(cs)
|
||||
return source, err
|
||||
}
|
||||
|
||||
// EqualRemoteConfigSources is a helper for comparing remote config sources by
|
||||
// comparing the underlying API objects for semantic equality.
|
||||
func EqualRemoteConfigSources(a, b RemoteConfigSource) bool {
|
||||
if a != nil && b != nil {
|
||||
return apiequality.Semantic.DeepEqual(a.object(), b.object())
|
||||
}
|
||||
if a == nil && b == nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// remoteConfigMap implements RemoteConfigSource for v1/ConfigMap config sources
|
||||
type remoteConfigMap struct {
|
||||
source *apiv1.NodeConfigSource
|
||||
}
|
||||
|
||||
func (r *remoteConfigMap) UID() string {
|
||||
return string(r.source.ConfigMapRef.UID)
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
// get the ConfigMap via namespace/name, there doesn't seem to be a way to get it by UID
|
||||
cm, err := client.CoreV1().ConfigMaps(r.source.ConfigMapRef.Namespace).Get(r.source.ConfigMapRef.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
reason = fmt.Sprintf(status.FailSyncReasonDownloadFmt, r.source.ConfigMapRef.Name, r.source.ConfigMapRef.Namespace)
|
||||
return nil, reason, fmt.Errorf("%s, error: %v", reason, err)
|
||||
}
|
||||
|
||||
// ensure that UID matches the UID on the reference, the ObjectReference must be unambiguous
|
||||
if r.source.ConfigMapRef.UID != cm.UID {
|
||||
reason = fmt.Sprintf(status.FailSyncReasonUIDMismatchFmt, r.source.ConfigMapRef.UID, cm.UID)
|
||||
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
|
||||
}
|
||||
|
||||
func (r *remoteConfigMap) Encode() ([]byte, error) {
|
||||
encoder, err := utilcodec.NewJSONEncoder(apiv1.GroupName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data, err := runtime.Encode(encoder, r.source)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (r *remoteConfigMap) object() interface{} {
|
||||
return r.source
|
||||
}
|
140
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/checkpoint/download_test.go
generated
vendored
Normal file
140
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/checkpoint/download_test.go
generated
vendored
Normal file
@ -0,0 +1,140 @@
|
||||
/*
|
||||
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 checkpoint
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||
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"
|
||||
)
|
||||
|
||||
func TestNewRemoteConfigSource(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
source *apiv1.NodeConfigSource
|
||||
expect RemoteConfigSource
|
||||
err string
|
||||
}{
|
||||
// all NodeConfigSource subfields nil
|
||||
{"all NodeConfigSource subfields nil",
|
||||
&apiv1.NodeConfigSource{}, nil, "exactly one subfield must be non-nil"},
|
||||
{"ConfigMapRef: empty name, namespace, and UID",
|
||||
&apiv1.NodeConfigSource{ConfigMapRef: &apiv1.ObjectReference{}}, nil, "invalid ObjectReference"},
|
||||
// ConfigMapRef: empty name and namespace
|
||||
{"ConfigMapRef: empty name and namespace",
|
||||
&apiv1.NodeConfigSource{ConfigMapRef: &apiv1.ObjectReference{UID: "uid"}}, nil, "invalid ObjectReference"},
|
||||
// ConfigMapRef: empty name and UID
|
||||
{"ConfigMapRef: empty name and UID",
|
||||
&apiv1.NodeConfigSource{ConfigMapRef: &apiv1.ObjectReference{Namespace: "namespace"}}, nil, "invalid ObjectReference"},
|
||||
// ConfigMapRef: empty namespace and UID
|
||||
{"ConfigMapRef: empty namespace and UID",
|
||||
&apiv1.NodeConfigSource{ConfigMapRef: &apiv1.ObjectReference{Name: "name"}}, nil, "invalid ObjectReference"},
|
||||
// ConfigMapRef: empty UID
|
||||
{"ConfigMapRef: empty namespace and UID",
|
||||
&apiv1.NodeConfigSource{ConfigMapRef: &apiv1.ObjectReference{Name: "name", Namespace: "namespace"}}, nil, "invalid ObjectReference"},
|
||||
// ConfigMapRef: empty namespace
|
||||
{"ConfigMapRef: empty namespace and UID",
|
||||
&apiv1.NodeConfigSource{ConfigMapRef: &apiv1.ObjectReference{Name: "name", UID: "uid"}}, nil, "invalid ObjectReference"},
|
||||
// ConfigMapRef: empty name
|
||||
{"ConfigMapRef: empty namespace and UID",
|
||||
&apiv1.NodeConfigSource{ConfigMapRef: &apiv1.ObjectReference{Namespace: "namespace", UID: "uid"}}, nil, "invalid ObjectReference"},
|
||||
// ConfigMapRef: valid reference
|
||||
{"ConfigMapRef: valid reference",
|
||||
&apiv1.NodeConfigSource{ConfigMapRef: &apiv1.ObjectReference{Name: "name", Namespace: "namespace", UID: "uid"}},
|
||||
&remoteConfigMap{&apiv1.NodeConfigSource{ConfigMapRef: &apiv1.ObjectReference{Name: "name", Namespace: "namespace", UID: "uid"}}}, ""},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
src, _, err := NewRemoteConfigSource(c.source)
|
||||
if utiltest.SkipRest(t, c.desc, err, c.err) {
|
||||
continue
|
||||
}
|
||||
// underlying object should match the object passed in
|
||||
if !apiequality.Semantic.DeepEqual(c.expect.object(), src.object()) {
|
||||
t.Errorf("case %q, expect RemoteConfigSource %s but got %s", c.desc, spew.Sdump(c.expect), spew.Sdump(src))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoteConfigMapUID(t *testing.T) {
|
||||
cases := []string{"", "uid", "376dfb73-56db-11e7-a01e-42010a800002"}
|
||||
for _, uidIn := range cases {
|
||||
cpt := &remoteConfigMap{
|
||||
&apiv1.NodeConfigSource{ConfigMapRef: &apiv1.ObjectReference{Name: "name", Namespace: "namespace", UID: types.UID(uidIn)}},
|
||||
}
|
||||
// UID method should return the correct value of the UID
|
||||
uidOut := cpt.UID()
|
||||
if uidIn != uidOut {
|
||||
t.Errorf("expect UID() to return %q, but got %q", uidIn, uidOut)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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",
|
||||
Namespace: "namespace",
|
||||
UID: "uid",
|
||||
}}
|
||||
client := fakeclient.NewSimpleClientset(cm)
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
source RemoteConfigSource
|
||||
expect Checkpoint
|
||||
err string
|
||||
}{
|
||||
|
||||
// object doesn't exist
|
||||
{"object doesn't exist",
|
||||
&remoteConfigMap{&apiv1.NodeConfigSource{ConfigMapRef: &apiv1.ObjectReference{Name: "bogus", Namespace: "namespace", UID: "bogus"}}},
|
||||
nil, "failed to download ConfigMap"},
|
||||
// UID of downloaded object doesn't match UID of referent found via namespace/name
|
||||
{"UID is incorrect for namespace/name",
|
||||
&remoteConfigMap{&apiv1.NodeConfigSource{ConfigMapRef: &apiv1.ObjectReference{Name: "name", Namespace: "namespace", UID: "bogus"}}},
|
||||
nil, "does not match UID"},
|
||||
// successful download
|
||||
{"object exists and reference is correct",
|
||||
&remoteConfigMap{&apiv1.NodeConfigSource{ConfigMapRef: &apiv1.ObjectReference{Name: "name", Namespace: "namespace", UID: "uid"}}},
|
||||
&configMapCheckpoint{kubeletCodecs, cm}, ""},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
cpt, _, err := c.source.Download(client)
|
||||
if utiltest.SkipRest(t, c.desc, err, c.err) {
|
||||
continue
|
||||
}
|
||||
// "downloaded" object should match the expected
|
||||
if !apiequality.Semantic.DeepEqual(c.expect.object(), cpt.object()) {
|
||||
t.Errorf("case %q, expect Checkpoint %s but got %s", c.desc, spew.Sdump(c.expect), spew.Sdump(cpt))
|
||||
}
|
||||
}
|
||||
}
|
56
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/checkpoint/store/BUILD
generated
vendored
Normal file
56
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/checkpoint/store/BUILD
generated
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"fsstore_test.go",
|
||||
"store_test.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/kubelet/kubeletconfig/checkpoint/store",
|
||||
library = ":go_default_library",
|
||||
deps = [
|
||||
"//pkg/kubelet/kubeletconfig/checkpoint:go_default_library",
|
||||
"//pkg/kubelet/kubeletconfig/util/files:go_default_library",
|
||||
"//pkg/kubelet/kubeletconfig/util/test:go_default_library",
|
||||
"//pkg/util/filesystem:go_default_library",
|
||||
"//vendor/github.com/davecgh/go-spew/spew:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"fakestore.go",
|
||||
"fsstore.go",
|
||||
"store.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/kubelet/kubeletconfig/checkpoint/store",
|
||||
deps = [
|
||||
"//pkg/kubelet/kubeletconfig/checkpoint:go_default_library",
|
||||
"//pkg/kubelet/kubeletconfig/util/files:go_default_library",
|
||||
"//pkg/kubelet/kubeletconfig/util/log:go_default_library",
|
||||
"//pkg/util/filesystem:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
76
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/checkpoint/store/fakestore.go
generated
vendored
Normal file
76
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/checkpoint/store/fakestore.go
generated
vendored
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
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 store
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubelet/kubeletconfig/checkpoint"
|
||||
)
|
||||
|
||||
// so far only implements Current(), LastKnownGood(), SetCurrent(), and SetLastKnownGood()
|
||||
type fakeStore struct {
|
||||
current checkpoint.RemoteConfigSource
|
||||
lastKnownGood checkpoint.RemoteConfigSource
|
||||
}
|
||||
|
||||
func (s *fakeStore) Initialize() error {
|
||||
return fmt.Errorf("Initialize method not supported")
|
||||
}
|
||||
|
||||
func (s *fakeStore) Exists(uid string) (bool, error) {
|
||||
return false, fmt.Errorf("Exists method not supported")
|
||||
}
|
||||
|
||||
func (s *fakeStore) Save(c checkpoint.Checkpoint) error {
|
||||
return fmt.Errorf("Save method not supported")
|
||||
}
|
||||
|
||||
func (s *fakeStore) Load(uid string) (checkpoint.Checkpoint, error) {
|
||||
return nil, fmt.Errorf("Load method not supported")
|
||||
}
|
||||
|
||||
func (s *fakeStore) CurrentModified() (time.Time, error) {
|
||||
return time.Time{}, fmt.Errorf("CurrentModified method not supported")
|
||||
}
|
||||
|
||||
func (s *fakeStore) Current() (checkpoint.RemoteConfigSource, error) {
|
||||
return s.current, nil
|
||||
}
|
||||
|
||||
func (s *fakeStore) LastKnownGood() (checkpoint.RemoteConfigSource, error) {
|
||||
return s.lastKnownGood, nil
|
||||
}
|
||||
|
||||
func (s *fakeStore) SetCurrent(source checkpoint.RemoteConfigSource) error {
|
||||
s.current = source
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *fakeStore) SetCurrentUpdated(source checkpoint.RemoteConfigSource) (bool, error) {
|
||||
return setCurrentUpdated(s, source)
|
||||
}
|
||||
|
||||
func (s *fakeStore) SetLastKnownGood(source checkpoint.RemoteConfigSource) error {
|
||||
s.lastKnownGood = source
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *fakeStore) Reset() (bool, error) {
|
||||
return false, fmt.Errorf("Reset method not supported")
|
||||
}
|
157
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/checkpoint/store/fsstore.go
generated
vendored
Normal file
157
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/checkpoint/store/fsstore.go
generated
vendored
Normal file
@ -0,0 +1,157 @@
|
||||
/*
|
||||
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 store
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubelet/kubeletconfig/checkpoint"
|
||||
utilfiles "k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/files"
|
||||
utillog "k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/log"
|
||||
utilfs "k8s.io/kubernetes/pkg/util/filesystem"
|
||||
)
|
||||
|
||||
const (
|
||||
curFile = ".cur"
|
||||
lkgFile = ".lkg"
|
||||
)
|
||||
|
||||
// fsStore is for tracking checkpoints in the local filesystem, implements Store
|
||||
type fsStore struct {
|
||||
// fs is the filesystem to use for storage operations; can be mocked for testing
|
||||
fs utilfs.Filesystem
|
||||
// checkpointsDir is the absolute path to the storage directory for fsStore
|
||||
checkpointsDir string
|
||||
}
|
||||
|
||||
// NewFsStore returns a Store that saves its data in `checkpointsDir`
|
||||
func NewFsStore(fs utilfs.Filesystem, checkpointsDir string) Store {
|
||||
return &fsStore{
|
||||
fs: fs,
|
||||
checkpointsDir: checkpointsDir,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *fsStore) Initialize() error {
|
||||
utillog.Infof("initializing config checkpoints directory %q", s.checkpointsDir)
|
||||
if err := utilfiles.EnsureDir(s.fs, s.checkpointsDir); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := utilfiles.EnsureFile(s.fs, filepath.Join(s.checkpointsDir, curFile)); err != nil {
|
||||
return err
|
||||
}
|
||||
return utilfiles.EnsureFile(s.fs, filepath.Join(s.checkpointsDir, lkgFile))
|
||||
}
|
||||
|
||||
func (s *fsStore) Exists(uid string) (bool, error) {
|
||||
ok, err := utilfiles.FileExists(s.fs, filepath.Join(s.checkpointsDir, uid))
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to determine whether checkpoint %q exists, error: %v", uid, err)
|
||||
}
|
||||
return ok, nil
|
||||
}
|
||||
|
||||
func (s *fsStore) Save(c checkpoint.Checkpoint) error {
|
||||
// encode the checkpoint
|
||||
data, err := c.Encode()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// save the file
|
||||
return utilfiles.ReplaceFile(s.fs, filepath.Join(s.checkpointsDir, c.UID()), data)
|
||||
}
|
||||
|
||||
func (s *fsStore) Load(uid string) (checkpoint.Checkpoint, error) {
|
||||
filePath := filepath.Join(s.checkpointsDir, uid)
|
||||
utillog.Infof("loading configuration from %q", filePath)
|
||||
|
||||
// load the file
|
||||
data, err := s.fs.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read checkpoint file %q, error: %v", filePath, err)
|
||||
}
|
||||
|
||||
// decode it
|
||||
c, err := checkpoint.DecodeCheckpoint(data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode checkpoint file %q, error: %v", filePath, err)
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (s *fsStore) CurrentModified() (time.Time, error) {
|
||||
path := filepath.Join(s.checkpointsDir, curFile)
|
||||
info, err := s.fs.Stat(path)
|
||||
if err != nil {
|
||||
return time.Time{}, fmt.Errorf("failed to stat %q while checking modification time, error: %v", path, err)
|
||||
}
|
||||
return info.ModTime(), nil
|
||||
}
|
||||
|
||||
func (s *fsStore) Current() (checkpoint.RemoteConfigSource, error) {
|
||||
return s.sourceFromFile(curFile)
|
||||
}
|
||||
|
||||
func (s *fsStore) LastKnownGood() (checkpoint.RemoteConfigSource, error) {
|
||||
return s.sourceFromFile(lkgFile)
|
||||
}
|
||||
|
||||
func (s *fsStore) SetCurrent(source checkpoint.RemoteConfigSource) error {
|
||||
return s.setSourceFile(curFile, source)
|
||||
}
|
||||
|
||||
func (s *fsStore) SetCurrentUpdated(source checkpoint.RemoteConfigSource) (bool, error) {
|
||||
return setCurrentUpdated(s, source)
|
||||
}
|
||||
|
||||
func (s *fsStore) SetLastKnownGood(source checkpoint.RemoteConfigSource) error {
|
||||
return s.setSourceFile(lkgFile, source)
|
||||
}
|
||||
|
||||
func (s *fsStore) Reset() (bool, error) {
|
||||
return reset(s)
|
||||
}
|
||||
|
||||
// sourceFromFile returns the RemoteConfigSource stored in the file at `s.checkpointsDir/relPath`,
|
||||
// or nil if the file is empty
|
||||
func (s *fsStore) sourceFromFile(relPath string) (checkpoint.RemoteConfigSource, error) {
|
||||
path := filepath.Join(s.checkpointsDir, relPath)
|
||||
data, err := s.fs.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if len(data) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
return checkpoint.DecodeRemoteConfigSource(data)
|
||||
}
|
||||
|
||||
// set source file replaces the file at `s.checkpointsDir/relPath` with a file containing `source`
|
||||
func (s *fsStore) setSourceFile(relPath string, source checkpoint.RemoteConfigSource) error {
|
||||
path := filepath.Join(s.checkpointsDir, relPath)
|
||||
// if nil, reset the file
|
||||
if source == nil {
|
||||
return utilfiles.ReplaceFile(s.fs, path, []byte{})
|
||||
}
|
||||
// encode the source and save it to the file
|
||||
data, err := source.Encode()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return utilfiles.ReplaceFile(s.fs, path, data)
|
||||
}
|
628
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/checkpoint/store/fsstore_test.go
generated
vendored
Normal file
628
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/checkpoint/store/fsstore_test.go
generated
vendored
Normal file
@ -0,0 +1,628 @@
|
||||
/*
|
||||
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 store
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/kubernetes/pkg/kubelet/kubeletconfig/checkpoint"
|
||||
utilfiles "k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/files"
|
||||
utiltest "k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/test"
|
||||
utilfs "k8s.io/kubernetes/pkg/util/filesystem"
|
||||
)
|
||||
|
||||
const testCheckpointsDir = "/test-checkpoints-dir"
|
||||
|
||||
func newInitializedFakeFsStore() (*fsStore, error) {
|
||||
fs := utilfs.NewFakeFs()
|
||||
store := NewFsStore(fs, testCheckpointsDir)
|
||||
if err := store.Initialize(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return store.(*fsStore), nil
|
||||
}
|
||||
|
||||
func TestFsStoreInitialize(t *testing.T) {
|
||||
store, err := newInitializedFakeFsStore()
|
||||
if err != nil {
|
||||
t.Fatalf("fsStore.Initialize() failed with error: %v", err)
|
||||
}
|
||||
|
||||
// check that testCheckpointsDir exists
|
||||
_, err = store.fs.Stat(testCheckpointsDir)
|
||||
if err != nil {
|
||||
t.Fatalf("expect %q to exist, but stat failed with error: %v", testCheckpointsDir, err)
|
||||
}
|
||||
|
||||
// check that testCheckpointsDir contains the curFile
|
||||
curPath := filepath.Join(testCheckpointsDir, curFile)
|
||||
_, err = store.fs.Stat(curPath)
|
||||
if err != nil {
|
||||
t.Fatalf("expect %q to exist, but stat failed with error: %v", curPath, err)
|
||||
}
|
||||
|
||||
// check that testCheckpointsDir contains the lkgFile
|
||||
lkgPath := filepath.Join(testCheckpointsDir, lkgFile)
|
||||
_, err = store.fs.Stat(lkgPath)
|
||||
if err != nil {
|
||||
t.Fatalf("expect %q to exist, but stat failed with error: %v", lkgPath, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFsStoreExists(t *testing.T) {
|
||||
store, err := newInitializedFakeFsStore()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to construct a store, error: %v", err)
|
||||
}
|
||||
|
||||
// create a checkpoint file; this is enough for an exists check
|
||||
cpt, err := checkpoint.NewConfigMapCheckpoint(&apiv1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{UID: "uid"},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("could not construct checkpoint, error: %v", err)
|
||||
}
|
||||
saveTestCheckpointFile(t, store.fs, cpt)
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
uid string // the uid to test
|
||||
expect bool
|
||||
err string
|
||||
}{
|
||||
{"exists", "uid", true, ""},
|
||||
{"does not exist", "bogus-uid", false, ""},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
ok, err := store.Exists(c.uid)
|
||||
if utiltest.SkipRest(t, c.desc, err, c.err) {
|
||||
continue
|
||||
}
|
||||
if c.expect != ok {
|
||||
t.Errorf("case %q, expect %t but got %t", c.desc, c.expect, ok)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFsStoreSave(t *testing.T) {
|
||||
store, err := newInitializedFakeFsStore()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to construct a store, error: %v", err)
|
||||
}
|
||||
|
||||
cpt, err := checkpoint.NewConfigMapCheckpoint(&apiv1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{UID: "uid"},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("could not construct checkpoint, error: %v", err)
|
||||
}
|
||||
|
||||
// save the checkpoint
|
||||
err = store.Save(cpt)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to save checkpoint, error: %v", err)
|
||||
}
|
||||
|
||||
// expect the saved checkpoint file to match the encoding of the checkpoint
|
||||
data, err := cpt.Encode()
|
||||
if err != nil {
|
||||
t.Fatalf("unable to encode the checkpoint, error: %v", err)
|
||||
}
|
||||
expect := string(data)
|
||||
|
||||
data = readTestCheckpointFile(t, store.fs, cpt.UID())
|
||||
cptFile := string(data)
|
||||
|
||||
if expect != cptFile {
|
||||
t.Errorf("expect %q but got %q", expect, cptFile)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFsStoreLoad(t *testing.T) {
|
||||
store, err := newInitializedFakeFsStore()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to construct a store, error: %v", err)
|
||||
}
|
||||
|
||||
const uid = "uid"
|
||||
cpt, err := checkpoint.NewConfigMapCheckpoint(&apiv1.ConfigMap{ObjectMeta: metav1.ObjectMeta{UID: types.UID(uid)}})
|
||||
if err != nil {
|
||||
t.Fatalf("unable to construct checkpoint, error: %v", err)
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
loadUID string
|
||||
cpt checkpoint.Checkpoint
|
||||
err string
|
||||
}{
|
||||
{"checkpoint exists", uid, cpt, ""},
|
||||
{"checkpoint does not exist", "bogus-uid", nil, "failed to read"},
|
||||
}
|
||||
for _, c := range cases {
|
||||
if c.cpt != nil {
|
||||
saveTestCheckpointFile(t, store.fs, c.cpt)
|
||||
}
|
||||
cpt, err := store.Load(c.loadUID)
|
||||
if utiltest.SkipRest(t, c.desc, err, c.err) {
|
||||
continue
|
||||
}
|
||||
if !checkpoint.EqualCheckpoints(c.cpt, cpt) {
|
||||
t.Errorf("case %q, expect %q but got %q", c.desc, spew.Sdump(c.cpt), spew.Sdump(cpt))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFsStoreRoundTrip(t *testing.T) {
|
||||
store, err := newInitializedFakeFsStore()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to construct a store, error: %v", err)
|
||||
}
|
||||
const uid = "uid"
|
||||
cpt, err := checkpoint.NewConfigMapCheckpoint(&apiv1.ConfigMap{ObjectMeta: metav1.ObjectMeta{UID: types.UID(uid)}})
|
||||
if err != nil {
|
||||
t.Fatalf("unable to construct checkpoint, error: %v", err)
|
||||
}
|
||||
err = store.Save(cpt)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to save checkpoint, error: %v", err)
|
||||
}
|
||||
cptAfter, err := store.Load(uid)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to load checkpoint, error: %v", err)
|
||||
}
|
||||
if !checkpoint.EqualCheckpoints(cpt, cptAfter) {
|
||||
t.Errorf("expect %q but got %q", spew.Sdump(cpt), spew.Sdump(cptAfter))
|
||||
}
|
||||
}
|
||||
|
||||
func TestFsStoreCurrentModified(t *testing.T) {
|
||||
store, err := newInitializedFakeFsStore()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to construct a store, error: %v", err)
|
||||
}
|
||||
|
||||
// create an empty current file, this is good enough for testing
|
||||
saveTestSourceFile(t, store.fs, curFile, nil)
|
||||
|
||||
// set the timestamps to the current time, so we can compare to result of store.SetCurrentModified
|
||||
now := time.Now()
|
||||
err = store.fs.Chtimes(filepath.Join(testCheckpointsDir, curFile), now, now)
|
||||
if err != nil {
|
||||
t.Fatalf("could not change timestamps, error: %v", err)
|
||||
}
|
||||
|
||||
// for now we hope that the system won't truncate the time to a less precise unit,
|
||||
// if this test fails on certain systems that may be the reason.
|
||||
modTime, err := store.CurrentModified()
|
||||
if err != nil {
|
||||
t.Fatalf("unable to determine modification time of current config source, error: %v", err)
|
||||
}
|
||||
if !now.Equal(modTime) {
|
||||
t.Errorf("expect %q but got %q", now.Format(time.RFC3339), modTime.Format(time.RFC3339))
|
||||
}
|
||||
}
|
||||
|
||||
func TestFsStoreCurrent(t *testing.T) {
|
||||
store, err := newInitializedFakeFsStore()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to construct a store, error: %v", err)
|
||||
}
|
||||
|
||||
source, _, err := checkpoint.NewRemoteConfigSource(&apiv1.NodeConfigSource{
|
||||
ConfigMapRef: &apiv1.ObjectReference{Name: "name", Namespace: "namespace", UID: "uid"}})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
expect checkpoint.RemoteConfigSource
|
||||
err string
|
||||
}{
|
||||
{"default source", nil, ""},
|
||||
{"non-default source", source, ""},
|
||||
}
|
||||
for _, c := range cases {
|
||||
// save the last known good source
|
||||
saveTestSourceFile(t, store.fs, curFile, c.expect)
|
||||
|
||||
// load last-known-good and compare to expected result
|
||||
source, err := store.Current()
|
||||
if utiltest.SkipRest(t, c.desc, err, c.err) {
|
||||
continue
|
||||
}
|
||||
if !checkpoint.EqualRemoteConfigSources(c.expect, source) {
|
||||
t.Errorf("case %q, expect %q but got %q", spew.Sdump(c.expect), spew.Sdump(c.expect), spew.Sdump(source))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFsStoreLastKnownGood(t *testing.T) {
|
||||
store, err := newInitializedFakeFsStore()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to construct a store, error: %v", err)
|
||||
}
|
||||
|
||||
source, _, err := checkpoint.NewRemoteConfigSource(&apiv1.NodeConfigSource{
|
||||
ConfigMapRef: &apiv1.ObjectReference{Name: "name", Namespace: "namespace", UID: "uid"}})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
expect checkpoint.RemoteConfigSource
|
||||
err string
|
||||
}{
|
||||
{"default source", nil, ""},
|
||||
{"non-default source", source, ""},
|
||||
}
|
||||
for _, c := range cases {
|
||||
// save the last known good source
|
||||
saveTestSourceFile(t, store.fs, lkgFile, c.expect)
|
||||
|
||||
// load last-known-good and compare to expected result
|
||||
source, err := store.LastKnownGood()
|
||||
if utiltest.SkipRest(t, c.desc, err, c.err) {
|
||||
continue
|
||||
}
|
||||
if !checkpoint.EqualRemoteConfigSources(c.expect, source) {
|
||||
t.Errorf("case %q, expect %q but got %q", spew.Sdump(c.expect), spew.Sdump(c.expect), spew.Sdump(source))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFsStoreSetCurrent(t *testing.T) {
|
||||
store, err := newInitializedFakeFsStore()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to construct a store, error: %v", err)
|
||||
}
|
||||
|
||||
const uid = "uid"
|
||||
expect := fmt.Sprintf(`{"kind":"NodeConfigSource","apiVersion":"v1","configMapRef":{"namespace":"namespace","name":"name","uid":"%s"}}%s`, uid, "\n")
|
||||
source, _, err := checkpoint.NewRemoteConfigSource(&apiv1.NodeConfigSource{ConfigMapRef: &apiv1.ObjectReference{
|
||||
Name: "name", Namespace: "namespace", UID: types.UID(uid)}})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// save the current source
|
||||
if err := store.SetCurrent(source); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// check that the source saved as we would expect
|
||||
data := readTestSourceFile(t, store.fs, curFile)
|
||||
if expect != string(data) {
|
||||
t.Errorf("expect current source file to contain %q, but got %q", expect, string(data))
|
||||
}
|
||||
}
|
||||
|
||||
func TestFsStoreSetCurrentUpdated(t *testing.T) {
|
||||
store, err := newInitializedFakeFsStore()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to construct a store, error: %v", err)
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
current string
|
||||
newCurrent string
|
||||
expectUpdated bool
|
||||
err string
|
||||
}{
|
||||
{"", "", false, ""},
|
||||
{"uid", "", true, ""},
|
||||
{"", "uid", true, ""},
|
||||
{"uid", "uid", false, ""},
|
||||
{"uid", "other-uid", true, ""},
|
||||
{"other-uid", "uid", true, ""},
|
||||
{"other-uid", "other-uid", false, ""},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
// construct current source
|
||||
var source checkpoint.RemoteConfigSource
|
||||
expectSource := ""
|
||||
if len(c.current) > 0 {
|
||||
expectSource = fmt.Sprintf(`{"kind":"NodeConfigSource","apiVersion":"v1","configMapRef":{"namespace":"namespace","name":"name","uid":"%s"}}%s`, c.current, "\n")
|
||||
source, _, err = checkpoint.NewRemoteConfigSource(&apiv1.NodeConfigSource{ConfigMapRef: &apiv1.ObjectReference{
|
||||
Name: "name", Namespace: "namespace", UID: types.UID(c.current)}})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// construct new source
|
||||
var newSource checkpoint.RemoteConfigSource
|
||||
expectNewSource := ""
|
||||
if len(c.newCurrent) > 0 {
|
||||
expectNewSource = fmt.Sprintf(`{"kind":"NodeConfigSource","apiVersion":"v1","configMapRef":{"namespace":"namespace","name":"new-name","uid":"%s"}}%s`, c.newCurrent, "\n")
|
||||
newSource, _, err = checkpoint.NewRemoteConfigSource(&apiv1.NodeConfigSource{ConfigMapRef: &apiv1.ObjectReference{
|
||||
Name: "new-name", Namespace: "namespace", UID: types.UID(c.newCurrent)}})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// set the initial current
|
||||
if err := store.SetCurrent(source); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// update to the new current
|
||||
updated, err := store.SetCurrentUpdated(newSource)
|
||||
if utiltest.SkipRest(t, fmt.Sprintf("%q -> %q", c.current, c.newCurrent), err, c.err) {
|
||||
continue
|
||||
}
|
||||
|
||||
// check that SetCurrentUpdated correctly reports whether the current checkpoint changed
|
||||
if c.expectUpdated != updated {
|
||||
t.Errorf("case %q -> %q, expect %v but got %v", c.current, c.newCurrent, c.expectUpdated, updated)
|
||||
}
|
||||
|
||||
// check that curFile is saved by SetCurrentUpdated as we expect
|
||||
data := readTestSourceFile(t, store.fs, curFile)
|
||||
if c.current == c.newCurrent {
|
||||
// same UID should leave file unchanged
|
||||
if expectSource != string(data) {
|
||||
t.Errorf("case %q -> %q, expect current source file to contain %q, but got %q", c.current, c.newCurrent, expectSource, string(data))
|
||||
}
|
||||
} else if expectNewSource != string(data) {
|
||||
// otherwise expect the file to change
|
||||
t.Errorf("case %q -> %q, expect current source file to contain %q, but got %q", c.current, c.newCurrent, expectNewSource, string(data))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestFsStoreSetLastKnownGood(t *testing.T) {
|
||||
store, err := newInitializedFakeFsStore()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to construct a store, error: %v", err)
|
||||
}
|
||||
|
||||
const uid = "uid"
|
||||
expect := fmt.Sprintf(`{"kind":"NodeConfigSource","apiVersion":"v1","configMapRef":{"namespace":"namespace","name":"name","uid":"%s"}}%s`, uid, "\n")
|
||||
source, _, err := checkpoint.NewRemoteConfigSource(&apiv1.NodeConfigSource{ConfigMapRef: &apiv1.ObjectReference{
|
||||
Name: "name", Namespace: "namespace", UID: types.UID(uid)}})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// save the last known good source
|
||||
if err := store.SetLastKnownGood(source); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// check that the source saved as we would expect
|
||||
data := readTestSourceFile(t, store.fs, lkgFile)
|
||||
if expect != string(data) {
|
||||
t.Errorf("expect last-known-good source file to contain %q, but got %q", expect, string(data))
|
||||
}
|
||||
}
|
||||
|
||||
func TestFsStoreReset(t *testing.T) {
|
||||
store, err := newInitializedFakeFsStore()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to construct a store, error: %v", err)
|
||||
}
|
||||
|
||||
source, _, err := checkpoint.NewRemoteConfigSource(&apiv1.NodeConfigSource{ConfigMapRef: &apiv1.ObjectReference{Name: "name", Namespace: "namespace", UID: "uid"}})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
otherSource, _, err := checkpoint.NewRemoteConfigSource(&apiv1.NodeConfigSource{ConfigMapRef: &apiv1.ObjectReference{Name: "other-name", Namespace: "namespace", UID: "other-uid"}})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
cases := []struct {
|
||||
desc string
|
||||
current checkpoint.RemoteConfigSource
|
||||
lastKnownGood checkpoint.RemoteConfigSource
|
||||
updated bool
|
||||
}{
|
||||
{"nil -> nil", nil, nil, false},
|
||||
{"source -> nil", source, nil, true},
|
||||
{"nil -> source", nil, source, false},
|
||||
{"source -> source", source, source, true},
|
||||
{"source -> otherSource", source, otherSource, true},
|
||||
{"otherSource -> source", otherSource, source, true},
|
||||
}
|
||||
for _, c := range cases {
|
||||
// manually save the sources to their respective files
|
||||
saveTestSourceFile(t, store.fs, curFile, c.current)
|
||||
saveTestSourceFile(t, store.fs, lkgFile, c.lastKnownGood)
|
||||
|
||||
// reset
|
||||
updated, err := store.Reset()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// make sure the files were emptied
|
||||
if size := testSourceFileSize(t, store.fs, curFile); size > 0 {
|
||||
t.Errorf("case %q, expect source file %q to be empty but got %d bytes", c.desc, curFile, size)
|
||||
}
|
||||
if size := testSourceFileSize(t, store.fs, lkgFile); size > 0 {
|
||||
t.Errorf("case %q, expect source file %q to be empty but got %d bytes", c.desc, lkgFile, size)
|
||||
}
|
||||
|
||||
// make sure Current() and LastKnownGood() both return nil
|
||||
current, err := store.Current()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
lastKnownGood, err := store.LastKnownGood()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if current != nil || lastKnownGood != nil {
|
||||
t.Errorf("case %q, expect nil for current and last-known-good checkpoints, but still have %q and %q, respectively",
|
||||
c.desc, current, lastKnownGood)
|
||||
}
|
||||
if c.updated != updated {
|
||||
t.Errorf("case %q, expect reset to return %t, but got %t", c.desc, c.updated, updated)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFsStoreSourceFromFile(t *testing.T) {
|
||||
store, err := newInitializedFakeFsStore()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to construct a store, error: %v", err)
|
||||
}
|
||||
|
||||
source, _, err := checkpoint.NewRemoteConfigSource(&apiv1.NodeConfigSource{
|
||||
ConfigMapRef: &apiv1.ObjectReference{Name: "name", Namespace: "namespace", UID: "uid"}})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
desc string
|
||||
expect checkpoint.RemoteConfigSource
|
||||
err string
|
||||
}{
|
||||
{"default source", nil, ""},
|
||||
{"non-default source", source, ""},
|
||||
}
|
||||
|
||||
const name = "some-source-file"
|
||||
for _, c := range cases {
|
||||
saveTestSourceFile(t, store.fs, name, c.expect)
|
||||
source, err := store.sourceFromFile(name)
|
||||
if utiltest.SkipRest(t, c.desc, err, c.err) {
|
||||
continue
|
||||
}
|
||||
if !checkpoint.EqualRemoteConfigSources(c.expect, source) {
|
||||
t.Errorf("case %q, expect %q but got %q", spew.Sdump(c.expect), spew.Sdump(c.expect), spew.Sdump(source))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFsStoreSetSourceFile(t *testing.T) {
|
||||
store, err := newInitializedFakeFsStore()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to construct a store, error: %v", err)
|
||||
}
|
||||
|
||||
source, _, err := checkpoint.NewRemoteConfigSource(&apiv1.NodeConfigSource{ConfigMapRef: &apiv1.ObjectReference{Name: "name", Namespace: "namespace", UID: "uid"}})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
source checkpoint.RemoteConfigSource
|
||||
}{
|
||||
{nil},
|
||||
{source},
|
||||
}
|
||||
|
||||
const name = "some-source-file"
|
||||
for _, c := range cases {
|
||||
// set the source file
|
||||
err := store.setSourceFile(name, c.source)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to set source file, error: %v", err)
|
||||
}
|
||||
// read back the file
|
||||
data := readTestSourceFile(t, store.fs, name)
|
||||
str := string(data)
|
||||
|
||||
if c.source != nil {
|
||||
// expect the contents to match the encoding of the source
|
||||
data, err := c.source.Encode()
|
||||
expect := string(data)
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't encode source, error: %v", err)
|
||||
}
|
||||
if expect != str {
|
||||
t.Errorf("case %q, expect %q but got %q", spew.Sdump(c.source), expect, str)
|
||||
}
|
||||
} else {
|
||||
// expect empty file
|
||||
expect := ""
|
||||
if expect != str {
|
||||
t.Errorf("case %q, expect %q but got %q", spew.Sdump(c.source), expect, str)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func readTestCheckpointFile(t *testing.T, fs utilfs.Filesystem, uid string) []byte {
|
||||
data, err := fs.ReadFile(filepath.Join(testCheckpointsDir, uid))
|
||||
if err != nil {
|
||||
t.Fatalf("unable to read test checkpoint file, error: %v", err)
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
func saveTestCheckpointFile(t *testing.T, fs utilfs.Filesystem, cpt checkpoint.Checkpoint) {
|
||||
data, err := cpt.Encode()
|
||||
if err != nil {
|
||||
t.Fatalf("unable to encode test checkpoint, error: %v", err)
|
||||
}
|
||||
fmt.Println(cpt.UID())
|
||||
err = utilfiles.ReplaceFile(fs, filepath.Join(testCheckpointsDir, cpt.UID()), data)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to save test checkpoint file, error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func readTestSourceFile(t *testing.T, fs utilfs.Filesystem, relPath string) []byte {
|
||||
data, err := fs.ReadFile(filepath.Join(testCheckpointsDir, relPath))
|
||||
if err != nil {
|
||||
t.Fatalf("unable to read test source file, error: %v", err)
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
func saveTestSourceFile(t *testing.T, fs utilfs.Filesystem, relPath string, source checkpoint.RemoteConfigSource) {
|
||||
if source != nil {
|
||||
data, err := source.Encode()
|
||||
if err != nil {
|
||||
t.Fatalf("unable to save test source file, error: %v", err)
|
||||
}
|
||||
err = utilfiles.ReplaceFile(fs, filepath.Join(testCheckpointsDir, relPath), data)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to save test source file, error: %v", err)
|
||||
}
|
||||
} else {
|
||||
err := utilfiles.ReplaceFile(fs, filepath.Join(testCheckpointsDir, relPath), []byte{})
|
||||
if err != nil {
|
||||
t.Fatalf("unable to save test source file, error: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testSourceFileSize(t *testing.T, fs utilfs.Filesystem, relPath string) int64 {
|
||||
info, err := fs.Stat(filepath.Join(testCheckpointsDir, relPath))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
return info.Size()
|
||||
}
|
84
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/checkpoint/store/store.go
generated
vendored
Normal file
84
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/checkpoint/store/store.go
generated
vendored
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
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 store
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubelet/kubeletconfig/checkpoint"
|
||||
)
|
||||
|
||||
// Store saves checkpoints and information about which is the current and last-known-good checkpoint to a storage layer
|
||||
type Store interface {
|
||||
// Initialize sets up the storage layer
|
||||
Initialize() error
|
||||
// Exists returns true if a checkpoint with `uid` exists in the store, false otherwise
|
||||
Exists(uid string) (bool, error)
|
||||
// Save saves the checkpoint to the storage layer
|
||||
Save(c checkpoint.Checkpoint) error
|
||||
// Load loads the checkpoint with UID `uid` from the storage layer, or returns an error if the checkpoint does not exist
|
||||
Load(uid string) (checkpoint.Checkpoint, error)
|
||||
// CurrentModified returns the last time that the current UID was set
|
||||
CurrentModified() (time.Time, error)
|
||||
// Current returns the source that points to the current checkpoint, or nil if no current checkpoint is set
|
||||
Current() (checkpoint.RemoteConfigSource, error)
|
||||
// LastKnownGood returns the source that points to the last-known-good checkpoint, or nil if no last-known-good checkpoint is set
|
||||
LastKnownGood() (checkpoint.RemoteConfigSource, error)
|
||||
// SetCurrent saves the source that points to the current checkpoint, set to nil to unset
|
||||
SetCurrent(source checkpoint.RemoteConfigSource) error
|
||||
// SetCurrentUpdated is similar to SetCurrent, but also returns whether the current checkpoint changed as a result
|
||||
SetCurrentUpdated(source checkpoint.RemoteConfigSource) (bool, error)
|
||||
// SetLastKnownGood saves the source that points to the last-known-good checkpoint, set to nil to unset
|
||||
SetLastKnownGood(source checkpoint.RemoteConfigSource) error
|
||||
// Reset unsets the current and last-known-good UIDs and returns whether the current UID was unset as a result of the reset
|
||||
Reset() (bool, error)
|
||||
}
|
||||
|
||||
// reset is a helper for implementing Reset, which can be implemented in terms of Store methods
|
||||
func reset(s Store) (bool, error) {
|
||||
if err := s.SetLastKnownGood(nil); err != nil {
|
||||
return false, fmt.Errorf("failed to reset last-known-good UID in checkpoint store, error: %v", err)
|
||||
}
|
||||
updated, err := s.SetCurrentUpdated(nil)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to reset current UID in checkpoint store, error: %v", err)
|
||||
}
|
||||
return updated, nil
|
||||
}
|
||||
|
||||
// setCurrentUpdated is a helper for implementing SetCurrentUpdated, which can be implemented in terms of Store methods
|
||||
func setCurrentUpdated(s Store, source checkpoint.RemoteConfigSource) (bool, error) {
|
||||
cur, err := s.Current()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// if both are nil, no need to update
|
||||
if cur == nil && source == nil {
|
||||
return false, nil
|
||||
}
|
||||
// if UIDs match, no need to update
|
||||
if (source != nil && cur != nil) && cur.UID() == source.UID() {
|
||||
return false, nil
|
||||
}
|
||||
// update the source
|
||||
if err := s.SetCurrent(source); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
96
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/checkpoint/store/store_test.go
generated
vendored
Normal file
96
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/checkpoint/store/store_test.go
generated
vendored
Normal file
@ -0,0 +1,96 @@
|
||||
/*
|
||||
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 store
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
"k8s.io/kubernetes/pkg/kubelet/kubeletconfig/checkpoint"
|
||||
)
|
||||
|
||||
func TestReset(t *testing.T) {
|
||||
source, _, err := checkpoint.NewRemoteConfigSource(&apiv1.NodeConfigSource{ConfigMapRef: &apiv1.ObjectReference{Name: "name", Namespace: "namespace", UID: "uid"}})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
otherSource, _, err := checkpoint.NewRemoteConfigSource(&apiv1.NodeConfigSource{ConfigMapRef: &apiv1.ObjectReference{Name: "other-name", Namespace: "namespace", UID: "other-uid"}})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
cases := []struct {
|
||||
s *fakeStore
|
||||
updated bool
|
||||
}{
|
||||
{&fakeStore{current: nil, lastKnownGood: nil}, false},
|
||||
{&fakeStore{current: source, lastKnownGood: nil}, true},
|
||||
{&fakeStore{current: nil, lastKnownGood: source}, false},
|
||||
{&fakeStore{current: source, lastKnownGood: source}, true},
|
||||
{&fakeStore{current: source, lastKnownGood: otherSource}, true},
|
||||
{&fakeStore{current: otherSource, lastKnownGood: source}, true},
|
||||
}
|
||||
for _, c := range cases {
|
||||
updated, err := reset(c.s)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if c.s.current != nil || c.s.lastKnownGood != nil {
|
||||
t.Errorf("case %q, expect nil for current and last-known-good checkpoints, but still have %q and %q, respectively",
|
||||
spew.Sdump(c.s), c.s.current, c.s.lastKnownGood)
|
||||
}
|
||||
if c.updated != updated {
|
||||
t.Errorf("case %q, expect reset to return %t, but got %t", spew.Sdump(c.s), c.updated, updated)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetCurrentUpdated(t *testing.T) {
|
||||
source, _, err := checkpoint.NewRemoteConfigSource(&apiv1.NodeConfigSource{ConfigMapRef: &apiv1.ObjectReference{Name: "name", Namespace: "namespace", UID: "uid"}})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
otherSource, _, err := checkpoint.NewRemoteConfigSource(&apiv1.NodeConfigSource{ConfigMapRef: &apiv1.ObjectReference{Name: "other-name", Namespace: "namespace", UID: "other-uid"}})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
cases := []struct {
|
||||
s *fakeStore
|
||||
newCurrent checkpoint.RemoteConfigSource
|
||||
updated bool
|
||||
}{
|
||||
{&fakeStore{current: nil}, nil, false},
|
||||
{&fakeStore{current: nil}, source, true},
|
||||
{&fakeStore{current: source}, source, false},
|
||||
{&fakeStore{current: source}, nil, true},
|
||||
{&fakeStore{current: source}, otherSource, true},
|
||||
}
|
||||
for _, c := range cases {
|
||||
current := c.s.current
|
||||
updated, err := setCurrentUpdated(c.s, c.newCurrent)
|
||||
if err != nil {
|
||||
t.Fatalf("case %q -> %q, unexpected error: %v", current, c.newCurrent, err)
|
||||
}
|
||||
if c.newCurrent != c.s.current {
|
||||
t.Errorf("case %q -> %q, expect current UID to be %q, but got %q", current, c.newCurrent, c.newCurrent, c.s.current)
|
||||
}
|
||||
if c.updated != updated {
|
||||
t.Errorf("case %q -> %q, expect setCurrentUpdated to return %t, but got %t", current, c.newCurrent, c.updated, updated)
|
||||
}
|
||||
}
|
||||
}
|
49
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/configfiles/BUILD
generated
vendored
Normal file
49
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/configfiles/BUILD
generated
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["configfiles.go"],
|
||||
importpath = "k8s.io/kubernetes/pkg/kubelet/kubeletconfig/configfiles",
|
||||
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/util/filesystem:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["configfiles_test.go"],
|
||||
importpath = "k8s.io/kubernetes/pkg/kubelet/kubeletconfig/configfiles",
|
||||
library = ":go_default_library",
|
||||
deps = [
|
||||
"//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/files:go_default_library",
|
||||
"//pkg/kubelet/kubeletconfig/util/test:go_default_library",
|
||||
"//pkg/util/filesystem:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library",
|
||||
],
|
||||
)
|
94
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/configfiles/configfiles.go
generated
vendored
Normal file
94
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/configfiles/configfiles.go
generated
vendored
Normal file
@ -0,0 +1,94 @@
|
||||
/*
|
||||
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 configfiles
|
||||
|
||||
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/util/filesystem"
|
||||
)
|
||||
|
||||
const kubeletFile = "kubelet"
|
||||
|
||||
// Loader loads configuration from a storage layer
|
||||
type Loader interface {
|
||||
// Load loads and returns the KubeletConfiguration from the storage layer, or an error if a configuration could not be loaded
|
||||
Load() (*kubeletconfig.KubeletConfiguration, error)
|
||||
}
|
||||
|
||||
// fsLoader loads configuration from `configDir`
|
||||
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
|
||||
}
|
||||
|
||||
return &fsLoader{
|
||||
fs: fs,
|
||||
kubeletCodecs: kubeletCodecs,
|
||||
configDir: configDir,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (loader *fsLoader) Load() (*kubeletconfig.KubeletConfiguration, error) {
|
||||
// require the config be in a file called "kubelet"
|
||||
path := filepath.Join(loader.configDir, kubeletFile)
|
||||
data, err := loader.fs.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read init config file %q, error: %v", path, err)
|
||||
}
|
||||
|
||||
// no configuration is an error, some parameters are required
|
||||
if len(data) == 0 {
|
||||
return nil, fmt.Errorf("init config file %q was empty, but some parameters are required", path)
|
||||
}
|
||||
|
||||
kc, err := utilcodec.DecodeKubeletConfiguration(loader.kubeletCodecs, data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// make all paths absolute
|
||||
resolveRelativePaths(kubeletconfig.KubeletConfigurationPathRefs(kc), loader.configDir)
|
||||
return kc, nil
|
||||
}
|
||||
|
||||
// resolveRelativePaths makes relative paths absolute by resolving them against `root`
|
||||
func resolveRelativePaths(paths []*string, root string) {
|
||||
for _, path := range paths {
|
||||
// leave empty paths alone, "no path" is a valid input
|
||||
// do not attempt to resolve paths that are already absolute
|
||||
if len(*path) > 0 && !filepath.IsAbs(*path) {
|
||||
*path = filepath.Join(root, *path)
|
||||
}
|
||||
}
|
||||
}
|
213
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/configfiles/configfiles_test.go
generated
vendored
Normal file
213
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/configfiles/configfiles_test.go
generated
vendored
Normal file
@ -0,0 +1,213 @@
|
||||
/*
|
||||
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 configfiles
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||
"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"
|
||||
utilfiles "k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/files"
|
||||
utiltest "k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/test"
|
||||
utilfs "k8s.io/kubernetes/pkg/util/filesystem"
|
||||
)
|
||||
|
||||
const configDir = "/test-config-dir"
|
||||
const relativePath = "relative/path/test"
|
||||
|
||||
func TestLoad(t *testing.T) {
|
||||
cases := []struct {
|
||||
desc string
|
||||
file *string
|
||||
expect *kubeletconfig.KubeletConfiguration
|
||||
err string
|
||||
}{
|
||||
// missing file
|
||||
{
|
||||
"missing file",
|
||||
nil,
|
||||
nil,
|
||||
"failed to read",
|
||||
},
|
||||
// empty file
|
||||
{
|
||||
"empty file",
|
||||
newString(``),
|
||||
nil,
|
||||
"was empty",
|
||||
},
|
||||
// invalid format
|
||||
{
|
||||
"invalid yaml",
|
||||
newString(`*`),
|
||||
nil,
|
||||
"failed to decode",
|
||||
},
|
||||
{
|
||||
"invalid json",
|
||||
newString(`{*`),
|
||||
nil,
|
||||
"failed to decode",
|
||||
},
|
||||
// invalid object
|
||||
{
|
||||
"missing kind",
|
||||
newString(`{"apiVersion":"kubeletconfig/v1alpha1"}`),
|
||||
nil,
|
||||
"failed to decode",
|
||||
},
|
||||
{
|
||||
"missing version",
|
||||
newString(`{"kind":"KubeletConfiguration"}`),
|
||||
nil,
|
||||
"failed to decode",
|
||||
},
|
||||
{
|
||||
"unregistered kind",
|
||||
newString(`{"kind":"BogusKind","apiVersion":"kubeletconfig/v1alpha1"}`),
|
||||
nil,
|
||||
"failed to decode",
|
||||
},
|
||||
{
|
||||
"unregistered version",
|
||||
newString(`{"kind":"KubeletConfiguration","apiVersion":"bogusversion"}`),
|
||||
nil,
|
||||
"failed to decode",
|
||||
},
|
||||
|
||||
// empty object with correct kind and version should result in the defaults for that kind and version
|
||||
{
|
||||
"default from yaml",
|
||||
newString(`kind: KubeletConfiguration
|
||||
apiVersion: kubeletconfig/v1alpha1`),
|
||||
newConfig(t),
|
||||
"",
|
||||
},
|
||||
{
|
||||
"default from json",
|
||||
newString(`{"kind":"KubeletConfiguration","apiVersion":"kubeletconfig/v1alpha1"}`),
|
||||
newConfig(t),
|
||||
"",
|
||||
},
|
||||
|
||||
// relative path
|
||||
{
|
||||
"yaml, relative path is resolved",
|
||||
newString(fmt.Sprintf(`kind: KubeletConfiguration
|
||||
apiVersion: kubeletconfig/v1alpha1
|
||||
podManifestPath: %s`, relativePath)),
|
||||
func() *kubeletconfig.KubeletConfiguration {
|
||||
kc := newConfig(t)
|
||||
kc.PodManifestPath = filepath.Join(configDir, relativePath)
|
||||
return kc
|
||||
}(),
|
||||
"",
|
||||
},
|
||||
{
|
||||
"json, relative path is resolved",
|
||||
newString(fmt.Sprintf(`{"kind":"KubeletConfiguration","apiVersion":"kubeletconfig/v1alpha1","podManifestPath":"%s"}`, relativePath)),
|
||||
func() *kubeletconfig.KubeletConfiguration {
|
||||
kc := newConfig(t)
|
||||
kc.PodManifestPath = filepath.Join(configDir, relativePath)
|
||||
return kc
|
||||
}(),
|
||||
"",
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.desc, func(t *testing.T) {
|
||||
fs := utilfs.NewFakeFs()
|
||||
if c.file != nil {
|
||||
if err := addFile(fs, filepath.Join(configDir, kubeletFile), *c.file); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
loader, err := NewFsLoader(fs, configDir)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
kc, err := loader.Load()
|
||||
if utiltest.SkipRest(t, c.desc, err, c.err) {
|
||||
return
|
||||
}
|
||||
if !apiequality.Semantic.DeepEqual(c.expect, kc) {
|
||||
t.Fatalf("expect %#v but got %#v", *c.expect, *kc)
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveRelativePaths(t *testing.T) {
|
||||
absolutePath := filepath.Join(configDir, "absolute")
|
||||
cases := []struct {
|
||||
desc string
|
||||
path string
|
||||
expect string
|
||||
}{
|
||||
{"empty path", "", ""},
|
||||
{"absolute path", absolutePath, absolutePath},
|
||||
{"relative path", relativePath, filepath.Join(configDir, relativePath)},
|
||||
}
|
||||
|
||||
paths := kubeletconfig.KubeletConfigurationPathRefs(newConfig(t))
|
||||
if len(paths) == 0 {
|
||||
t.Fatalf("requires at least one path field to exist in the KubeletConfiguration type")
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.desc, func(t *testing.T) {
|
||||
// set the path, resolve it, and check if it resolved as we would expect
|
||||
*(paths[0]) = c.path
|
||||
resolveRelativePaths(paths, configDir)
|
||||
if *(paths[0]) != c.expect {
|
||||
t.Fatalf("expect %s but got %s", c.expect, *(paths[0]))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func newString(s string) *string {
|
||||
return &s
|
||||
}
|
||||
|
||||
func addFile(fs utilfs.Filesystem, path string, file string) error {
|
||||
if err := utilfiles.EnsureDir(fs, filepath.Dir(path)); err != nil {
|
||||
return err
|
||||
}
|
||||
return utilfiles.ReplaceFile(fs, path, []byte(file))
|
||||
}
|
||||
|
||||
func newConfig(t *testing.T) *kubeletconfig.KubeletConfiguration {
|
||||
kubeletScheme, _, err := kubeletscheme.NewSchemeAndCodecs()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
// get the built-in default configuration
|
||||
external := &kubeletconfigv1alpha1.KubeletConfiguration{}
|
||||
kubeletScheme.Default(external)
|
||||
kc := &kubeletconfig.KubeletConfiguration{}
|
||||
err = kubeletScheme.Convert(external, kc, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
return kc
|
||||
}
|
183
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/configsync.go
generated
vendored
Normal file
183
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/configsync.go
generated
vendored
Normal file
@ -0,0 +1,183 @@
|
||||
/*
|
||||
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 kubeletconfig
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
"k8s.io/kubernetes/pkg/kubelet/kubeletconfig/checkpoint"
|
||||
"k8s.io/kubernetes/pkg/kubelet/kubeletconfig/status"
|
||||
utillog "k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/log"
|
||||
)
|
||||
|
||||
// pokeConfiSourceWorker tells the worker thread that syncs config sources that work needs to be done
|
||||
func (cc *Controller) pokeConfigSourceWorker() {
|
||||
select {
|
||||
case cc.pendingConfigSource <- true:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
// syncConfigSource checks if work needs to be done to use a new configuration, and does that work if necessary
|
||||
func (cc *Controller) syncConfigSource(client clientset.Interface, nodeName string) {
|
||||
select {
|
||||
case <-cc.pendingConfigSource:
|
||||
default:
|
||||
// no work to be done, return
|
||||
return
|
||||
}
|
||||
|
||||
// if the sync fails, we want to retry
|
||||
var syncerr error
|
||||
defer func() {
|
||||
if syncerr != nil {
|
||||
utillog.Errorf(syncerr.Error())
|
||||
cc.pokeConfigSourceWorker()
|
||||
}
|
||||
}()
|
||||
|
||||
node, err := latestNode(cc.informer.GetStore(), nodeName)
|
||||
if err != nil {
|
||||
cc.configOK.SetFailSyncCondition(status.FailSyncReasonInformer)
|
||||
syncerr = fmt.Errorf("%s, error: %v", status.FailSyncReasonInformer, err)
|
||||
return
|
||||
}
|
||||
|
||||
// check the Node and download any new config
|
||||
if updated, reason, err := cc.doSyncConfigSource(client, node.Spec.ConfigSource); err != nil {
|
||||
cc.configOK.SetFailSyncCondition(reason)
|
||||
syncerr = fmt.Errorf("%s, error: %v", reason, err)
|
||||
return
|
||||
} else if updated {
|
||||
// TODO(mtaufen): Consider adding a "currently restarting kubelet" ConfigOK message for this case
|
||||
utillog.Infof("config updated, Kubelet will restart to begin using new config")
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// If we get here:
|
||||
// - there is no need to restart to update the current config
|
||||
// - there was no error trying to sync configuration
|
||||
// - if, previously, there was an error trying to sync configuration, we need to clear that error from the condition
|
||||
cc.configOK.ClearFailSyncCondition()
|
||||
}
|
||||
|
||||
// doSyncConfigSource checkpoints and sets the store's current config to the new config or resets config,
|
||||
// depending on the `source`, and returns whether the current config in the checkpoint store was updated as a result
|
||||
func (cc *Controller) doSyncConfigSource(client clientset.Interface, source *apiv1.NodeConfigSource) (bool, string, error) {
|
||||
if source == nil {
|
||||
utillog.Infof("Node.Spec.ConfigSource is empty, will reset current and last-known-good to defaults")
|
||||
updated, reason, err := cc.resetConfig()
|
||||
if err != nil {
|
||||
return false, reason, err
|
||||
}
|
||||
return updated, "", nil
|
||||
}
|
||||
|
||||
// if the NodeConfigSource is non-nil, download the config
|
||||
utillog.Infof("Node.Spec.ConfigSource is non-empty, will checkpoint source and update config if necessary")
|
||||
remote, reason, err := checkpoint.NewRemoteConfigSource(source)
|
||||
if err != nil {
|
||||
return false, reason, err
|
||||
}
|
||||
reason, err = cc.checkpointConfigSource(client, remote)
|
||||
if err != nil {
|
||||
return false, reason, err
|
||||
}
|
||||
updated, reason, err := cc.setCurrentConfig(remote)
|
||||
if err != nil {
|
||||
return false, reason, err
|
||||
}
|
||||
return updated, "", nil
|
||||
}
|
||||
|
||||
// checkpointConfigSource downloads and checkpoints the object referred to by `source` if the checkpoint does not already exist,
|
||||
// if a failure occurs, returns a sanitized failure reason and an error
|
||||
func (cc *Controller) checkpointConfigSource(client clientset.Interface, source checkpoint.RemoteConfigSource) (string, error) {
|
||||
uid := source.UID()
|
||||
|
||||
// if the checkpoint already exists, skip downloading
|
||||
if ok, err := cc.checkpointStore.Exists(uid); err != nil {
|
||||
reason := fmt.Sprintf(status.FailSyncReasonCheckpointExistenceFmt, uid)
|
||||
return reason, fmt.Errorf("%s, error: %v", reason, err)
|
||||
} else if ok {
|
||||
utillog.Infof("checkpoint already exists for object with UID %q, skipping download", uid)
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// download
|
||||
checkpoint, reason, err := source.Download(client)
|
||||
if err != nil {
|
||||
return reason, fmt.Errorf("%s, error: %v", reason, err)
|
||||
}
|
||||
|
||||
// save
|
||||
err = cc.checkpointStore.Save(checkpoint)
|
||||
if err != nil {
|
||||
reason := fmt.Sprintf(status.FailSyncReasonSaveCheckpointFmt, checkpoint.UID())
|
||||
return reason, fmt.Errorf("%s, error: %v", reason, err)
|
||||
}
|
||||
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// setCurrentConfig updates UID of the current checkpoint in the checkpoint store to `uid` and returns whether the
|
||||
// current UID changed as a result, or a sanitized failure reason and an error.
|
||||
func (cc *Controller) setCurrentConfig(source checkpoint.RemoteConfigSource) (bool, string, error) {
|
||||
updated, err := cc.checkpointStore.SetCurrentUpdated(source)
|
||||
if err != nil {
|
||||
if source == nil {
|
||||
return false, status.FailSyncReasonSetCurrentDefault, err
|
||||
}
|
||||
return false, fmt.Sprintf(status.FailSyncReasonSetCurrentUIDFmt, source.UID()), err
|
||||
}
|
||||
return updated, "", nil
|
||||
}
|
||||
|
||||
// resetConfig resets the current and last-known-good checkpoints in the checkpoint store to their default values and
|
||||
// returns whether the current checkpoint changed as a result, or a sanitized failure reason and an error.
|
||||
func (cc *Controller) resetConfig() (bool, string, error) {
|
||||
updated, err := cc.checkpointStore.Reset()
|
||||
if err != nil {
|
||||
return false, status.FailSyncReasonReset, err
|
||||
}
|
||||
return updated, "", nil
|
||||
}
|
||||
|
||||
// latestNode returns the most recent Node with `nodeName` from `store`
|
||||
func latestNode(store cache.Store, nodeName string) (*apiv1.Node, error) {
|
||||
obj, ok, err := store.GetByKey(nodeName)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("failed to retrieve Node %q from informer's store, error: %v", nodeName, err)
|
||||
utillog.Errorf(err.Error())
|
||||
return nil, err
|
||||
} else if !ok {
|
||||
err := fmt.Errorf("Node %q does not exist in the informer's store, can't sync config source", nodeName)
|
||||
utillog.Errorf(err.Error())
|
||||
return nil, err
|
||||
}
|
||||
node, ok := obj.(*apiv1.Node)
|
||||
if !ok {
|
||||
err := fmt.Errorf("failed to cast object from informer's store to Node, can't sync config source for Node %q", nodeName)
|
||||
utillog.Errorf(err.Error())
|
||||
return nil, err
|
||||
}
|
||||
return node, nil
|
||||
}
|
290
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/controller.go
generated
vendored
Normal file
290
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/controller.go
generated
vendored
Normal file
@ -0,0 +1,290 @@
|
||||
/*
|
||||
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 kubeletconfig
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
"k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig"
|
||||
"k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig/validation"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubelet/kubeletconfig/checkpoint/store"
|
||||
"k8s.io/kubernetes/pkg/kubelet/kubeletconfig/configfiles"
|
||||
"k8s.io/kubernetes/pkg/kubelet/kubeletconfig/status"
|
||||
utillog "k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/log"
|
||||
utilpanic "k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/panic"
|
||||
utilfs "k8s.io/kubernetes/pkg/util/filesystem"
|
||||
)
|
||||
|
||||
const (
|
||||
checkpointsDir = "checkpoints"
|
||||
initConfigDir = "init"
|
||||
)
|
||||
|
||||
// Controller is the controller which, among other things:
|
||||
// - loads configuration from disk
|
||||
// - checkpoints configuration to disk
|
||||
// - downloads new configuration from the API server
|
||||
// - validates configuration
|
||||
// - 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/node/dynamic-kubelet-configuration.md
|
||||
type Controller struct {
|
||||
// dynamicConfig, if true, indicates that we should sync config from the API server
|
||||
dynamicConfig bool
|
||||
|
||||
// defaultConfig is the configuration to use if no initConfig is provided
|
||||
defaultConfig *kubeletconfig.KubeletConfiguration
|
||||
|
||||
// initConfig is the unmarshaled init config, this will be loaded by the Controller if an initConfigDir is provided
|
||||
initConfig *kubeletconfig.KubeletConfiguration
|
||||
|
||||
// initLoader is for loading the Kubelet's init configuration files from disk
|
||||
initLoader configfiles.Loader
|
||||
|
||||
// pendingConfigSource; write to this channel to indicate that the config source needs to be synced from the API server
|
||||
pendingConfigSource chan bool
|
||||
|
||||
// configOK manages the ConfigOK condition that is reported in Node.Status.Conditions
|
||||
configOK status.ConfigOKCondition
|
||||
|
||||
// informer is the informer that watches the Node object
|
||||
informer cache.SharedInformer
|
||||
|
||||
// checkpointStore persists config source checkpoints to a storage layer
|
||||
checkpointStore store.Store
|
||||
}
|
||||
|
||||
// NewController constructs a new Controller object and returns it. Directory paths must be absolute.
|
||||
// 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(defaultConfig *kubeletconfig.KubeletConfiguration,
|
||||
initConfigDir string,
|
||||
dynamicConfigDir string) (*Controller, error) {
|
||||
var err error
|
||||
|
||||
fs := utilfs.DefaultFs{}
|
||||
|
||||
var initLoader configfiles.Loader
|
||||
if len(initConfigDir) > 0 {
|
||||
initLoader, err = configfiles.NewFsLoader(fs, initConfigDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
dynamicConfig := false
|
||||
if len(dynamicConfigDir) > 0 {
|
||||
dynamicConfig = true
|
||||
}
|
||||
|
||||
return &Controller{
|
||||
dynamicConfig: dynamicConfig,
|
||||
defaultConfig: defaultConfig,
|
||||
// channels must have capacity at least 1, since we signal with non-blocking writes
|
||||
pendingConfigSource: make(chan bool, 1),
|
||||
configOK: status.NewConfigOKCondition(),
|
||||
checkpointStore: store.NewFsStore(fs, filepath.Join(dynamicConfigDir, checkpointsDir)),
|
||||
initLoader: initLoader,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Bootstrap attempts to return a valid KubeletConfiguration based on the configuration of the Controller,
|
||||
// or returns an error if no valid configuration could be produced. Bootstrap should be called synchronously before StartSync.
|
||||
func (cc *Controller) Bootstrap() (*kubeletconfig.KubeletConfiguration, error) {
|
||||
utillog.Infof("starting controller")
|
||||
|
||||
// ALWAYS validate the local (default and init) configs. This makes incorrectly provisioned nodes an error.
|
||||
// These must be valid because they are the foundational last-known-good configs.
|
||||
utillog.Infof("validating combination of defaults and flags")
|
||||
if err := validation.ValidateKubeletConfiguration(cc.defaultConfig); err != nil {
|
||||
return nil, fmt.Errorf("combination of defaults and flags failed validation, error: %v", err)
|
||||
}
|
||||
// only attempt to load and validate the init config if the user provided a path
|
||||
if cc.initLoader != nil {
|
||||
utillog.Infof("loading init config")
|
||||
kc, err := cc.initLoader.Load()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// validate the init config
|
||||
utillog.Infof("validating init config")
|
||||
if err := validation.ValidateKubeletConfiguration(kc); err != nil {
|
||||
return nil, fmt.Errorf("failed to validate the init config, error: %v", err)
|
||||
}
|
||||
cc.initConfig = kc
|
||||
}
|
||||
// Assert: the default and init configs are both valid
|
||||
|
||||
// if dynamic config is disabled, skip trying to load any checkpoints because they won't exist
|
||||
if !cc.dynamicConfig {
|
||||
return cc.localConfig(), nil
|
||||
}
|
||||
|
||||
// assert: now we know that a dynamicConfigDir was provided, and we can rely on that existing
|
||||
|
||||
// make sure the filesystem is set up properly
|
||||
// TODO(mtaufen): rename this to initializeDynamicConfigDir
|
||||
if err := cc.initialize(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// determine UID of the current config source
|
||||
curUID := ""
|
||||
if curSource, err := cc.checkpointStore.Current(); err != nil {
|
||||
return nil, err
|
||||
} else if curSource != nil {
|
||||
curUID = curSource.UID()
|
||||
}
|
||||
|
||||
// if curUID indicates the local config should be used, return the correct one of those
|
||||
if len(curUID) == 0 {
|
||||
return cc.localConfig(), nil
|
||||
} // Assert: we will not use the local configurations, unless we roll back to lkg; curUID is non-empty
|
||||
|
||||
// TODO(mtaufen): consider re-verifying integrity and re-attempting download when a load/verify/parse/validate
|
||||
// error happens outside trial period, we already made it past the trial so it's probably filesystem corruption
|
||||
// or something else scary (unless someone is using a 0-length trial period)
|
||||
|
||||
// load the current config
|
||||
checkpoint, err := cc.checkpointStore.Load(curUID)
|
||||
if err != nil {
|
||||
// TODO(mtaufen): rollback for now, but this could reasonably be handled by re-attempting a download,
|
||||
// it probably indicates some sort of corruption
|
||||
return cc.lkgRollback(fmt.Sprintf(status.CurFailLoadReasonFmt, curUID), fmt.Sprintf("error: %v", err))
|
||||
}
|
||||
|
||||
// parse the checkpoint into a KubeletConfiguration
|
||||
cur, err := checkpoint.Parse()
|
||||
if err != nil {
|
||||
return cc.lkgRollback(fmt.Sprintf(status.CurFailParseReasonFmt, curUID), fmt.Sprintf("error: %v", err))
|
||||
}
|
||||
|
||||
// validate current config
|
||||
if err := validation.ValidateKubeletConfiguration(cur); err != nil {
|
||||
return cc.lkgRollback(fmt.Sprintf(status.CurFailValidateReasonFmt, curUID), fmt.Sprintf("error: %v", err))
|
||||
}
|
||||
|
||||
// when the trial period is over, the current config becomes the last-known-good
|
||||
if trial, err := cc.inTrial(cur.ConfigTrialDuration.Duration); err != nil {
|
||||
return nil, err
|
||||
} else if !trial {
|
||||
if err := cc.graduateCurrentToLastKnownGood(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// update the status to note that we will use the current config
|
||||
cc.configOK.Set(fmt.Sprintf(status.CurRemoteMessageFmt, curUID), status.CurRemoteOKReason, apiv1.ConditionTrue)
|
||||
return cur, nil
|
||||
}
|
||||
|
||||
// StartSync launches the controller's sync loops if `client` is non-nil and `nodeName` is non-empty.
|
||||
// It will always start the Node condition reporting loop, and will also start the dynamic conifg sync loops
|
||||
// if dynamic config is enabled on the controller. If `nodeName` is empty but `client` is non-nil, an error is logged.
|
||||
func (cc *Controller) StartSync(client clientset.Interface, nodeName string) {
|
||||
if client == nil {
|
||||
utillog.Infof("nil client, will not start sync loops")
|
||||
return
|
||||
} else if len(nodeName) == 0 {
|
||||
utillog.Errorf("cannot start sync loops with empty nodeName")
|
||||
return
|
||||
}
|
||||
|
||||
// start the ConfigOK condition sync loop
|
||||
go utilpanic.HandlePanic(func() {
|
||||
utillog.Infof("starting ConfigOK condition sync loop")
|
||||
wait.JitterUntil(func() {
|
||||
cc.configOK.Sync(client, nodeName)
|
||||
}, 10*time.Second, 0.2, true, wait.NeverStop)
|
||||
})()
|
||||
|
||||
// only sync to new, remotely provided configurations if dynamic config was enabled
|
||||
if cc.dynamicConfig {
|
||||
cc.informer = newSharedNodeInformer(client, nodeName,
|
||||
cc.onAddNodeEvent, cc.onUpdateNodeEvent, cc.onDeleteNodeEvent)
|
||||
// start the informer loop
|
||||
// Rather than use utilruntime.HandleCrash, which doesn't actually crash in the Kubelet,
|
||||
// we use HandlePanic to manually call the panic handlers and then crash.
|
||||
// We have a better chance of recovering normal operation if we just restart the Kubelet in the event
|
||||
// of a Go runtime error.
|
||||
go utilpanic.HandlePanic(func() {
|
||||
utillog.Infof("starting Node informer sync loop")
|
||||
cc.informer.Run(wait.NeverStop)
|
||||
})()
|
||||
|
||||
// start the config source sync loop
|
||||
go utilpanic.HandlePanic(func() {
|
||||
utillog.Infof("starting config source sync loop")
|
||||
wait.JitterUntil(func() {
|
||||
cc.syncConfigSource(client, nodeName)
|
||||
}, 10*time.Second, 0.2, true, wait.NeverStop)
|
||||
})()
|
||||
} else {
|
||||
utillog.Infof("dynamic config not enabled, will not sync to remote config")
|
||||
}
|
||||
}
|
||||
|
||||
// initialize makes sure that the storage layers for various controller components are set up correctly
|
||||
func (cc *Controller) initialize() error {
|
||||
utillog.Infof("ensuring filesystem is set up correctly")
|
||||
// initialize local checkpoint storage location
|
||||
return cc.checkpointStore.Initialize()
|
||||
}
|
||||
|
||||
// localConfig returns the initConfig if it is loaded, otherwise returns the defaultConfig.
|
||||
// It also sets the local configOK condition to match the returned config.
|
||||
func (cc *Controller) localConfig() *kubeletconfig.KubeletConfiguration {
|
||||
if cc.initConfig != nil {
|
||||
cc.configOK.Set(status.CurInitMessage, status.CurInitOKReason, apiv1.ConditionTrue)
|
||||
return cc.initConfig
|
||||
}
|
||||
cc.configOK.Set(status.CurDefaultMessage, status.CurDefaultOKReason, apiv1.ConditionTrue)
|
||||
return cc.defaultConfig
|
||||
}
|
||||
|
||||
// inTrial returns true if the time elapsed since the last modification of the current config does not exceed `trialDur`, false otherwise
|
||||
func (cc *Controller) inTrial(trialDur time.Duration) (bool, error) {
|
||||
now := time.Now()
|
||||
t, err := cc.checkpointStore.CurrentModified()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if now.Sub(t) <= trialDur {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// graduateCurrentToLastKnownGood sets the last-known-good UID on the checkpointStore
|
||||
// to the same value as the current UID maintained by the checkpointStore
|
||||
func (cc *Controller) graduateCurrentToLastKnownGood() error {
|
||||
curUID, err := cc.checkpointStore.Current()
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not graduate last-known-good config to current config, error: %v", err)
|
||||
}
|
||||
err = cc.checkpointStore.SetLastKnownGood(curUID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not graduate last-known-good config to current config, error: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
70
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/rollback.go
generated
vendored
Normal file
70
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/rollback.go
generated
vendored
Normal file
@ -0,0 +1,70 @@
|
||||
/*
|
||||
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 kubeletconfig
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
"k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig"
|
||||
"k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig/validation"
|
||||
"k8s.io/kubernetes/pkg/kubelet/kubeletconfig/status"
|
||||
utillog "k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/log"
|
||||
)
|
||||
|
||||
// lkgRollback returns a valid last-known-good configuration, and updates the `cc.configOK` condition
|
||||
// regarding the `reason` for the rollback, or returns an error if a valid last-known-good could not be produced
|
||||
func (cc *Controller) lkgRollback(reason, detail string) (*kubeletconfig.KubeletConfiguration, error) {
|
||||
utillog.Errorf(fmt.Sprintf("%s, %s", reason, detail))
|
||||
|
||||
lkgUID := ""
|
||||
if lkgSource, err := cc.checkpointStore.LastKnownGood(); err != nil {
|
||||
return nil, fmt.Errorf("unable to determine last-known-good config, error: %v", err)
|
||||
} else if lkgSource != nil {
|
||||
lkgUID = lkgSource.UID()
|
||||
}
|
||||
|
||||
// if lkgUID indicates the default should be used, return initConfig or defaultConfig
|
||||
if len(lkgUID) == 0 {
|
||||
if cc.initConfig != nil {
|
||||
cc.configOK.Set(status.LkgInitMessage, reason, apiv1.ConditionFalse)
|
||||
return cc.initConfig, nil
|
||||
}
|
||||
cc.configOK.Set(status.LkgDefaultMessage, reason, apiv1.ConditionFalse)
|
||||
return cc.defaultConfig, nil
|
||||
}
|
||||
|
||||
// load
|
||||
checkpoint, err := cc.checkpointStore.Load(lkgUID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s, error: %v", fmt.Sprintf(status.LkgFailLoadReasonFmt, lkgUID), err)
|
||||
}
|
||||
|
||||
// parse
|
||||
lkg, err := checkpoint.Parse()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s, error: %v", fmt.Sprintf(status.LkgFailParseReasonFmt, lkgUID), err)
|
||||
}
|
||||
|
||||
// validate
|
||||
if err := validation.ValidateKubeletConfiguration(lkg); err != nil {
|
||||
return nil, fmt.Errorf("%s, error: %v", fmt.Sprintf(status.LkgFailValidateReasonFmt, lkgUID), err)
|
||||
}
|
||||
|
||||
cc.configOK.Set(fmt.Sprintf(status.LkgRemoteMessageFmt, lkgUID), reason, apiv1.ConditionFalse)
|
||||
return lkg, nil
|
||||
}
|
36
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/status/BUILD
generated
vendored
Normal file
36
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/status/BUILD
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["status.go"],
|
||||
importpath = "k8s.io/kubernetes/pkg/kubelet/kubeletconfig/status",
|
||||
deps = [
|
||||
"//pkg/api/legacyscheme:go_default_library",
|
||||
"//pkg/apis/core:go_default_library",
|
||||
"//pkg/kubelet/kubeletconfig/util/equal: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/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/strategicpatch:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
323
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/status/status.go
generated
vendored
Normal file
323
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/status/status.go
generated
vendored
Normal file
@ -0,0 +1,323 @@
|
||||
/*
|
||||
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 status
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
kuberuntime "k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/strategicpatch"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
utilequal "k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/equal"
|
||||
utillog "k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/log"
|
||||
)
|
||||
|
||||
const (
|
||||
// CurDefaultMessage indicates the Kubelet is using it's current config, which is the default
|
||||
CurDefaultMessage = "using current (default)"
|
||||
// LkgDefaultMessage indicates the Kubelet is using it's last-known-good config, which is the default
|
||||
LkgDefaultMessage = "using last-known-good (default)"
|
||||
|
||||
// CurInitMessage indicates the Kubelet is using it's current config, which is from the init config files
|
||||
CurInitMessage = "using current (init)"
|
||||
// LkgInitMessage indicates the Kubelet is using it's last-known-good config, which is from the init config files
|
||||
LkgInitMessage = "using last-known-good (init)"
|
||||
|
||||
// CurRemoteMessageFmt indicates the Kubelet is usin it's current config, which is from an API source
|
||||
CurRemoteMessageFmt = "using current (UID: %q)"
|
||||
// LkgRemoteMessageFmt indicates the Kubelet is using it's last-known-good config, which is from an API source
|
||||
LkgRemoteMessageFmt = "using last-known-good (UID: %q)"
|
||||
|
||||
// CurDefaultOKReason indicates that no init config files were provided
|
||||
CurDefaultOKReason = "current is set to the local default, and no init config was provided"
|
||||
// CurInitOKReason indicates that init config files were provided
|
||||
CurInitOKReason = "current is set to the local default, and an init config was provided"
|
||||
// CurRemoteOKReason indicates that the config referenced by Node.ConfigSource is currently passing all checks
|
||||
CurRemoteOKReason = "passing all checks"
|
||||
|
||||
// CurFailLoadReasonFmt indicates that the Kubelet failed to load the current config checkpoint for an API source
|
||||
CurFailLoadReasonFmt = "failed to load current (UID: %q)"
|
||||
// CurFailParseReasonFmt indicates that the Kubelet failed to parse the current config checkpoint for an API source
|
||||
CurFailParseReasonFmt = "failed to parse current (UID: %q)"
|
||||
// CurFailValidateReasonFmt indicates that the Kubelet failed to validate the current config checkpoint for an API source
|
||||
CurFailValidateReasonFmt = "failed to validate current (UID: %q)"
|
||||
// CurFailCrashLoopReasonFmt indicates that the Kubelet experienced a crash loop while using the current config checkpoint for an API source
|
||||
CurFailCrashLoopReasonFmt = "current failed trial period due to crash loop (UID: %q)"
|
||||
|
||||
// LkgFail*ReasonFmt reasons are currently used to print errors in the Kubelet log, but do not appear in Node.Status.Conditions
|
||||
|
||||
// LkgFailLoadReasonFmt indicates that the Kubelet failed to load the last-known-good config checkpoint for an API source
|
||||
LkgFailLoadReasonFmt = "failed to load last-known-good (UID: %q)"
|
||||
// LkgFailParseReasonFmt indicates that the Kubelet failed to parse the last-known-good config checkpoint for an API source
|
||||
LkgFailParseReasonFmt = "failed to parse last-known-good (UID: %q)"
|
||||
// LkgFailValidateReasonFmt indicates that the Kubelet failed to validate the last-known-good config checkpoint for an API source
|
||||
LkgFailValidateReasonFmt = "failed to validate last-known-good (UID: %q)"
|
||||
|
||||
// FailSyncReasonFmt is used when the system couldn't sync the config, due to a malformed Node.Spec.ConfigSource, a download failure, etc.
|
||||
FailSyncReasonFmt = "failed to sync, reason: %s"
|
||||
// FailSyncReasonAllNilSubfields is used when no subfields are set
|
||||
FailSyncReasonAllNilSubfields = "invalid NodeConfigSource, exactly one subfield must be non-nil, but all were nil"
|
||||
// FailSyncReasonPartialObjectReference is used when some required subfields remain unset
|
||||
FailSyncReasonPartialObjectReference = "invalid ObjectReference, all of UID, Name, and Namespace must be specified"
|
||||
// FailSyncReasonUIDMismatchFmt is used when there is a UID mismatch between the referenced and downloaded ConfigMaps,
|
||||
// this can happen because objects must be downloaded by namespace/name, rather than by UID
|
||||
FailSyncReasonUIDMismatchFmt = "invalid ObjectReference, UID %q does not match UID of downloaded ConfigMap %q"
|
||||
// FailSyncReasonDownloadFmt is used when the download fails, e.g. due to network issues
|
||||
FailSyncReasonDownloadFmt = "failed to download ConfigMap with name %q from namespace %q"
|
||||
// FailSyncReasonInformer is used when the informer fails to report the Node object
|
||||
FailSyncReasonInformer = "failed to read Node from informer object cache"
|
||||
// FailSyncReasonReset is used when we can't reset the local configuration references, e.g. due to filesystem issues
|
||||
FailSyncReasonReset = "failed to reset to local (default or init) config"
|
||||
// FailSyncReasonCheckpointExistenceFmt is used when we can't determine if a checkpoint already exists, e.g. due to filesystem issues
|
||||
FailSyncReasonCheckpointExistenceFmt = "failed to determine whether object with UID %q was already checkpointed"
|
||||
// FailSyncReasonSaveCheckpointFmt is used when we can't save a checkpoint, e.g. due to filesystem issues
|
||||
FailSyncReasonSaveCheckpointFmt = "failed to save config checkpoint for object with UID %q"
|
||||
// FailSyncReasonSetCurrentDefault is used when we can't set the current config checkpoint to the local default, e.g. due to filesystem issues
|
||||
FailSyncReasonSetCurrentDefault = "failed to set current config checkpoint to default"
|
||||
// FailSyncReasonSetCurrentUIDFmt is used when we can't set the current config checkpoint to a checkpointed object, e.g. due to filesystem issues
|
||||
FailSyncReasonSetCurrentUIDFmt = "failed to set current config checkpoint to object with UID %q"
|
||||
|
||||
// EmptyMessage is a placeholder in the case that we accidentally set the condition's message to the empty string.
|
||||
// Doing so can result in a partial patch, and thus a confusing status; this makes it clear that the message was not provided.
|
||||
EmptyMessage = "unknown - message not provided"
|
||||
// EmptyReason is a placeholder in the case that we accidentally set the condition's reason to the empty string.
|
||||
// Doing so can result in a partial patch, and thus a confusing status; this makes it clear that the reason was not provided.
|
||||
EmptyReason = "unknown - reason not provided"
|
||||
)
|
||||
|
||||
// ConfigOKCondition represents a ConfigOK NodeCondition
|
||||
type ConfigOKCondition interface {
|
||||
// Set sets the Message, Reason, and Status of the condition
|
||||
Set(message, reason string, status apiv1.ConditionStatus)
|
||||
// SetFailSyncCondition sets the condition for when syncing Kubelet config fails
|
||||
SetFailSyncCondition(reason string)
|
||||
// ClearFailSyncCondition clears the overlay from SetFailSyncCondition
|
||||
ClearFailSyncCondition()
|
||||
// Sync patches the current condition into the Node identified by `nodeName`
|
||||
Sync(client clientset.Interface, nodeName string)
|
||||
}
|
||||
|
||||
// configOKCondition implements ConfigOKCondition
|
||||
type configOKCondition struct {
|
||||
// conditionMux is a mutex on the condition, alternate between setting and syncing the condition
|
||||
conditionMux sync.Mutex
|
||||
// condition is the current ConfigOK node condition, which will be reported in the Node.status.conditions
|
||||
condition *apiv1.NodeCondition
|
||||
// failedSyncReason is sent in place of the usual reason when the Kubelet is failing to sync the remote config
|
||||
failedSyncReason string
|
||||
// pendingCondition; write to this channel to indicate that ConfigOK needs to be synced to the API server
|
||||
pendingCondition chan bool
|
||||
}
|
||||
|
||||
// NewConfigOKCondition returns a new ConfigOKCondition
|
||||
func NewConfigOKCondition() ConfigOKCondition {
|
||||
return &configOKCondition{
|
||||
// channels must have capacity at least 1, since we signal with non-blocking writes
|
||||
pendingCondition: make(chan bool, 1),
|
||||
}
|
||||
}
|
||||
|
||||
// unsafeSet sets the current state of the condition
|
||||
// it does not grab the conditionMux lock, so you should generally use setConfigOK unless you need to grab the lock
|
||||
// at a higher level to synchronize additional operations
|
||||
func (c *configOKCondition) unsafeSet(message, reason string, status apiv1.ConditionStatus) {
|
||||
// We avoid an empty Message, Reason, or Status on the condition. Since we use Patch to update conditions, an empty
|
||||
// field might cause a value from a previous condition to leak through, which can be very confusing.
|
||||
if len(message) == 0 {
|
||||
message = EmptyMessage
|
||||
}
|
||||
if len(reason) == 0 {
|
||||
reason = EmptyReason
|
||||
}
|
||||
if len(string(status)) == 0 {
|
||||
status = apiv1.ConditionUnknown
|
||||
}
|
||||
|
||||
c.condition = &apiv1.NodeCondition{
|
||||
Message: message,
|
||||
Reason: reason,
|
||||
Status: status,
|
||||
Type: apiv1.NodeConfigOK,
|
||||
}
|
||||
|
||||
c.pokeSyncWorker()
|
||||
}
|
||||
|
||||
func (c *configOKCondition) Set(message, reason string, status apiv1.ConditionStatus) {
|
||||
c.conditionMux.Lock()
|
||||
defer c.conditionMux.Unlock()
|
||||
c.unsafeSet(message, reason, status)
|
||||
}
|
||||
|
||||
// SetFailSyncCondition updates the ConfigOK status to reflect that we failed to sync to the latest config,
|
||||
// e.g. due to a malformed Node.Spec.ConfigSource, a download failure, etc.
|
||||
func (c *configOKCondition) SetFailSyncCondition(reason string) {
|
||||
c.conditionMux.Lock()
|
||||
defer c.conditionMux.Unlock()
|
||||
// set the reason overlay and poke the sync worker to send the update
|
||||
c.failedSyncReason = fmt.Sprintf(FailSyncReasonFmt, reason)
|
||||
c.pokeSyncWorker()
|
||||
}
|
||||
|
||||
// ClearFailSyncCondition removes the "failed to sync" reason overlay
|
||||
func (c *configOKCondition) ClearFailSyncCondition() {
|
||||
c.conditionMux.Lock()
|
||||
defer c.conditionMux.Unlock()
|
||||
// clear the reason overlay and poke the sync worker to send the update
|
||||
c.failedSyncReason = ""
|
||||
c.pokeSyncWorker()
|
||||
}
|
||||
|
||||
// pokeSyncWorker notes that the ConfigOK condition needs to be synced to the API server
|
||||
func (c *configOKCondition) pokeSyncWorker() {
|
||||
select {
|
||||
case c.pendingCondition <- true:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
// Sync attempts to sync `c.condition` with the Node object for this Kubelet,
|
||||
// if syncing fails, an error is logged, and work is queued for retry.
|
||||
func (c *configOKCondition) Sync(client clientset.Interface, nodeName string) {
|
||||
select {
|
||||
case <-c.pendingCondition:
|
||||
default:
|
||||
// no work to be done, return
|
||||
return
|
||||
}
|
||||
|
||||
// grab the lock
|
||||
c.conditionMux.Lock()
|
||||
defer c.conditionMux.Unlock()
|
||||
|
||||
// if the sync fails, we want to retry
|
||||
var err error
|
||||
defer func() {
|
||||
if err != nil {
|
||||
utillog.Errorf(err.Error())
|
||||
c.pokeSyncWorker()
|
||||
}
|
||||
}()
|
||||
|
||||
if c.condition == nil {
|
||||
utillog.Infof("ConfigOK condition is nil, skipping ConfigOK sync")
|
||||
return
|
||||
}
|
||||
|
||||
// get the Node so we can check the current condition
|
||||
node, err := client.CoreV1().Nodes().Get(nodeName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
err = fmt.Errorf("could not get Node %q, will not sync ConfigOK condition, error: %v", nodeName, err)
|
||||
return
|
||||
}
|
||||
|
||||
// set timestamps
|
||||
syncTime := metav1.NewTime(time.Now())
|
||||
c.condition.LastHeartbeatTime = syncTime
|
||||
if remote := getConfigOK(node.Status.Conditions); remote == nil || !utilequal.ConfigOKEq(remote, c.condition) {
|
||||
// update transition time the first time we create the condition,
|
||||
// or if we are semantically changing the condition
|
||||
c.condition.LastTransitionTime = syncTime
|
||||
} else {
|
||||
// since the conditions are semantically equal, use lastTransitionTime from the condition currently on the Node
|
||||
// we need to do this because the field will always be represented in the patch generated below, and this copy
|
||||
// prevents nullifying the field during the patch operation
|
||||
c.condition.LastTransitionTime = remote.LastTransitionTime
|
||||
}
|
||||
|
||||
// overlay the failedSyncReason if necessary
|
||||
var condition *apiv1.NodeCondition
|
||||
if len(c.failedSyncReason) > 0 {
|
||||
// get a copy of the condition before we add the overlay
|
||||
condition = c.condition.DeepCopy()
|
||||
condition.Reason = c.failedSyncReason
|
||||
condition.Status = apiv1.ConditionFalse
|
||||
} else {
|
||||
condition = c.condition
|
||||
}
|
||||
|
||||
// generate the patch
|
||||
mediaType := "application/json"
|
||||
info, ok := kuberuntime.SerializerInfoForMediaType(legacyscheme.Codecs.SupportedMediaTypes(), mediaType)
|
||||
if !ok {
|
||||
err = fmt.Errorf("unsupported media type %q", mediaType)
|
||||
return
|
||||
}
|
||||
versions := legacyscheme.Registry.EnabledVersionsForGroup(api.GroupName)
|
||||
if len(versions) == 0 {
|
||||
err = fmt.Errorf("no enabled versions for group %q", api.GroupName)
|
||||
return
|
||||
}
|
||||
// the "best" version supposedly comes first in the list returned from apiv1.Registry.EnabledVersionsForGroup
|
||||
encoder := legacyscheme.Codecs.EncoderForVersion(info.Serializer, versions[0])
|
||||
|
||||
before, err := kuberuntime.Encode(encoder, node)
|
||||
if err != nil {
|
||||
err = fmt.Errorf(`failed to encode "before" node while generating patch, error: %v`, err)
|
||||
return
|
||||
}
|
||||
|
||||
patchConfigOK(node, condition)
|
||||
after, err := kuberuntime.Encode(encoder, node)
|
||||
if err != nil {
|
||||
err = fmt.Errorf(`failed to encode "after" node while generating patch, error: %v`, err)
|
||||
return
|
||||
}
|
||||
|
||||
patch, err := strategicpatch.CreateTwoWayMergePatch(before, after, apiv1.Node{})
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to generate patch for updating ConfigOK condition, error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// patch the remote Node object
|
||||
_, err = client.CoreV1().Nodes().PatchStatus(nodeName, patch)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("could not update ConfigOK condition, error: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// patchConfigOK replaces or adds the ConfigOK condition to the node
|
||||
func patchConfigOK(node *apiv1.Node, configOK *apiv1.NodeCondition) {
|
||||
for i := range node.Status.Conditions {
|
||||
if node.Status.Conditions[i].Type == apiv1.NodeConfigOK {
|
||||
// edit the condition
|
||||
node.Status.Conditions[i] = *configOK
|
||||
return
|
||||
}
|
||||
}
|
||||
// append the condition
|
||||
node.Status.Conditions = append(node.Status.Conditions, *configOK)
|
||||
}
|
||||
|
||||
// getConfigOK returns the first NodeCondition in `cs` with Type == apiv1.NodeConfigOK,
|
||||
// or if no such condition exists, returns nil.
|
||||
func getConfigOK(cs []apiv1.NodeCondition) *apiv1.NodeCondition {
|
||||
for i := range cs {
|
||||
if cs[i].Type == apiv1.NodeConfigOK {
|
||||
return &cs[i]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
32
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/codec/BUILD
generated
vendored
Normal file
32
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/codec/BUILD
generated
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["codec.go"],
|
||||
importpath = "k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/codec",
|
||||
deps = [
|
||||
"//pkg/api/legacyscheme:go_default_library",
|
||||
"//pkg/apis/core/install:go_default_library",
|
||||
"//pkg/kubelet/apis/kubeletconfig:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
65
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/codec/codec.go
generated
vendored
Normal file
65
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/codec/codec.go
generated
vendored
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
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 codec
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
// ensure the core apis are installed
|
||||
_ "k8s.io/kubernetes/pkg/apis/core/install"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
"k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig"
|
||||
)
|
||||
|
||||
// TODO(mtaufen): allow an encoder to be injected into checkpoint objects at creation time? (then we could ultimately instantiate only one encoder)
|
||||
|
||||
// NewJSONEncoder generates a new runtime.Encoder that encodes objects to JSON
|
||||
func NewJSONEncoder(groupName string) (runtime.Encoder, error) {
|
||||
// encode to json
|
||||
mediaType := "application/json"
|
||||
info, ok := runtime.SerializerInfoForMediaType(legacyscheme.Codecs.SupportedMediaTypes(), mediaType)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unsupported media type %q", mediaType)
|
||||
}
|
||||
|
||||
versions := legacyscheme.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 legacyscheme.Registry.EnabledVersionsForGroup
|
||||
return legacyscheme.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)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode, error: %v", err)
|
||||
}
|
||||
|
||||
internalKC, ok := obj.(*kubeletconfig.KubeletConfiguration)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("failed to cast object to KubeletConfiguration, unexpected type: %v", gvk)
|
||||
}
|
||||
|
||||
return internalKC, nil
|
||||
}
|
26
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/equal/BUILD
generated
vendored
Normal file
26
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/equal/BUILD
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["equal.go"],
|
||||
importpath = "k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/equal",
|
||||
deps = ["//vendor/k8s.io/api/core/v1:go_default_library"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
51
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/equal/equal.go
generated
vendored
Normal file
51
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/equal/equal.go
generated
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
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 equal
|
||||
|
||||
import apiv1 "k8s.io/api/core/v1"
|
||||
|
||||
// ConfigSourceEq returns true if the two config sources are semantically equivalent in the context of dynamic config
|
||||
func ConfigSourceEq(a, b *apiv1.NodeConfigSource) bool {
|
||||
if a == b {
|
||||
return true
|
||||
} else if a == nil || b == nil {
|
||||
// not equal, and one is nil
|
||||
return false
|
||||
}
|
||||
// check equality of config source subifelds
|
||||
if a.ConfigMapRef != b.ConfigMapRef {
|
||||
return ObjectRefEq(a.ConfigMapRef, b.ConfigMapRef)
|
||||
}
|
||||
// all internal subfields of the config soruce are equal
|
||||
return true
|
||||
}
|
||||
|
||||
// ObjectRefEq returns true if the two object references are semantically equivalent in the context of dynamic config
|
||||
func ObjectRefEq(a, b *apiv1.ObjectReference) bool {
|
||||
if a == b {
|
||||
return true
|
||||
} else if a == nil || b == nil {
|
||||
// not equal, and one is nil
|
||||
return false
|
||||
}
|
||||
return a.UID == b.UID && a.Namespace == b.Namespace && a.Name == b.Name
|
||||
}
|
||||
|
||||
// ConfigOKEq returns true if the two conditions are semantically equivalent in the context of dynamic config
|
||||
func ConfigOKEq(a, b *apiv1.NodeCondition) bool {
|
||||
return a.Message == b.Message && a.Reason == b.Reason && a.Status == b.Status
|
||||
}
|
26
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/files/BUILD
generated
vendored
Normal file
26
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/files/BUILD
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["files.go"],
|
||||
importpath = "k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/files",
|
||||
deps = ["//pkg/util/filesystem:go_default_library"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
139
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/files/files.go
generated
vendored
Normal file
139
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/files/files.go
generated
vendored
Normal file
@ -0,0 +1,139 @@
|
||||
/*
|
||||
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 files
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
utilfs "k8s.io/kubernetes/pkg/util/filesystem"
|
||||
)
|
||||
|
||||
const defaultPerm = 0666
|
||||
|
||||
// FileExists returns true if a regular file exists at `path`, false if `path` does not exist, otherwise an error
|
||||
func FileExists(fs utilfs.Filesystem, path string) (bool, error) {
|
||||
if info, err := fs.Stat(path); err == nil {
|
||||
if info.Mode().IsRegular() {
|
||||
return true, nil
|
||||
}
|
||||
return false, fmt.Errorf("expected regular file at %q, but mode is %q", path, info.Mode().String())
|
||||
} else if os.IsNotExist(err) {
|
||||
return false, nil
|
||||
} else {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
// EnsureFile ensures that a regular file exists at `path`, and if it must create the file any
|
||||
// necessary parent directories will also be created and the new file will be empty.
|
||||
func EnsureFile(fs utilfs.Filesystem, path string) error {
|
||||
// if file exists, don't change it, but do report any unexpected errors
|
||||
if ok, err := FileExists(fs, path); ok || err != nil {
|
||||
return err
|
||||
} // Assert: file does not exist
|
||||
|
||||
// create any necessary parents
|
||||
err := fs.MkdirAll(filepath.Dir(path), defaultPerm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// create the file
|
||||
file, err := fs.Create(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// close the file, since we don't intend to use it yet
|
||||
return file.Close()
|
||||
}
|
||||
|
||||
// WriteTmpFile creates a temporary file at `path`, writes `data` into it, and fsyncs the file
|
||||
func WriteTmpFile(fs utilfs.Filesystem, path string, data []byte) (tmpPath string, retErr error) {
|
||||
dir := filepath.Dir(path)
|
||||
prefix := filepath.Base(path)
|
||||
|
||||
// create the tmp file
|
||||
tmpFile, err := fs.TempFile(dir, prefix)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer func() {
|
||||
// close the file, return the close error only if there haven't been any other errors
|
||||
if err := tmpFile.Close(); retErr == nil {
|
||||
retErr = err
|
||||
}
|
||||
// if there was an error writing, syncing, or closing, delete the temporary file and return the error
|
||||
if retErr != nil {
|
||||
if err := fs.Remove(tmpPath); err != nil {
|
||||
retErr = fmt.Errorf("attempted to remove temporary file %q after error %v, but failed due to error: %v", path, retErr, err)
|
||||
}
|
||||
tmpPath = ""
|
||||
}
|
||||
}()
|
||||
|
||||
// Name() will be an absolute path when using utilfs.DefaultFS, because ioutil.TempFile passes
|
||||
// an absolute path to os.Open, and we ensure similar behavior in utilfs.FakeFS for testing.
|
||||
tmpPath = tmpFile.Name()
|
||||
|
||||
// write data
|
||||
if _, err := tmpFile.Write(data); err != nil {
|
||||
return tmpPath, err
|
||||
}
|
||||
// sync file, to ensure it's written in case a hard reset happens
|
||||
return tmpPath, tmpFile.Sync()
|
||||
}
|
||||
|
||||
// ReplaceFile replaces the contents of the file at `path` with `data` by writing to a tmp file in the same
|
||||
// dir as `path` and renaming the tmp file over `path`. The file does not have to exist to use ReplaceFile.
|
||||
// Note ReplaceFile calls fsync.
|
||||
func ReplaceFile(fs utilfs.Filesystem, path string, data []byte) error {
|
||||
// write data to a temporary file
|
||||
tmpPath, err := WriteTmpFile(fs, path, data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// rename over existing file
|
||||
return fs.Rename(tmpPath, path)
|
||||
}
|
||||
|
||||
// DirExists returns true if a directory exists at `path`, false if `path` does not exist, otherwise an error
|
||||
func DirExists(fs utilfs.Filesystem, path string) (bool, error) {
|
||||
if info, err := fs.Stat(path); err == nil {
|
||||
if info.IsDir() {
|
||||
return true, nil
|
||||
}
|
||||
return false, fmt.Errorf("expected dir at %q, but mode is is %q", path, info.Mode().String())
|
||||
} else if os.IsNotExist(err) {
|
||||
return false, nil
|
||||
} else {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
// EnsureDir ensures that a directory exists at `path`, and if it must create the directory any
|
||||
// necessary parent directories will also be created and the new directory will be empty.
|
||||
func EnsureDir(fs utilfs.Filesystem, path string) error {
|
||||
// if dir exists, don't change it, but do report any unexpected errors
|
||||
if ok, err := DirExists(fs, path); ok || err != nil {
|
||||
return err
|
||||
} // Assert: dir does not exist
|
||||
|
||||
// create the dir
|
||||
return fs.MkdirAll(path, defaultPerm)
|
||||
}
|
26
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/log/BUILD
generated
vendored
Normal file
26
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/log/BUILD
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["log.go"],
|
||||
importpath = "k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/log",
|
||||
deps = ["//vendor/github.com/golang/glog:go_default_library"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
49
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/log/log.go
generated
vendored
Normal file
49
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/log/log.go
generated
vendored
Normal file
@ -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 log
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
const logFmt = "kubelet config controller: %s"
|
||||
|
||||
// Errorf shim that inserts "kubelet config controller" at the beginning of the log message,
|
||||
// while still reporting the call site of the logging function.
|
||||
func Errorf(format string, args ...interface{}) {
|
||||
var s string
|
||||
if len(args) > 0 {
|
||||
s = fmt.Sprintf(format, args...)
|
||||
} else {
|
||||
s = format
|
||||
}
|
||||
glog.ErrorDepth(1, fmt.Sprintf(logFmt, s))
|
||||
}
|
||||
|
||||
// Infof shim that inserts "kubelet config controller" at the beginning of the log message,
|
||||
// while still reporting the call site of the logging function.
|
||||
func Infof(format string, args ...interface{}) {
|
||||
var s string
|
||||
if len(args) > 0 {
|
||||
s = fmt.Sprintf(format, args...)
|
||||
} else {
|
||||
s = format
|
||||
}
|
||||
glog.InfoDepth(1, fmt.Sprintf(logFmt, s))
|
||||
}
|
26
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/panic/BUILD
generated
vendored
Normal file
26
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/panic/BUILD
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["panic.go"],
|
||||
importpath = "k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/panic",
|
||||
deps = ["//vendor/k8s.io/apimachinery/pkg/util/runtime:go_default_library"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
36
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/panic/panic.go
generated
vendored
Normal file
36
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/panic/panic.go
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
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 panic
|
||||
|
||||
import utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
|
||||
// HandlePanic returns a function that wraps `fn` with the utilruntime.PanicHandlers, and continues
|
||||
// to bubble the panic after the PanicHandlers are called
|
||||
func HandlePanic(fn func()) func() {
|
||||
return func() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
for _, fn := range utilruntime.PanicHandlers {
|
||||
fn(r)
|
||||
}
|
||||
panic(r)
|
||||
}
|
||||
}()
|
||||
// call the function
|
||||
fn()
|
||||
}
|
||||
}
|
25
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/test/BUILD
generated
vendored
Normal file
25
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/test/BUILD
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["test.go"],
|
||||
importpath = "k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/test",
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
40
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/test/test.go
generated
vendored
Normal file
40
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/test/test.go
generated
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
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 test
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// SkipRest returns true if there was a non-nil error or if we expected an error that didn't happen,
|
||||
// and logs the appropriate error on the test object.
|
||||
// The return value indicates whether we should skip the rest of the test case due to the error result.
|
||||
func SkipRest(t *testing.T, desc string, err error, contains string) bool {
|
||||
if err != nil {
|
||||
if len(contains) == 0 {
|
||||
t.Errorf("case %q, expect nil error but got %q", desc, err.Error())
|
||||
} else if !strings.Contains(err.Error(), contains) {
|
||||
t.Errorf("case %q, expect error to contain %q but got %q", desc, contains, err.Error())
|
||||
}
|
||||
return true
|
||||
} else if len(contains) > 0 {
|
||||
t.Errorf("case %q, expect error to contain %q but got nil error", desc, contains)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
125
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/watch.go
generated
vendored
Normal file
125
vendor/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/watch.go
generated
vendored
Normal file
@ -0,0 +1,125 @@
|
||||
/*
|
||||
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 kubeletconfig
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
kuberuntime "k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
utilequal "k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/equal"
|
||||
utillog "k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/log"
|
||||
)
|
||||
|
||||
// newSharedNodeInformer returns a shared informer that uses `client` to watch the Node with
|
||||
// `nodeName` for changes and respond with `addFunc`, `updateFunc`, and `deleteFunc`.
|
||||
func newSharedNodeInformer(client clientset.Interface, nodeName string,
|
||||
addFunc func(newObj interface{}),
|
||||
updateFunc func(oldObj interface{}, newObj interface{}),
|
||||
deleteFunc func(deletedObj interface{})) cache.SharedInformer {
|
||||
// select nodes by name
|
||||
fieldselector := fields.OneTermEqualSelector("metadata.name", nodeName)
|
||||
|
||||
// add some randomness to resync period, which can help avoid controllers falling into lock-step
|
||||
minResyncPeriod := 15 * time.Minute
|
||||
factor := rand.Float64() + 1
|
||||
resyncPeriod := time.Duration(float64(minResyncPeriod.Nanoseconds()) * factor)
|
||||
|
||||
lw := &cache.ListWatch{
|
||||
ListFunc: func(options metav1.ListOptions) (kuberuntime.Object, error) {
|
||||
return client.CoreV1().Nodes().List(metav1.ListOptions{
|
||||
FieldSelector: fieldselector.String(),
|
||||
})
|
||||
},
|
||||
WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
|
||||
return client.CoreV1().Nodes().Watch(metav1.ListOptions{
|
||||
FieldSelector: fieldselector.String(),
|
||||
ResourceVersion: options.ResourceVersion,
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
handler := cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: addFunc,
|
||||
UpdateFunc: updateFunc,
|
||||
DeleteFunc: deleteFunc,
|
||||
}
|
||||
|
||||
informer := cache.NewSharedInformer(lw, &apiv1.Node{}, resyncPeriod)
|
||||
informer.AddEventHandler(handler)
|
||||
|
||||
return informer
|
||||
}
|
||||
|
||||
// onAddNodeEvent calls onUpdateNodeEvent with the new object and a nil old object
|
||||
func (cc *Controller) onAddNodeEvent(newObj interface{}) {
|
||||
cc.onUpdateNodeEvent(nil, newObj)
|
||||
}
|
||||
|
||||
// onUpdateNodeEvent checks whether the configSource changed between oldObj and newObj, and pokes the
|
||||
// configuration sync worker if there was a change
|
||||
func (cc *Controller) onUpdateNodeEvent(oldObj interface{}, newObj interface{}) {
|
||||
newNode, ok := newObj.(*apiv1.Node)
|
||||
if !ok {
|
||||
utillog.Errorf("failed to cast new object to Node, couldn't handle event")
|
||||
return
|
||||
}
|
||||
if oldObj == nil {
|
||||
// Node was just added, need to sync
|
||||
cc.pokeConfigSourceWorker()
|
||||
return
|
||||
}
|
||||
oldNode, ok := oldObj.(*apiv1.Node)
|
||||
if !ok {
|
||||
utillog.Errorf("failed to cast old object to Node, couldn't handle event")
|
||||
return
|
||||
}
|
||||
if !utilequal.ConfigSourceEq(oldNode.Spec.ConfigSource, newNode.Spec.ConfigSource) {
|
||||
cc.pokeConfigSourceWorker()
|
||||
}
|
||||
}
|
||||
|
||||
// onDeleteNodeEvent logs a message if the Node was deleted and may log errors
|
||||
// if an unexpected DeletedFinalStateUnknown was received.
|
||||
// We allow the sync-loop to continue, because it is possible that the Kubelet detected
|
||||
// a Node with unexpected externalID and is attempting to delete and re-create the Node
|
||||
// (see pkg/kubelet/kubelet_node_status.go), or that someone accidently deleted the Node
|
||||
// (the Kubelet will re-create it).
|
||||
func (cc *Controller) onDeleteNodeEvent(deletedObj interface{}) {
|
||||
node, ok := deletedObj.(*apiv1.Node)
|
||||
if !ok {
|
||||
tombstone, ok := deletedObj.(cache.DeletedFinalStateUnknown)
|
||||
if !ok {
|
||||
utillog.Errorf("couldn't cast deleted object to DeletedFinalStateUnknown, object: %+v", deletedObj)
|
||||
return
|
||||
}
|
||||
node, ok = tombstone.Obj.(*apiv1.Node)
|
||||
if !ok {
|
||||
utillog.Errorf("received DeletedFinalStateUnknown object but it did not contain a Node, object: %+v", deletedObj)
|
||||
return
|
||||
}
|
||||
utillog.Infof("Node was deleted (DeletedFinalStateUnknown), sync-loop will continue because the Kubelet might recreate the Node, node: %+v", node)
|
||||
return
|
||||
}
|
||||
utillog.Infof("Node was deleted, sync-loop will continue because the Kubelet might recreate the Node, node: %+v", node)
|
||||
}
|
Reference in New Issue
Block a user