vendor update for CSI 0.3.0

This commit is contained in:
gman
2018-07-18 16:47:22 +02:00
parent 6f484f92fc
commit 8ea659f0d5
6810 changed files with 438061 additions and 193861 deletions

View File

@ -13,7 +13,9 @@ go_library(
"//pkg/api/legacyscheme:go_default_library",
"//pkg/apis/core/install:go_default_library",
"//pkg/kubelet/apis/kubeletconfig:go_default_library",
"//pkg/kubelet/apis/kubeletconfig/scheme:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
],
)

View File

@ -23,23 +23,51 @@ import (
_ "k8s.io/kubernetes/pkg/apis/core/install"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig"
"k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig/scheme"
)
// TODO(mtaufen): allow an encoder to be injected into checkpoint objects at creation time? (then we could ultimately instantiate only one encoder)
// EncodeKubeletConfig encodes an internal KubeletConfiguration to an external YAML representation
func EncodeKubeletConfig(internal *kubeletconfig.KubeletConfiguration, targetVersion schema.GroupVersion) ([]byte, error) {
encoder, err := NewKubeletconfigYAMLEncoder(targetVersion)
if err != nil {
return nil, err
}
// encoder will convert to external version
data, err := runtime.Encode(encoder, internal)
if err != nil {
return nil, err
}
return data, nil
}
// NewJSONEncoder generates a new runtime.Encoder that encodes objects to JSON
func NewJSONEncoder(groupName string) (runtime.Encoder, error) {
// encode to json
mediaType := "application/json"
// NewKubeletconfigYAMLEncoder returns an encoder that can write objects in the kubeletconfig API group to YAML
func NewKubeletconfigYAMLEncoder(targetVersion schema.GroupVersion) (runtime.Encoder, error) {
_, codecs, err := scheme.NewSchemeAndCodecs()
if err != nil {
return nil, err
}
mediaType := "application/yaml"
info, ok := runtime.SerializerInfoForMediaType(codecs.SupportedMediaTypes(), mediaType)
if !ok {
return nil, fmt.Errorf("unsupported media type %q", mediaType)
}
return codecs.EncoderForVersion(info.Serializer, targetVersion), nil
}
// NewYAMLEncoder generates a new runtime.Encoder that encodes objects to YAML
func NewYAMLEncoder(groupName string) (runtime.Encoder, error) {
// encode to YAML
mediaType := "application/yaml"
info, ok := runtime.SerializerInfoForMediaType(legacyscheme.Codecs.SupportedMediaTypes(), mediaType)
if !ok {
return nil, fmt.Errorf("unsupported media type %q", mediaType)
}
versions := legacyscheme.Registry.EnabledVersionsForGroup(groupName)
versions := legacyscheme.Scheme.PrioritizedVersionsForGroup(groupName)
if len(versions) == 0 {
return nil, fmt.Errorf("no enabled versions for group %q", groupName)
}

View File

@ -18,33 +18,6 @@ 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 source 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
}
// KubeletConfigOkEq returns true if the two conditions are semantically equivalent in the context of dynamic config
func KubeletConfigOkEq(a, b *apiv1.NodeCondition) bool {
return a.Message == b.Message && a.Reason == b.Reason && a.Status == b.Status

View File

@ -3,6 +3,7 @@ package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
@ -24,3 +25,13 @@ filegroup(
srcs = [":package-srcs"],
tags = ["automanaged"],
)
go_test(
name = "go_default_test",
srcs = ["files_test.go"],
embed = [":go_default_library"],
deps = [
"//pkg/kubelet/kubeletconfig/util/test:go_default_library",
"//pkg/util/filesystem:go_default_library",
],
)

View File

@ -24,7 +24,10 @@ import (
utilfs "k8s.io/kubernetes/pkg/util/filesystem"
)
const defaultPerm = 0666
const (
defaultPerm = 0755
tmptag = "tmp_" // additional prefix to prevent accidental collisions
)
// 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) {
@ -64,9 +67,10 @@ func EnsureFile(fs utilfs.Filesystem, path string) error {
}
// WriteTmpFile creates a temporary file at `path`, writes `data` into it, and fsyncs the file
// Expects the parent directory to exist.
func WriteTmpFile(fs utilfs.Filesystem, path string, data []byte) (tmpPath string, retErr error) {
dir := filepath.Dir(path)
prefix := filepath.Base(path)
prefix := tmptag + filepath.Base(path)
// create the tmp file
tmpFile, err := fs.TempFile(dir, prefix)
@ -81,7 +85,7 @@ func WriteTmpFile(fs utilfs.Filesystem, path string, data []byte) (tmpPath strin
// 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)
retErr = fmt.Errorf("attempted to remove temporary file %q after error %v, but failed due to error: %v", tmpPath, retErr, err)
}
tmpPath = ""
}
@ -100,7 +104,8 @@ func WriteTmpFile(fs utilfs.Filesystem, path string, data []byte) (tmpPath strin
}
// 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.
// dir as `path` and renaming the tmp file over `path`. The file does not have to exist to use ReplaceFile,
// but the parent directory must exist.
// Note ReplaceFile calls fsync.
func ReplaceFile(fs utilfs.Filesystem, path string, data []byte) error {
// write data to a temporary file
@ -118,7 +123,7 @@ func DirExists(fs utilfs.Filesystem, path string) (bool, error) {
if info.IsDir() {
return true, nil
}
return false, fmt.Errorf("expected dir at %q, but mode is is %q", path, info.Mode().String())
return false, fmt.Errorf("expected dir at %q, but mode is %q", path, info.Mode().String())
} else if os.IsNotExist(err) {
return false, nil
} else {
@ -137,3 +142,88 @@ func EnsureDir(fs utilfs.Filesystem, path string) error {
// create the dir
return fs.MkdirAll(path, defaultPerm)
}
// WriteTempDir creates a temporary dir at `path`, writes `files` into it, and fsyncs all the files
// The keys of `files` represent file names. These names must not:
// - be empty
// - be a path that contains more than the base name of a file (e.g. foo/bar is invalid, as is /bar)
// - match `.` or `..` exactly
// - be longer than 255 characters
// The above validation rules are based on atomic_writer.go, though in this case are more restrictive
// because we only allow a flat hierarchy.
func WriteTempDir(fs utilfs.Filesystem, path string, files map[string]string) (tmpPath string, retErr error) {
// validate the filename keys; for now we only allow a flat keyset
for name := range files {
// invalidate empty names
if name == "" {
return "", fmt.Errorf("invalid file key: must not be empty: %q", name)
}
// invalidate: foo/bar and /bar
if name != filepath.Base(name) {
return "", fmt.Errorf("invalid file key %q, only base names are allowed", name)
}
// invalidate `.` and `..`
if name == "." || name == ".." {
return "", fmt.Errorf("invalid file key, may not be '.' or '..'")
}
// invalidate length > 255 characters
if len(name) > 255 {
return "", fmt.Errorf("invalid file key %q, must be less than 255 characters", name)
}
}
// write the temp directory in parent dir and return path to the tmp directory
dir := filepath.Dir(path)
prefix := tmptag + filepath.Base(path)
// create the tmp dir
var err error
tmpPath, err = fs.TempDir(dir, prefix)
if err != nil {
return "", err
}
// be sure to clean up if there was an error
defer func() {
if retErr != nil {
if err := fs.RemoveAll(tmpPath); err != nil {
retErr = fmt.Errorf("attempted to remove temporary directory %q after error %v, but failed due to error: %v", tmpPath, retErr, err)
}
}
}()
// write data
for name, data := range files {
// create the file
file, err := fs.Create(filepath.Join(tmpPath, name))
if err != nil {
return tmpPath, err
}
// be sure to close the file when we're done
defer func() {
// close the file when we're done, don't overwrite primary retErr if close fails
if err := file.Close(); retErr == nil {
retErr = err
}
}()
// write the file
if _, err := file.Write([]byte(data)); err != nil {
return tmpPath, err
}
// sync the file, to ensure it's written in case a hard reset happens
if err := file.Sync(); err != nil {
return tmpPath, err
}
}
return tmpPath, nil
}
// ReplaceDir replaces the contents of the dir at `path` with `files` by writing to a tmp dir in the same
// dir as `path` and renaming the tmp dir over `path`. The dir does not have to exist to use ReplaceDir.
func ReplaceDir(fs utilfs.Filesystem, path string, files map[string]string) error {
// write data to a temporary directory
tmpPath, err := WriteTempDir(fs, path, files)
if err != nil {
return err
}
// rename over target directory
return fs.Rename(tmpPath, path)
}

View File

@ -0,0 +1,476 @@
/*
Copyright 2018 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"
"testing"
utiltest "k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/test"
utilfs "k8s.io/kubernetes/pkg/util/filesystem"
)
const (
prefix = "test-util-files"
)
type file struct {
name string
// mode distinguishes file type,
// we only check for regular vs. directory in these tests,
// specify regular as 0, directory as os.ModeDir
mode os.FileMode
data string // ignored if mode == os.ModeDir
}
func (f *file) write(fs utilfs.Filesystem, dir string) error {
path := filepath.Join(dir, f.name)
if f.mode.IsDir() {
if err := fs.MkdirAll(path, defaultPerm); err != nil {
return err
}
} else if f.mode.IsRegular() {
// create parent directories, if necessary
parents := filepath.Dir(path)
if err := fs.MkdirAll(parents, defaultPerm); err != nil {
return err
}
// create the file
handle, err := fs.Create(path)
if err != nil {
return err
}
_, err = handle.Write([]byte(f.data))
if err != nil {
if cerr := handle.Close(); cerr != nil {
return fmt.Errorf("error %v closing file after error: %v", cerr, err)
}
return err
}
} else {
return fmt.Errorf("mode not implemented for testing %s", f.mode.String())
}
return nil
}
func (f *file) expect(fs utilfs.Filesystem, dir string) error {
path := filepath.Join(dir, f.name)
if f.mode.IsDir() {
info, err := fs.Stat(path)
if err != nil {
return err
}
if !info.IsDir() {
return fmt.Errorf("expected directory, got mode %s", info.Mode().String())
}
} else if f.mode.IsRegular() {
info, err := fs.Stat(path)
if err != nil {
return err
}
if !info.Mode().IsRegular() {
return fmt.Errorf("expected regular file, got mode %s", info.Mode().String())
}
data, err := fs.ReadFile(path)
if err != nil {
return err
}
if f.data != string(data) {
return fmt.Errorf("expected file data %q, got %q", f.data, string(data))
}
} else {
return fmt.Errorf("mode not implemented for testing %s", f.mode.String())
}
return nil
}
// write files, perform some function, then attempt to read files back
// if err is non-empty, expects an error from the function performed in the test
// and skips reading back the expected files
type test struct {
desc string
writes []file
expects []file
fn func(fs utilfs.Filesystem, dir string, c *test) []error
err string
}
func (c *test) write(t *testing.T, fs utilfs.Filesystem, dir string) {
for _, f := range c.writes {
if err := f.write(fs, dir); err != nil {
t.Fatalf("error pre-writing file: %v", err)
}
}
}
// you can optionally skip calling t.Errorf by passing a nil t, and process the
// returned errors instead
func (c *test) expect(t *testing.T, fs utilfs.Filesystem, dir string) []error {
errs := []error{}
for _, f := range c.expects {
if err := f.expect(fs, dir); err != nil {
msg := fmt.Errorf("expect %#v, got error: %v", f, err)
errs = append(errs, msg)
if t != nil {
t.Errorf("%s", msg)
}
}
}
return errs
}
// run a test case, with an arbitrary function to execute between write and expect
// if c.fn is nil, errors from c.expect are checked against c.err, instead of errors
// from fn being checked against c.err
func (c *test) run(t *testing.T, fs utilfs.Filesystem) {
// isolate each test case in a new temporary directory
dir, err := fs.TempDir("", prefix)
if err != nil {
t.Fatalf("error creating temporary directory for test: %v", err)
}
c.write(t, fs, dir)
// if fn exists, check errors from fn, then check expected files
if c.fn != nil {
errs := c.fn(fs, dir, c)
if len(errs) > 0 {
for _, err := range errs {
utiltest.ExpectError(t, err, c.err)
}
// skip checking expected files if we expected errors
// (usually means we didn't create file)
return
}
c.expect(t, fs, dir)
return
}
// just check expected files, and compare errors from c.expect to c.err
// (this lets us test the helper functions above)
errs := c.expect(nil, fs, dir)
for _, err := range errs {
utiltest.ExpectError(t, err, c.err)
}
}
// simple test of the above helper functions
func TestHelpers(t *testing.T) {
// omitting the test.fn means test.err is compared to errors from test.expect
cases := []test{
{
desc: "regular file",
writes: []file{{name: "foo", data: "bar"}},
expects: []file{{name: "foo", data: "bar"}},
},
{
desc: "directory",
writes: []file{{name: "foo", mode: os.ModeDir}},
expects: []file{{name: "foo", mode: os.ModeDir}},
},
{
desc: "deep regular file",
writes: []file{{name: "foo/bar", data: "baz"}},
expects: []file{{name: "foo/bar", data: "baz"}},
},
{
desc: "deep directory",
writes: []file{{name: "foo/bar", mode: os.ModeDir}},
expects: []file{{name: "foo/bar", mode: os.ModeDir}},
},
{
desc: "missing file",
expects: []file{{name: "foo", data: "bar"}},
err: "no such file or directory",
},
{
desc: "missing directory",
expects: []file{{name: "foo/bar", mode: os.ModeDir}},
err: "no such file or directory",
},
}
for _, c := range cases {
t.Run(c.desc, func(t *testing.T) {
c.run(t, utilfs.DefaultFs{})
})
}
}
func TestFileExists(t *testing.T) {
fn := func(fs utilfs.Filesystem, dir string, c *test) []error {
ok, err := FileExists(fs, filepath.Join(dir, "foo"))
if err != nil {
return []error{err}
}
if !ok {
return []error{fmt.Errorf("does not exist (test)")}
}
return nil
}
cases := []test{
{
fn: fn,
desc: "file exists",
writes: []file{{name: "foo"}},
},
{
fn: fn,
desc: "file does not exist",
err: "does not exist (test)",
},
{
fn: fn,
desc: "object has non-file mode",
writes: []file{{name: "foo", mode: os.ModeDir}},
err: "expected regular file",
},
}
for _, c := range cases {
t.Run(c.desc, func(t *testing.T) {
c.run(t, utilfs.DefaultFs{})
})
}
}
func TestEnsureFile(t *testing.T) {
fn := func(fs utilfs.Filesystem, dir string, c *test) []error {
var errs []error
for _, f := range c.expects {
if err := EnsureFile(fs, filepath.Join(dir, f.name)); err != nil {
errs = append(errs, err)
}
}
return errs
}
cases := []test{
{
fn: fn,
desc: "file exists",
writes: []file{{name: "foo"}},
expects: []file{{name: "foo"}},
},
{
fn: fn,
desc: "file does not exist",
expects: []file{{name: "bar"}},
},
{
fn: fn,
desc: "neither parent nor file exists",
expects: []file{{name: "baz/quux"}},
},
}
for _, c := range cases {
t.Run(c.desc, func(t *testing.T) {
c.run(t, utilfs.DefaultFs{})
})
}
}
// Note: This transitively tests WriteTmpFile
func TestReplaceFile(t *testing.T) {
fn := func(fs utilfs.Filesystem, dir string, c *test) []error {
var errs []error
for _, f := range c.expects {
if err := ReplaceFile(fs, filepath.Join(dir, f.name), []byte(f.data)); err != nil {
errs = append(errs, err)
}
}
return errs
}
cases := []test{
{
fn: fn,
desc: "file exists",
writes: []file{{name: "foo"}},
expects: []file{{name: "foo", data: "bar"}},
},
{
fn: fn,
desc: "file does not exist",
expects: []file{{name: "foo", data: "bar"}},
},
{
fn: func(fs utilfs.Filesystem, dir string, c *test) []error {
if err := ReplaceFile(fs, filepath.Join(dir, "foo/bar"), []byte("")); err != nil {
return []error{err}
}
return nil
},
desc: "neither parent nor file exists",
err: "no such file or directory",
},
}
for _, c := range cases {
t.Run(c.desc, func(t *testing.T) {
c.run(t, utilfs.DefaultFs{})
})
}
}
func TestDirExists(t *testing.T) {
fn := func(fs utilfs.Filesystem, dir string, c *test) []error {
ok, err := DirExists(fs, filepath.Join(dir, "foo"))
if err != nil {
return []error{err}
}
if !ok {
return []error{fmt.Errorf("does not exist (test)")}
}
return nil
}
cases := []test{
{
fn: fn,
desc: "dir exists",
writes: []file{{name: "foo", mode: os.ModeDir}},
},
{
fn: fn,
desc: "dir does not exist",
err: "does not exist (test)",
},
{
fn: fn,
desc: "object has non-dir mode",
writes: []file{{name: "foo"}},
err: "expected dir",
},
}
for _, c := range cases {
t.Run(c.desc, func(t *testing.T) {
c.run(t, utilfs.DefaultFs{})
})
}
}
func TestEnsureDir(t *testing.T) {
fn := func(fs utilfs.Filesystem, dir string, c *test) []error {
var errs []error
for _, f := range c.expects {
if err := EnsureDir(fs, filepath.Join(dir, f.name)); err != nil {
errs = append(errs, err)
}
}
return errs
}
cases := []test{
{
fn: fn,
desc: "dir exists",
writes: []file{{name: "foo", mode: os.ModeDir}},
expects: []file{{name: "foo", mode: os.ModeDir}},
},
{
fn: fn,
desc: "dir does not exist",
expects: []file{{name: "bar", mode: os.ModeDir}},
},
{
fn: fn,
desc: "neither parent nor dir exists",
expects: []file{{name: "baz/quux", mode: os.ModeDir}},
},
}
for _, c := range cases {
t.Run(c.desc, func(t *testing.T) {
c.run(t, utilfs.DefaultFs{})
})
}
}
func TestWriteTempDir(t *testing.T) {
// writing a tmp dir is covered by TestReplaceDir, but we additionally test filename validation here
c := test{
desc: "invalid file key",
err: "invalid file key",
fn: func(fs utilfs.Filesystem, dir string, c *test) []error {
if _, err := WriteTempDir(fs, filepath.Join(dir, "tmpdir"), map[string]string{"foo/bar": ""}); err != nil {
return []error{err}
}
return nil
},
}
c.run(t, utilfs.DefaultFs{})
}
func TestReplaceDir(t *testing.T) {
fn := func(fs utilfs.Filesystem, dir string, c *test) []error {
errs := []error{}
// compute filesets from expected files and call ReplaceDir for each
// we don't nest dirs in test cases, order of ReplaceDir call is not guaranteed
dirs := map[string]map[string]string{}
// allocate dirs
for _, f := range c.expects {
if f.mode.IsDir() {
path := filepath.Join(dir, f.name)
if _, ok := dirs[path]; !ok {
dirs[path] = map[string]string{}
}
} else if f.mode.IsRegular() {
path := filepath.Join(dir, filepath.Dir(f.name))
if _, ok := dirs[path]; !ok {
// require an expectation for the parent directory if there is an expectation for the file
errs = append(errs, fmt.Errorf("no prior parent directory in c.expects for file %s", f.name))
continue
}
dirs[path][filepath.Base(f.name)] = f.data
}
}
// short-circuit test case validation errors
if len(errs) > 0 {
return errs
}
// call ReplaceDir for each desired dir
for path, files := range dirs {
if err := ReplaceDir(fs, path, files); err != nil {
errs = append(errs, err)
}
}
return errs
}
cases := []test{
{
fn: fn,
desc: "fn catches invalid test case",
expects: []file{{name: "foo/bar"}},
err: "no prior parent directory",
},
{
fn: fn,
desc: "empty dir",
expects: []file{{name: "foo", mode: os.ModeDir}},
},
{
fn: fn,
desc: "dir with files",
expects: []file{
{name: "foo", mode: os.ModeDir},
{name: "foo/bar", data: "baz"},
{name: "foo/baz", data: "bar"},
},
},
}
for _, c := range cases {
t.Run(c.desc, func(t *testing.T) {
c.run(t, utilfs.DefaultFs{})
})
}
}

View File

@ -21,6 +21,21 @@ import (
"testing"
)
// ExpectError calls t.Fatalf if the error does not contain a substr match.
// If substr is empty, a nil error is expected.
// It is useful to call ExpectError from subtests.
func ExpectError(t *testing.T, err error, substr string) {
if err != nil {
if len(substr) == 0 {
t.Fatalf("expect nil error but got %q", err.Error())
} else if !strings.Contains(err.Error(), substr) {
t.Fatalf("expect error to contain %q but got %q", substr, err.Error())
}
} else if len(substr) > 0 {
t.Fatalf("expect error to contain %q but got nil error", substr)
}
}
// 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.