mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-14 10:53:34 +00:00
vendor files
This commit is contained in:
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)
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user