mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-14 18:53:35 +00:00
vendor cleanup: remove unused,non-go and test files
This commit is contained in:
79
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/BUILD
generated
vendored
79
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/BUILD
generated
vendored
@ -1,79 +0,0 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"arguments.go",
|
||||
"cgroupdriver.go",
|
||||
"copy.go",
|
||||
"endpoint.go",
|
||||
"error.go",
|
||||
"marshal.go",
|
||||
"template.go",
|
||||
"version.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/cmd/kubeadm/app/util",
|
||||
deps = [
|
||||
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
|
||||
"//cmd/kubeadm/app/apis/kubeadm/v1alpha1:go_default_library",
|
||||
"//vendor/gopkg.in/yaml.v2: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",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/net:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/validation:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes/scheme:go_default_library",
|
||||
"//vendor/k8s.io/utils/exec:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"arguments_test.go",
|
||||
"cgroupdriver_test.go",
|
||||
"endpoint_test.go",
|
||||
"error_test.go",
|
||||
"marshal_test.go",
|
||||
"template_test.go",
|
||||
"version_test.go",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
|
||||
"//cmd/kubeadm/app/apis/kubeadm/scheme:go_default_library",
|
||||
"//cmd/kubeadm/app/apis/kubeadm/v1alpha1:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [
|
||||
":package-srcs",
|
||||
"//cmd/kubeadm/app/util/apiclient:all-srcs",
|
||||
"//cmd/kubeadm/app/util/audit:all-srcs",
|
||||
"//cmd/kubeadm/app/util/config:all-srcs",
|
||||
"//cmd/kubeadm/app/util/dryrun:all-srcs",
|
||||
"//cmd/kubeadm/app/util/etcd:all-srcs",
|
||||
"//cmd/kubeadm/app/util/kubeconfig:all-srcs",
|
||||
"//cmd/kubeadm/app/util/pubkeypin:all-srcs",
|
||||
"//cmd/kubeadm/app/util/staticpod:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
)
|
74
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient/BUILD
generated
vendored
74
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient/BUILD
generated
vendored
@ -1,74 +0,0 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"clientbacked_dryrun.go",
|
||||
"dryrunclient.go",
|
||||
"idempotency.go",
|
||||
"init_dryrun.go",
|
||||
"wait.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient",
|
||||
deps = [
|
||||
"//cmd/kubeadm/app/constants:go_default_library",
|
||||
"//cmd/kubeadm/app/util:go_default_library",
|
||||
"//pkg/kubelet/apis:go_default_library",
|
||||
"//pkg/kubelet/types:go_default_library",
|
||||
"//pkg/registry/core/service/ipallocator:go_default_library",
|
||||
"//vendor/k8s.io/api/apps/v1:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/api/rbac/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/errors: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/schema:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/intstr:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/net:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/strategicpatch:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||
"//vendor/k8s.io/client-go/dynamic:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes/fake:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes/scheme:go_default_library",
|
||||
"//vendor/k8s.io/client-go/rest:go_default_library",
|
||||
"//vendor/k8s.io/client-go/testing:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/clientcmd:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"dryrunclient_test.go",
|
||||
"init_dryrun_test.go",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/api/rbac/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//vendor/k8s.io/client-go/testing:go_default_library",
|
||||
],
|
||||
)
|
131
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient/clientbacked_dryrun.go
generated
vendored
131
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient/clientbacked_dryrun.go
generated
vendored
@ -1,131 +0,0 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package apiclient
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/dynamic"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
clientsetscheme "k8s.io/client-go/kubernetes/scheme"
|
||||
"k8s.io/client-go/rest"
|
||||
core "k8s.io/client-go/testing"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
)
|
||||
|
||||
// ClientBackedDryRunGetter implements the DryRunGetter interface for use in NewDryRunClient() and proxies all GET and LIST requests to the backing API server reachable via rest.Config
|
||||
type ClientBackedDryRunGetter struct {
|
||||
client clientset.Interface
|
||||
dynamicClient dynamic.Interface
|
||||
}
|
||||
|
||||
// InitDryRunGetter should implement the DryRunGetter interface
|
||||
var _ DryRunGetter = &ClientBackedDryRunGetter{}
|
||||
|
||||
// NewClientBackedDryRunGetter creates a new ClientBackedDryRunGetter instance based on the rest.Config object
|
||||
func NewClientBackedDryRunGetter(config *rest.Config) (*ClientBackedDryRunGetter, error) {
|
||||
client, err := clientset.NewForConfig(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dynamicClient, err := dynamic.NewForConfig(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &ClientBackedDryRunGetter{
|
||||
client: client,
|
||||
dynamicClient: dynamicClient,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewClientBackedDryRunGetterFromKubeconfig creates a new ClientBackedDryRunGetter instance from the given KubeConfig file
|
||||
func NewClientBackedDryRunGetterFromKubeconfig(file string) (*ClientBackedDryRunGetter, error) {
|
||||
config, err := clientcmd.LoadFromFile(file)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to load kubeconfig: %v", err)
|
||||
}
|
||||
clientConfig, err := clientcmd.NewDefaultClientConfig(*config, &clientcmd.ConfigOverrides{}).ClientConfig()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create API client configuration from kubeconfig: %v", err)
|
||||
}
|
||||
return NewClientBackedDryRunGetter(clientConfig)
|
||||
}
|
||||
|
||||
// HandleGetAction handles GET actions to the dryrun clientset this interface supports
|
||||
func (clg *ClientBackedDryRunGetter) HandleGetAction(action core.GetAction) (bool, runtime.Object, error) {
|
||||
unstructuredObj, err := clg.dynamicClient.Resource(action.GetResource()).Namespace(action.GetNamespace()).Get(action.GetName(), metav1.GetOptions{})
|
||||
// Inform the user that the requested object wasn't found.
|
||||
printIfNotExists(err)
|
||||
if err != nil {
|
||||
return true, nil, err
|
||||
}
|
||||
newObj, err := decodeUnstructuredIntoAPIObject(action, unstructuredObj)
|
||||
if err != nil {
|
||||
fmt.Printf("error after decode: %v %v\n", unstructuredObj, err)
|
||||
return true, nil, err
|
||||
}
|
||||
return true, newObj, err
|
||||
}
|
||||
|
||||
// HandleListAction handles LIST actions to the dryrun clientset this interface supports
|
||||
func (clg *ClientBackedDryRunGetter) HandleListAction(action core.ListAction) (bool, runtime.Object, error) {
|
||||
listOpts := metav1.ListOptions{
|
||||
LabelSelector: action.GetListRestrictions().Labels.String(),
|
||||
FieldSelector: action.GetListRestrictions().Fields.String(),
|
||||
}
|
||||
|
||||
unstructuredList, err := clg.dynamicClient.Resource(action.GetResource()).Namespace(action.GetNamespace()).List(listOpts)
|
||||
if err != nil {
|
||||
return true, nil, err
|
||||
}
|
||||
newObj, err := decodeUnstructuredIntoAPIObject(action, unstructuredList)
|
||||
if err != nil {
|
||||
fmt.Printf("error after decode: %v %v\n", unstructuredList, err)
|
||||
return true, nil, err
|
||||
}
|
||||
return true, newObj, err
|
||||
}
|
||||
|
||||
// Client gets the backing clientset.Interface
|
||||
func (clg *ClientBackedDryRunGetter) Client() clientset.Interface {
|
||||
return clg.client
|
||||
}
|
||||
|
||||
// decodeUnversionedIntoAPIObject converts the *unversioned.Unversioned object returned from the dynamic client
|
||||
// to bytes; and then decodes it back _to an external api version (k8s.io/api vs k8s.io/kubernetes/pkg/api*)_ using the normal API machinery
|
||||
func decodeUnstructuredIntoAPIObject(action core.Action, unstructuredObj runtime.Unstructured) (runtime.Object, error) {
|
||||
objBytes, err := json.Marshal(unstructuredObj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newObj, err := runtime.Decode(clientsetscheme.Codecs.UniversalDecoder(action.GetResource().GroupVersion()), objBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newObj, nil
|
||||
}
|
||||
|
||||
func printIfNotExists(err error) {
|
||||
if apierrors.IsNotFound(err) {
|
||||
fmt.Println("[dryrun] The GET request didn't yield any result, the API Server returned a NotFound error.")
|
||||
}
|
||||
}
|
242
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient/dryrunclient.go
generated
vendored
242
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient/dryrunclient.go
generated
vendored
@ -1,242 +0,0 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package apiclient
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
fakeclientset "k8s.io/client-go/kubernetes/fake"
|
||||
core "k8s.io/client-go/testing"
|
||||
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
||||
)
|
||||
|
||||
// DryRunGetter is an interface that must be supplied to the NewDryRunClient function in order to contstruct a fully functional fake dryrun clientset
|
||||
type DryRunGetter interface {
|
||||
HandleGetAction(core.GetAction) (bool, runtime.Object, error)
|
||||
HandleListAction(core.ListAction) (bool, runtime.Object, error)
|
||||
}
|
||||
|
||||
// MarshalFunc takes care of converting any object to a byte array for displaying the object to the user
|
||||
type MarshalFunc func(runtime.Object, schema.GroupVersion) ([]byte, error)
|
||||
|
||||
// DefaultMarshalFunc is the default MarshalFunc used; uses YAML to print objects to the user
|
||||
func DefaultMarshalFunc(obj runtime.Object, gv schema.GroupVersion) ([]byte, error) {
|
||||
return kubeadmutil.MarshalToYaml(obj, gv)
|
||||
}
|
||||
|
||||
// DryRunClientOptions specifies options to pass to NewDryRunClientWithOpts in order to get a dryrun clientset
|
||||
type DryRunClientOptions struct {
|
||||
Writer io.Writer
|
||||
Getter DryRunGetter
|
||||
PrependReactors []core.Reactor
|
||||
AppendReactors []core.Reactor
|
||||
MarshalFunc MarshalFunc
|
||||
PrintGETAndLIST bool
|
||||
}
|
||||
|
||||
// GetDefaultDryRunClientOptions returns the default DryRunClientOptions values
|
||||
func GetDefaultDryRunClientOptions(drg DryRunGetter, w io.Writer) DryRunClientOptions {
|
||||
return DryRunClientOptions{
|
||||
Writer: w,
|
||||
Getter: drg,
|
||||
PrependReactors: []core.Reactor{},
|
||||
AppendReactors: []core.Reactor{},
|
||||
MarshalFunc: DefaultMarshalFunc,
|
||||
PrintGETAndLIST: false,
|
||||
}
|
||||
}
|
||||
|
||||
// actionWithName is the generic interface for an action that has a name associated with it
|
||||
// This just makes it easier to catch all actions that has a name; instead of hard-coding all request that has it associated
|
||||
type actionWithName interface {
|
||||
core.Action
|
||||
GetName() string
|
||||
}
|
||||
|
||||
// actionWithObject is the generic interface for an action that has an object associated with it
|
||||
// This just makes it easier to catch all actions that has an object; instead of hard-coding all request that has it associated
|
||||
type actionWithObject interface {
|
||||
core.Action
|
||||
GetObject() runtime.Object
|
||||
}
|
||||
|
||||
// NewDryRunClient is a wrapper for NewDryRunClientWithOpts using some default values
|
||||
func NewDryRunClient(drg DryRunGetter, w io.Writer) clientset.Interface {
|
||||
return NewDryRunClientWithOpts(GetDefaultDryRunClientOptions(drg, w))
|
||||
}
|
||||
|
||||
// NewDryRunClientWithOpts returns a clientset.Interface that can be used normally for talking to the Kubernetes API.
|
||||
// This client doesn't apply changes to the backend. The client gets GET/LIST values from the DryRunGetter implementation.
|
||||
// This client logs all I/O to the writer w in YAML format
|
||||
func NewDryRunClientWithOpts(opts DryRunClientOptions) clientset.Interface {
|
||||
// Build a chain of reactors to act like a normal clientset; but log everything that is happening and don't change any state
|
||||
client := fakeclientset.NewSimpleClientset()
|
||||
|
||||
// Build the chain of reactors. Order matters; first item here will be invoked first on match, then the second one will be evaluated, etc.
|
||||
defaultReactorChain := []core.Reactor{
|
||||
// Log everything that happens. Default the object if it's about to be created/updated so that the logged object is representative.
|
||||
&core.SimpleReactor{
|
||||
Verb: "*",
|
||||
Resource: "*",
|
||||
Reaction: func(action core.Action) (bool, runtime.Object, error) {
|
||||
logDryRunAction(action, opts.Writer, opts.MarshalFunc)
|
||||
|
||||
return false, nil, nil
|
||||
},
|
||||
},
|
||||
// Let the DryRunGetter implementation take care of all GET requests.
|
||||
// The DryRunGetter implementation may call a real API Server behind the scenes or just fake everything
|
||||
&core.SimpleReactor{
|
||||
Verb: "get",
|
||||
Resource: "*",
|
||||
Reaction: func(action core.Action) (bool, runtime.Object, error) {
|
||||
getAction, ok := action.(core.GetAction)
|
||||
if !ok {
|
||||
// something's wrong, we can't handle this event
|
||||
return true, nil, fmt.Errorf("can't cast get reactor event action object to GetAction interface")
|
||||
}
|
||||
handled, obj, err := opts.Getter.HandleGetAction(getAction)
|
||||
|
||||
if opts.PrintGETAndLIST {
|
||||
// Print the marshalled object format with one tab indentation
|
||||
objBytes, err := opts.MarshalFunc(obj, action.GetResource().GroupVersion())
|
||||
if err == nil {
|
||||
fmt.Println("[dryrun] Returning faked GET response:")
|
||||
PrintBytesWithLinePrefix(opts.Writer, objBytes, "\t")
|
||||
}
|
||||
}
|
||||
|
||||
return handled, obj, err
|
||||
},
|
||||
},
|
||||
// Let the DryRunGetter implementation take care of all GET requests.
|
||||
// The DryRunGetter implementation may call a real API Server behind the scenes or just fake everything
|
||||
&core.SimpleReactor{
|
||||
Verb: "list",
|
||||
Resource: "*",
|
||||
Reaction: func(action core.Action) (bool, runtime.Object, error) {
|
||||
listAction, ok := action.(core.ListAction)
|
||||
if !ok {
|
||||
// something's wrong, we can't handle this event
|
||||
return true, nil, fmt.Errorf("can't cast list reactor event action object to ListAction interface")
|
||||
}
|
||||
handled, objs, err := opts.Getter.HandleListAction(listAction)
|
||||
|
||||
if opts.PrintGETAndLIST {
|
||||
// Print the marshalled object format with one tab indentation
|
||||
objBytes, err := opts.MarshalFunc(objs, action.GetResource().GroupVersion())
|
||||
if err == nil {
|
||||
fmt.Println("[dryrun] Returning faked LIST response:")
|
||||
PrintBytesWithLinePrefix(opts.Writer, objBytes, "\t")
|
||||
}
|
||||
}
|
||||
|
||||
return handled, objs, err
|
||||
},
|
||||
},
|
||||
// For the verbs that modify anything on the server; just return the object if present and exit successfully
|
||||
&core.SimpleReactor{
|
||||
Verb: "create",
|
||||
Resource: "*",
|
||||
Reaction: successfulModificationReactorFunc,
|
||||
},
|
||||
&core.SimpleReactor{
|
||||
Verb: "update",
|
||||
Resource: "*",
|
||||
Reaction: successfulModificationReactorFunc,
|
||||
},
|
||||
&core.SimpleReactor{
|
||||
Verb: "delete",
|
||||
Resource: "*",
|
||||
Reaction: successfulModificationReactorFunc,
|
||||
},
|
||||
&core.SimpleReactor{
|
||||
Verb: "delete-collection",
|
||||
Resource: "*",
|
||||
Reaction: successfulModificationReactorFunc,
|
||||
},
|
||||
&core.SimpleReactor{
|
||||
Verb: "patch",
|
||||
Resource: "*",
|
||||
Reaction: successfulModificationReactorFunc,
|
||||
},
|
||||
}
|
||||
|
||||
// The chain of reactors will look like this:
|
||||
// opts.PrependReactors | defaultReactorChain | opts.AppendReactors | client.Fake.ReactionChain (default reactors for the fake clientset)
|
||||
fullReactorChain := append(opts.PrependReactors, defaultReactorChain...)
|
||||
fullReactorChain = append(fullReactorChain, opts.AppendReactors...)
|
||||
|
||||
// Prepend the reaction chain with our reactors. Important, these MUST be prepended; not appended due to how the fake clientset works by default
|
||||
client.Fake.ReactionChain = append(fullReactorChain, client.Fake.ReactionChain...)
|
||||
return client
|
||||
}
|
||||
|
||||
// successfulModificationReactorFunc is a no-op that just returns the POSTed/PUTed value if present; but does nothing to edit any backing data store.
|
||||
func successfulModificationReactorFunc(action core.Action) (bool, runtime.Object, error) {
|
||||
objAction, ok := action.(actionWithObject)
|
||||
if ok {
|
||||
return true, objAction.GetObject(), nil
|
||||
}
|
||||
return true, nil, nil
|
||||
}
|
||||
|
||||
// logDryRunAction logs the action that was recorded by the "catch-all" (*,*) reactor and tells the user what would have happened in an user-friendly way
|
||||
func logDryRunAction(action core.Action, w io.Writer, marshalFunc MarshalFunc) {
|
||||
|
||||
group := action.GetResource().Group
|
||||
if len(group) == 0 {
|
||||
group = "core"
|
||||
}
|
||||
fmt.Fprintf(w, "[dryrun] Would perform action %s on resource %q in API group \"%s/%s\"\n", strings.ToUpper(action.GetVerb()), action.GetResource().Resource, group, action.GetResource().Version)
|
||||
|
||||
namedAction, ok := action.(actionWithName)
|
||||
if ok {
|
||||
fmt.Fprintf(w, "[dryrun] Resource name: %q\n", namedAction.GetName())
|
||||
}
|
||||
|
||||
objAction, ok := action.(actionWithObject)
|
||||
if ok && objAction.GetObject() != nil {
|
||||
// Print the marshalled object with a tab indentation
|
||||
objBytes, err := marshalFunc(objAction.GetObject(), action.GetResource().GroupVersion())
|
||||
if err == nil {
|
||||
fmt.Println("[dryrun] Attached object:")
|
||||
PrintBytesWithLinePrefix(w, objBytes, "\t")
|
||||
}
|
||||
}
|
||||
|
||||
patchAction, ok := action.(core.PatchAction)
|
||||
if ok {
|
||||
// Replace all occurrences of \" with a simple " when printing
|
||||
fmt.Fprintf(w, "[dryrun] Attached patch:\n\t%s\n", strings.Replace(string(patchAction.GetPatch()), `\"`, `"`, -1))
|
||||
}
|
||||
}
|
||||
|
||||
// PrintBytesWithLinePrefix prints objBytes to writer w with linePrefix in the beginning of every line
|
||||
func PrintBytesWithLinePrefix(w io.Writer, objBytes []byte, linePrefix string) {
|
||||
scanner := bufio.NewScanner(bytes.NewReader(objBytes))
|
||||
for scanner.Scan() {
|
||||
fmt.Fprintf(w, "%s%s\n", linePrefix, scanner.Text())
|
||||
}
|
||||
}
|
102
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient/dryrunclient_test.go
generated
vendored
102
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient/dryrunclient_test.go
generated
vendored
@ -1,102 +0,0 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package apiclient
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
rbac "k8s.io/api/rbac/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
core "k8s.io/client-go/testing"
|
||||
)
|
||||
|
||||
func TestLogDryRunAction(t *testing.T) {
|
||||
var tests = []struct {
|
||||
action core.Action
|
||||
expectedBytes []byte
|
||||
buf *bytes.Buffer
|
||||
}{
|
||||
{
|
||||
action: core.NewGetAction(schema.GroupVersionResource{Version: "v1", Resource: "services"}, "default", "kubernetes"),
|
||||
expectedBytes: []byte(`[dryrun] Would perform action GET on resource "services" in API group "core/v1"
|
||||
[dryrun] Resource name: "kubernetes"
|
||||
`),
|
||||
},
|
||||
{
|
||||
action: core.NewRootGetAction(schema.GroupVersionResource{Group: rbac.GroupName, Version: rbac.SchemeGroupVersion.Version, Resource: "clusterrolebindings"}, "system:node"),
|
||||
expectedBytes: []byte(`[dryrun] Would perform action GET on resource "clusterrolebindings" in API group "rbac.authorization.k8s.io/v1"
|
||||
[dryrun] Resource name: "system:node"
|
||||
`),
|
||||
},
|
||||
{
|
||||
action: core.NewListAction(schema.GroupVersionResource{Version: "v1", Resource: "services"}, schema.GroupVersionKind{Version: "v1", Kind: "Service"}, "default", metav1.ListOptions{}),
|
||||
expectedBytes: []byte(`[dryrun] Would perform action LIST on resource "services" in API group "core/v1"
|
||||
`),
|
||||
},
|
||||
{
|
||||
action: core.NewCreateAction(schema.GroupVersionResource{Version: "v1", Resource: "services"}, "default", &v1.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
Spec: v1.ServiceSpec{
|
||||
ClusterIP: "1.1.1.1",
|
||||
},
|
||||
}),
|
||||
expectedBytes: []byte(`[dryrun] Would perform action CREATE on resource "services" in API group "core/v1"
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: foo
|
||||
spec:
|
||||
clusterIP: 1.1.1.1
|
||||
status:
|
||||
loadBalancer: {}
|
||||
`),
|
||||
},
|
||||
{
|
||||
action: core.NewPatchAction(schema.GroupVersionResource{Version: "v1", Resource: "nodes"}, "default", "my-node", []byte(`{"spec":{"taints":[{"key": "foo", "value": "bar"}]}}`)),
|
||||
expectedBytes: []byte(`[dryrun] Would perform action PATCH on resource "nodes" in API group "core/v1"
|
||||
[dryrun] Resource name: "my-node"
|
||||
[dryrun] Attached patch:
|
||||
{"spec":{"taints":[{"key": "foo", "value": "bar"}]}}
|
||||
`),
|
||||
},
|
||||
{
|
||||
action: core.NewDeleteAction(schema.GroupVersionResource{Version: "v1", Resource: "pods"}, "default", "my-pod"),
|
||||
expectedBytes: []byte(`[dryrun] Would perform action DELETE on resource "pods" in API group "core/v1"
|
||||
[dryrun] Resource name: "my-pod"
|
||||
`),
|
||||
},
|
||||
}
|
||||
for _, rt := range tests {
|
||||
rt.buf = bytes.NewBufferString("")
|
||||
logDryRunAction(rt.action, rt.buf, DefaultMarshalFunc)
|
||||
actualBytes := rt.buf.Bytes()
|
||||
|
||||
if !bytes.Equal(actualBytes, rt.expectedBytes) {
|
||||
t.Errorf(
|
||||
"failed LogDryRunAction:\n\texpected bytes: %q\n\t actual: %q",
|
||||
rt.expectedBytes,
|
||||
actualBytes,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
240
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient/idempotency.go
generated
vendored
240
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient/idempotency.go
generated
vendored
@ -1,240 +0,0 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package apiclient
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
apps "k8s.io/api/apps/v1"
|
||||
"k8s.io/api/core/v1"
|
||||
rbac "k8s.io/api/rbac/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/strategicpatch"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||
kubeletapis "k8s.io/kubernetes/pkg/kubelet/apis"
|
||||
)
|
||||
|
||||
// TODO: We should invent a dynamic mechanism for this using the dynamic client instead of hard-coding these functions per-type
|
||||
// TODO: We may want to retry if .Update() fails on 409 Conflict
|
||||
|
||||
// CreateOrUpdateConfigMap creates a ConfigMap if the target resource doesn't exist. If the resource exists already, this function will update the resource instead.
|
||||
func CreateOrUpdateConfigMap(client clientset.Interface, cm *v1.ConfigMap) error {
|
||||
if _, err := client.CoreV1().ConfigMaps(cm.ObjectMeta.Namespace).Create(cm); err != nil {
|
||||
if !apierrors.IsAlreadyExists(err) {
|
||||
return fmt.Errorf("unable to create configmap: %v", err)
|
||||
}
|
||||
|
||||
if _, err := client.CoreV1().ConfigMaps(cm.ObjectMeta.Namespace).Update(cm); err != nil {
|
||||
return fmt.Errorf("unable to update configmap: %v", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateOrRetainConfigMap creates a ConfigMap if the target resource doesn't exist. If the resource exists already, this function will retain the resource instead.
|
||||
func CreateOrRetainConfigMap(client clientset.Interface, cm *v1.ConfigMap, configMapName string) error {
|
||||
if _, err := client.CoreV1().ConfigMaps(cm.ObjectMeta.Namespace).Get(configMapName, metav1.GetOptions{}); err != nil {
|
||||
if !apierrors.IsNotFound(err) {
|
||||
return nil
|
||||
}
|
||||
if _, err := client.CoreV1().ConfigMaps(cm.ObjectMeta.Namespace).Create(cm); err != nil {
|
||||
if !apierrors.IsAlreadyExists(err) {
|
||||
return fmt.Errorf("unable to create configmap: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateOrUpdateSecret creates a Secret if the target resource doesn't exist. If the resource exists already, this function will update the resource instead.
|
||||
func CreateOrUpdateSecret(client clientset.Interface, secret *v1.Secret) error {
|
||||
if _, err := client.CoreV1().Secrets(secret.ObjectMeta.Namespace).Create(secret); err != nil {
|
||||
if !apierrors.IsAlreadyExists(err) {
|
||||
return fmt.Errorf("unable to create secret: %v", err)
|
||||
}
|
||||
|
||||
if _, err := client.CoreV1().Secrets(secret.ObjectMeta.Namespace).Update(secret); err != nil {
|
||||
return fmt.Errorf("unable to update secret: %v", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateOrUpdateServiceAccount creates a ServiceAccount if the target resource doesn't exist. If the resource exists already, this function will update the resource instead.
|
||||
func CreateOrUpdateServiceAccount(client clientset.Interface, sa *v1.ServiceAccount) error {
|
||||
if _, err := client.CoreV1().ServiceAccounts(sa.ObjectMeta.Namespace).Create(sa); err != nil {
|
||||
// Note: We don't run .Update here afterwards as that's probably not required
|
||||
// Only thing that could be updated is annotations/labels in .metadata, but we don't use that currently
|
||||
if !apierrors.IsAlreadyExists(err) {
|
||||
return fmt.Errorf("unable to create serviceaccount: %v", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateOrUpdateDeployment creates a Deployment if the target resource doesn't exist. If the resource exists already, this function will update the resource instead.
|
||||
func CreateOrUpdateDeployment(client clientset.Interface, deploy *apps.Deployment) error {
|
||||
if _, err := client.AppsV1().Deployments(deploy.ObjectMeta.Namespace).Create(deploy); err != nil {
|
||||
if !apierrors.IsAlreadyExists(err) {
|
||||
return fmt.Errorf("unable to create deployment: %v", err)
|
||||
}
|
||||
|
||||
if _, err := client.AppsV1().Deployments(deploy.ObjectMeta.Namespace).Update(deploy); err != nil {
|
||||
return fmt.Errorf("unable to update deployment: %v", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateOrUpdateDaemonSet creates a DaemonSet if the target resource doesn't exist. If the resource exists already, this function will update the resource instead.
|
||||
func CreateOrUpdateDaemonSet(client clientset.Interface, ds *apps.DaemonSet) error {
|
||||
if _, err := client.AppsV1().DaemonSets(ds.ObjectMeta.Namespace).Create(ds); err != nil {
|
||||
if !apierrors.IsAlreadyExists(err) {
|
||||
return fmt.Errorf("unable to create daemonset: %v", err)
|
||||
}
|
||||
|
||||
if _, err := client.AppsV1().DaemonSets(ds.ObjectMeta.Namespace).Update(ds); err != nil {
|
||||
return fmt.Errorf("unable to update daemonset: %v", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteDaemonSetForeground deletes the specified DaemonSet in foreground mode; i.e. it blocks until/makes sure all the managed Pods are deleted
|
||||
func DeleteDaemonSetForeground(client clientset.Interface, namespace, name string) error {
|
||||
foregroundDelete := metav1.DeletePropagationForeground
|
||||
deleteOptions := &metav1.DeleteOptions{
|
||||
PropagationPolicy: &foregroundDelete,
|
||||
}
|
||||
return client.AppsV1().DaemonSets(namespace).Delete(name, deleteOptions)
|
||||
}
|
||||
|
||||
// DeleteDeploymentForeground deletes the specified Deployment in foreground mode; i.e. it blocks until/makes sure all the managed Pods are deleted
|
||||
func DeleteDeploymentForeground(client clientset.Interface, namespace, name string) error {
|
||||
foregroundDelete := metav1.DeletePropagationForeground
|
||||
deleteOptions := &metav1.DeleteOptions{
|
||||
PropagationPolicy: &foregroundDelete,
|
||||
}
|
||||
return client.AppsV1().Deployments(namespace).Delete(name, deleteOptions)
|
||||
}
|
||||
|
||||
// CreateOrUpdateRole creates a Role if the target resource doesn't exist. If the resource exists already, this function will update the resource instead.
|
||||
func CreateOrUpdateRole(client clientset.Interface, role *rbac.Role) error {
|
||||
if _, err := client.RbacV1().Roles(role.ObjectMeta.Namespace).Create(role); err != nil {
|
||||
if !apierrors.IsAlreadyExists(err) {
|
||||
return fmt.Errorf("unable to create RBAC role: %v", err)
|
||||
}
|
||||
|
||||
if _, err := client.RbacV1().Roles(role.ObjectMeta.Namespace).Update(role); err != nil {
|
||||
return fmt.Errorf("unable to update RBAC role: %v", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateOrUpdateRoleBinding creates a RoleBinding if the target resource doesn't exist. If the resource exists already, this function will update the resource instead.
|
||||
func CreateOrUpdateRoleBinding(client clientset.Interface, roleBinding *rbac.RoleBinding) error {
|
||||
if _, err := client.RbacV1().RoleBindings(roleBinding.ObjectMeta.Namespace).Create(roleBinding); err != nil {
|
||||
if !apierrors.IsAlreadyExists(err) {
|
||||
return fmt.Errorf("unable to create RBAC rolebinding: %v", err)
|
||||
}
|
||||
|
||||
if _, err := client.RbacV1().RoleBindings(roleBinding.ObjectMeta.Namespace).Update(roleBinding); err != nil {
|
||||
return fmt.Errorf("unable to update RBAC rolebinding: %v", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateOrUpdateClusterRole creates a ClusterRole if the target resource doesn't exist. If the resource exists already, this function will update the resource instead.
|
||||
func CreateOrUpdateClusterRole(client clientset.Interface, clusterRole *rbac.ClusterRole) error {
|
||||
if _, err := client.RbacV1().ClusterRoles().Create(clusterRole); err != nil {
|
||||
if !apierrors.IsAlreadyExists(err) {
|
||||
return fmt.Errorf("unable to create RBAC clusterrole: %v", err)
|
||||
}
|
||||
|
||||
if _, err := client.RbacV1().ClusterRoles().Update(clusterRole); err != nil {
|
||||
return fmt.Errorf("unable to update RBAC clusterrole: %v", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateOrUpdateClusterRoleBinding creates a ClusterRoleBinding if the target resource doesn't exist. If the resource exists already, this function will update the resource instead.
|
||||
func CreateOrUpdateClusterRoleBinding(client clientset.Interface, clusterRoleBinding *rbac.ClusterRoleBinding) error {
|
||||
if _, err := client.RbacV1().ClusterRoleBindings().Create(clusterRoleBinding); err != nil {
|
||||
if !apierrors.IsAlreadyExists(err) {
|
||||
return fmt.Errorf("unable to create RBAC clusterrolebinding: %v", err)
|
||||
}
|
||||
|
||||
if _, err := client.RbacV1().ClusterRoleBindings().Update(clusterRoleBinding); err != nil {
|
||||
return fmt.Errorf("unable to update RBAC clusterrolebinding: %v", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// PatchNode tries to patch a node using the following client, executing patchFn for the actual mutating logic
|
||||
func PatchNode(client clientset.Interface, nodeName string, patchFn func(*v1.Node)) error {
|
||||
// Loop on every false return. Return with an error if raised. Exit successfully if true is returned.
|
||||
return wait.Poll(constants.APICallRetryInterval, constants.PatchNodeTimeout, func() (bool, error) {
|
||||
// First get the node object
|
||||
n, err := client.CoreV1().Nodes().Get(nodeName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// The node may appear to have no labels at first,
|
||||
// so we wait for it to get hostname label.
|
||||
if _, found := n.ObjectMeta.Labels[kubeletapis.LabelHostname]; !found {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
oldData, err := json.Marshal(n)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Execute the mutating function
|
||||
patchFn(n)
|
||||
|
||||
newData, err := json.Marshal(n)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
patchBytes, err := strategicpatch.CreateTwoWayMergePatch(oldData, newData, v1.Node{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if _, err := client.CoreV1().Nodes().Patch(n.Name, types.StrategicMergePatchType, patchBytes); err != nil {
|
||||
if apierrors.IsConflict(err) {
|
||||
fmt.Println("[patchnode] Temporarily unable to update node metadata due to conflict (will retry)")
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
}
|
160
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient/init_dryrun.go
generated
vendored
160
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient/init_dryrun.go
generated
vendored
@ -1,160 +0,0 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package apiclient
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
core "k8s.io/client-go/testing"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||
"k8s.io/kubernetes/pkg/registry/core/service/ipallocator"
|
||||
)
|
||||
|
||||
// InitDryRunGetter implements the DryRunGetter interface and can be used to GET/LIST values in the dryrun fake clientset
|
||||
// Need to handle these routes in a special manner:
|
||||
// - GET /default/services/kubernetes -- must return a valid Service
|
||||
// - GET /clusterrolebindings/system:nodes -- can safely return a NotFound error
|
||||
// - GET /kube-system/secrets/bootstrap-token-* -- can safely return a NotFound error
|
||||
// - GET /nodes/<node-name> -- must return a valid Node
|
||||
// - ...all other, unknown GETs/LISTs will be logged
|
||||
type InitDryRunGetter struct {
|
||||
masterName string
|
||||
serviceSubnet string
|
||||
}
|
||||
|
||||
// InitDryRunGetter should implement the DryRunGetter interface
|
||||
var _ DryRunGetter = &InitDryRunGetter{}
|
||||
|
||||
// NewInitDryRunGetter creates a new instance of the InitDryRunGetter struct
|
||||
func NewInitDryRunGetter(masterName string, serviceSubnet string) *InitDryRunGetter {
|
||||
return &InitDryRunGetter{
|
||||
masterName: masterName,
|
||||
serviceSubnet: serviceSubnet,
|
||||
}
|
||||
}
|
||||
|
||||
// HandleGetAction handles GET actions to the dryrun clientset this interface supports
|
||||
func (idr *InitDryRunGetter) HandleGetAction(action core.GetAction) (bool, runtime.Object, error) {
|
||||
funcs := []func(core.GetAction) (bool, runtime.Object, error){
|
||||
idr.handleKubernetesService,
|
||||
idr.handleGetNode,
|
||||
idr.handleSystemNodesClusterRoleBinding,
|
||||
idr.handleGetBootstrapToken,
|
||||
}
|
||||
for _, f := range funcs {
|
||||
handled, obj, err := f(action)
|
||||
if handled {
|
||||
return handled, obj, err
|
||||
}
|
||||
}
|
||||
|
||||
return false, nil, nil
|
||||
}
|
||||
|
||||
// HandleListAction handles GET actions to the dryrun clientset this interface supports.
|
||||
// Currently there are no known LIST calls during kubeadm init this code has to take care of.
|
||||
func (idr *InitDryRunGetter) HandleListAction(action core.ListAction) (bool, runtime.Object, error) {
|
||||
return false, nil, nil
|
||||
}
|
||||
|
||||
// handleKubernetesService returns a faked Kubernetes service in order to be able to continue running kubeadm init.
|
||||
// The kube-dns addon code GETs the kubernetes service in order to extract the service subnet
|
||||
func (idr *InitDryRunGetter) handleKubernetesService(action core.GetAction) (bool, runtime.Object, error) {
|
||||
if action.GetName() != "kubernetes" || action.GetNamespace() != metav1.NamespaceDefault || action.GetResource().Resource != "services" {
|
||||
// We can't handle this event
|
||||
return false, nil, nil
|
||||
}
|
||||
|
||||
_, svcSubnet, err := net.ParseCIDR(idr.serviceSubnet)
|
||||
if err != nil {
|
||||
return true, nil, fmt.Errorf("error parsing CIDR %q: %v", idr.serviceSubnet, err)
|
||||
}
|
||||
|
||||
internalAPIServerVirtualIP, err := ipallocator.GetIndexedIP(svcSubnet, 1)
|
||||
if err != nil {
|
||||
return true, nil, fmt.Errorf("unable to get first IP address from the given CIDR (%s): %v", svcSubnet.String(), err)
|
||||
}
|
||||
|
||||
// The only used field of this Service object is the ClusterIP, which kube-dns uses to calculate its own IP
|
||||
return true, &v1.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "kubernetes",
|
||||
Namespace: metav1.NamespaceDefault,
|
||||
Labels: map[string]string{
|
||||
"component": "apiserver",
|
||||
"provider": "kubernetes",
|
||||
},
|
||||
},
|
||||
Spec: v1.ServiceSpec{
|
||||
ClusterIP: internalAPIServerVirtualIP.String(),
|
||||
Ports: []v1.ServicePort{
|
||||
{
|
||||
Name: "https",
|
||||
Port: 443,
|
||||
TargetPort: intstr.FromInt(6443),
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// handleGetNode returns a fake node object for the purpose of moving kubeadm init forwards.
|
||||
func (idr *InitDryRunGetter) handleGetNode(action core.GetAction) (bool, runtime.Object, error) {
|
||||
if action.GetName() != idr.masterName || action.GetResource().Resource != "nodes" {
|
||||
// We can't handle this event
|
||||
return false, nil, nil
|
||||
}
|
||||
|
||||
return true, &v1.Node{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: idr.masterName,
|
||||
Labels: map[string]string{
|
||||
"kubernetes.io/hostname": idr.masterName,
|
||||
},
|
||||
Annotations: map[string]string{},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// handleSystemNodesClusterRoleBinding handles the GET call to the system:nodes clusterrolebinding
|
||||
func (idr *InitDryRunGetter) handleSystemNodesClusterRoleBinding(action core.GetAction) (bool, runtime.Object, error) {
|
||||
if action.GetName() != constants.NodesClusterRoleBinding || action.GetResource().Resource != "clusterrolebindings" {
|
||||
// We can't handle this event
|
||||
return false, nil, nil
|
||||
}
|
||||
// We can safely return a NotFound error here as the code will just proceed normally and don't care about modifying this clusterrolebinding
|
||||
// This can only happen on an upgrade; and in that case the ClientBackedDryRunGetter impl will be used
|
||||
return true, nil, apierrors.NewNotFound(action.GetResource().GroupResource(), "clusterrolebinding not found")
|
||||
}
|
||||
|
||||
// handleGetBootstrapToken handles the case where kubeadm init creates the default token; and the token code GETs the
|
||||
// bootstrap token secret first in order to check if it already exists
|
||||
func (idr *InitDryRunGetter) handleGetBootstrapToken(action core.GetAction) (bool, runtime.Object, error) {
|
||||
if !strings.HasPrefix(action.GetName(), "bootstrap-token-") || action.GetNamespace() != metav1.NamespaceSystem || action.GetResource().Resource != "secrets" {
|
||||
// We can't handle this event
|
||||
return false, nil, nil
|
||||
}
|
||||
// We can safely return a NotFound error here as the code will just proceed normally and create the Bootstrap Token
|
||||
return true, nil, apierrors.NewNotFound(action.GetResource().GroupResource(), "secret not found")
|
||||
}
|
126
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient/init_dryrun_test.go
generated
vendored
126
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient/init_dryrun_test.go
generated
vendored
@ -1,126 +0,0 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package apiclient
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
rbac "k8s.io/api/rbac/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
core "k8s.io/client-go/testing"
|
||||
)
|
||||
|
||||
func TestHandleGetAction(t *testing.T) {
|
||||
masterName := "master-foo"
|
||||
serviceSubnet := "10.96.0.1/12"
|
||||
idr := NewInitDryRunGetter(masterName, serviceSubnet)
|
||||
|
||||
var tests = []struct {
|
||||
action core.GetActionImpl
|
||||
expectedHandled bool
|
||||
expectedObjectJSON []byte
|
||||
expectedErr bool
|
||||
}{
|
||||
{
|
||||
action: core.NewGetAction(schema.GroupVersionResource{Version: "v1", Resource: "services"}, "default", "kubernetes"),
|
||||
expectedHandled: true,
|
||||
expectedObjectJSON: []byte(`{"metadata":{"name":"kubernetes","namespace":"default","creationTimestamp":null,"labels":{"component":"apiserver","provider":"kubernetes"}},"spec":{"ports":[{"name":"https","port":443,"targetPort":6443}],"clusterIP":"10.96.0.1"},"status":{"loadBalancer":{}}}`),
|
||||
expectedErr: false,
|
||||
},
|
||||
{
|
||||
action: core.NewRootGetAction(schema.GroupVersionResource{Version: "v1", Resource: "nodes"}, masterName),
|
||||
expectedHandled: true,
|
||||
expectedObjectJSON: []byte(`{"metadata":{"name":"master-foo","creationTimestamp":null,"labels":{"kubernetes.io/hostname":"master-foo"}},"spec":{},"status":{"daemonEndpoints":{"kubeletEndpoint":{"Port":0}},"nodeInfo":{"machineID":"","systemUUID":"","bootID":"","kernelVersion":"","osImage":"","containerRuntimeVersion":"","kubeletVersion":"","kubeProxyVersion":"","operatingSystem":"","architecture":""}}}`),
|
||||
expectedErr: false,
|
||||
},
|
||||
{
|
||||
action: core.NewRootGetAction(schema.GroupVersionResource{Group: rbac.GroupName, Version: rbac.SchemeGroupVersion.Version, Resource: "clusterrolebindings"}, "system:node"),
|
||||
expectedHandled: true,
|
||||
expectedObjectJSON: []byte(``),
|
||||
expectedErr: true, // we expect a NotFound error here
|
||||
},
|
||||
{
|
||||
action: core.NewGetAction(schema.GroupVersionResource{Version: "v1", Resource: "secrets"}, "kube-system", "bootstrap-token-abcdef"),
|
||||
expectedHandled: true,
|
||||
expectedObjectJSON: []byte(``),
|
||||
expectedErr: true, // we expect a NotFound error here
|
||||
},
|
||||
{ // an ask for a kubernetes service in the _kube-system_ ns should not be answered
|
||||
action: core.NewGetAction(schema.GroupVersionResource{Version: "v1", Resource: "services"}, "kube-system", "kubernetes"),
|
||||
expectedHandled: false,
|
||||
expectedObjectJSON: []byte(``),
|
||||
expectedErr: false,
|
||||
},
|
||||
{ // an ask for an other service than kubernetes should not be answered
|
||||
action: core.NewGetAction(schema.GroupVersionResource{Version: "v1", Resource: "services"}, "default", "my-other-service"),
|
||||
expectedHandled: false,
|
||||
expectedObjectJSON: []byte(``),
|
||||
expectedErr: false,
|
||||
},
|
||||
{ // an ask for an other node than the master should not be answered
|
||||
action: core.NewRootGetAction(schema.GroupVersionResource{Version: "v1", Resource: "nodes"}, "other-node"),
|
||||
expectedHandled: false,
|
||||
expectedObjectJSON: []byte(``),
|
||||
expectedErr: false,
|
||||
},
|
||||
{ // an ask for a secret in any other ns than kube-system should not be answered
|
||||
action: core.NewGetAction(schema.GroupVersionResource{Version: "v1", Resource: "secrets"}, "default", "bootstrap-token-abcdef"),
|
||||
expectedHandled: false,
|
||||
expectedObjectJSON: []byte(``),
|
||||
expectedErr: false,
|
||||
},
|
||||
}
|
||||
for _, rt := range tests {
|
||||
handled, obj, actualErr := idr.HandleGetAction(rt.action)
|
||||
objBytes := []byte(``)
|
||||
if obj != nil {
|
||||
var err error
|
||||
objBytes, err = json.Marshal(obj)
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't marshal returned object")
|
||||
}
|
||||
}
|
||||
|
||||
if handled != rt.expectedHandled {
|
||||
t.Errorf(
|
||||
"failed HandleGetAction:\n\texpected handled: %t\n\t actual: %t %v",
|
||||
rt.expectedHandled,
|
||||
handled,
|
||||
rt.action,
|
||||
)
|
||||
}
|
||||
|
||||
if !bytes.Equal(objBytes, rt.expectedObjectJSON) {
|
||||
t.Errorf(
|
||||
"failed HandleGetAction:\n\texpected object: %q\n\t actual: %q",
|
||||
rt.expectedObjectJSON,
|
||||
objBytes,
|
||||
)
|
||||
}
|
||||
|
||||
if (actualErr != nil) != rt.expectedErr {
|
||||
t.Errorf(
|
||||
"failed HandleGetAction:\n\texpected error: %t\n\t actual: %t %v",
|
||||
rt.expectedErr,
|
||||
(actualErr != nil),
|
||||
rt.action,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
258
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient/wait.go
generated
vendored
258
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient/wait.go
generated
vendored
@ -1,258 +0,0 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package apiclient
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
netutil "k8s.io/apimachinery/pkg/util/net"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||
kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
|
||||
)
|
||||
|
||||
// Waiter is an interface for waiting for criteria in Kubernetes to happen
|
||||
type Waiter interface {
|
||||
// WaitForAPI waits for the API Server's /healthz endpoint to become "ok"
|
||||
WaitForAPI() error
|
||||
// WaitForPodsWithLabel waits for Pods in the kube-system namespace to become Ready
|
||||
WaitForPodsWithLabel(kvLabel string) error
|
||||
// WaitForPodToDisappear waits for the given Pod in the kube-system namespace to be deleted
|
||||
WaitForPodToDisappear(staticPodName string) error
|
||||
// WaitForStaticPodSingleHash fetches sha256 hash for the control plane static pod
|
||||
WaitForStaticPodSingleHash(nodeName string, component string) (string, error)
|
||||
// WaitForStaticPodHashChange waits for the given static pod component's static pod hash to get updated.
|
||||
// By doing that we can be sure that the kubelet has restarted the given Static Pod
|
||||
WaitForStaticPodHashChange(nodeName, component, previousHash string) error
|
||||
// WaitForStaticPodControlPlaneHashes fetches sha256 hashes for the control plane static pods
|
||||
WaitForStaticPodControlPlaneHashes(nodeName string) (map[string]string, error)
|
||||
// WaitForHealthyKubelet blocks until the kubelet /healthz endpoint returns 'ok'
|
||||
WaitForHealthyKubelet(initalTimeout time.Duration, healthzEndpoint string) error
|
||||
// SetTimeout adjusts the timeout to the specified duration
|
||||
SetTimeout(timeout time.Duration)
|
||||
}
|
||||
|
||||
// KubeWaiter is an implementation of Waiter that is backed by a Kubernetes client
|
||||
type KubeWaiter struct {
|
||||
client clientset.Interface
|
||||
timeout time.Duration
|
||||
writer io.Writer
|
||||
}
|
||||
|
||||
// NewKubeWaiter returns a new Waiter object that talks to the given Kubernetes cluster
|
||||
func NewKubeWaiter(client clientset.Interface, timeout time.Duration, writer io.Writer) Waiter {
|
||||
return &KubeWaiter{
|
||||
client: client,
|
||||
timeout: timeout,
|
||||
writer: writer,
|
||||
}
|
||||
}
|
||||
|
||||
// WaitForAPI waits for the API Server's /healthz endpoint to report "ok"
|
||||
func (w *KubeWaiter) WaitForAPI() error {
|
||||
start := time.Now()
|
||||
return wait.PollImmediate(constants.APICallRetryInterval, w.timeout, func() (bool, error) {
|
||||
healthStatus := 0
|
||||
w.client.Discovery().RESTClient().Get().AbsPath("/healthz").Do().StatusCode(&healthStatus)
|
||||
if healthStatus != http.StatusOK {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
fmt.Printf("[apiclient] All control plane components are healthy after %f seconds\n", time.Since(start).Seconds())
|
||||
return true, nil
|
||||
})
|
||||
}
|
||||
|
||||
// WaitForPodsWithLabel will lookup pods with the given label and wait until they are all
|
||||
// reporting status as running.
|
||||
func (w *KubeWaiter) WaitForPodsWithLabel(kvLabel string) error {
|
||||
|
||||
lastKnownPodNumber := -1
|
||||
return wait.PollImmediate(constants.APICallRetryInterval, w.timeout, func() (bool, error) {
|
||||
listOpts := metav1.ListOptions{LabelSelector: kvLabel}
|
||||
pods, err := w.client.CoreV1().Pods(metav1.NamespaceSystem).List(listOpts)
|
||||
if err != nil {
|
||||
fmt.Fprintf(w.writer, "[apiclient] Error getting Pods with label selector %q [%v]\n", kvLabel, err)
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if lastKnownPodNumber != len(pods.Items) {
|
||||
fmt.Fprintf(w.writer, "[apiclient] Found %d Pods for label selector %s\n", len(pods.Items), kvLabel)
|
||||
lastKnownPodNumber = len(pods.Items)
|
||||
}
|
||||
|
||||
if len(pods.Items) == 0 {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
for _, pod := range pods.Items {
|
||||
if pod.Status.Phase != v1.PodRunning {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
}
|
||||
|
||||
// WaitForPodToDisappear blocks until it timeouts or gets a "NotFound" response from the API Server when getting the Static Pod in question
|
||||
func (w *KubeWaiter) WaitForPodToDisappear(podName string) error {
|
||||
return wait.PollImmediate(constants.APICallRetryInterval, w.timeout, func() (bool, error) {
|
||||
_, err := w.client.CoreV1().Pods(metav1.NamespaceSystem).Get(podName, metav1.GetOptions{})
|
||||
if apierrors.IsNotFound(err) {
|
||||
fmt.Printf("[apiclient] The old Pod %q is now removed (which is desired)\n", podName)
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
})
|
||||
}
|
||||
|
||||
// WaitForHealthyKubelet blocks until the kubelet /healthz endpoint returns 'ok'
|
||||
func (w *KubeWaiter) WaitForHealthyKubelet(initalTimeout time.Duration, healthzEndpoint string) error {
|
||||
time.Sleep(initalTimeout)
|
||||
return TryRunCommand(func() error {
|
||||
client := &http.Client{Transport: netutil.SetOldTransportDefaults(&http.Transport{})}
|
||||
resp, err := client.Get(healthzEndpoint)
|
||||
if err != nil {
|
||||
fmt.Printf("[kubelet-check] It seems like the kubelet isn't running or healthy.\n")
|
||||
fmt.Printf("[kubelet-check] The HTTP call equal to 'curl -sSL %s' failed with error: %v.\n", healthzEndpoint, err)
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
fmt.Printf("[kubelet-check] It seems like the kubelet isn't running or healthy.")
|
||||
fmt.Printf("[kubelet-check] The HTTP call equal to 'curl -sSL %s' returned HTTP code %d\n", healthzEndpoint, resp.StatusCode)
|
||||
return fmt.Errorf("the kubelet healthz endpoint is unhealthy")
|
||||
}
|
||||
return nil
|
||||
}, 5) // a failureThreshold of five means waiting for a total of 155 seconds
|
||||
}
|
||||
|
||||
// SetTimeout adjusts the timeout to the specified duration
|
||||
func (w *KubeWaiter) SetTimeout(timeout time.Duration) {
|
||||
w.timeout = timeout
|
||||
}
|
||||
|
||||
// WaitForStaticPodControlPlaneHashes blocks until it timeouts or gets a hash map for all components and their Static Pods
|
||||
func (w *KubeWaiter) WaitForStaticPodControlPlaneHashes(nodeName string) (map[string]string, error) {
|
||||
|
||||
componentHash := ""
|
||||
var err error
|
||||
mirrorPodHashes := map[string]string{}
|
||||
for _, component := range constants.MasterComponents {
|
||||
err = wait.PollImmediate(constants.APICallRetryInterval, w.timeout, func() (bool, error) {
|
||||
componentHash, err = getStaticPodSingleHash(w.client, nodeName, component)
|
||||
if err != nil {
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mirrorPodHashes[component] = componentHash
|
||||
}
|
||||
|
||||
return mirrorPodHashes, nil
|
||||
}
|
||||
|
||||
// WaitForStaticPodSingleHash blocks until it timeouts or gets a hash for a single component and its Static Pod
|
||||
func (w *KubeWaiter) WaitForStaticPodSingleHash(nodeName string, component string) (string, error) {
|
||||
|
||||
componentPodHash := ""
|
||||
var err error
|
||||
err = wait.PollImmediate(constants.APICallRetryInterval, w.timeout, func() (bool, error) {
|
||||
componentPodHash, err = getStaticPodSingleHash(w.client, nodeName, component)
|
||||
if err != nil {
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
})
|
||||
|
||||
return componentPodHash, err
|
||||
}
|
||||
|
||||
// WaitForStaticPodHashChange blocks until it timeouts or notices that the Mirror Pod (for the Static Pod, respectively) has changed
|
||||
// This implicitly means this function blocks until the kubelet has restarted the Static Pod in question
|
||||
func (w *KubeWaiter) WaitForStaticPodHashChange(nodeName, component, previousHash string) error {
|
||||
return wait.PollImmediate(constants.APICallRetryInterval, w.timeout, func() (bool, error) {
|
||||
|
||||
hash, err := getStaticPodSingleHash(w.client, nodeName, component)
|
||||
if err != nil {
|
||||
return false, nil
|
||||
}
|
||||
// We should continue polling until the UID changes
|
||||
if hash == previousHash {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
}
|
||||
|
||||
// getStaticPodControlPlaneHashes computes hashes for all the control plane's Static Pod resources
|
||||
func getStaticPodControlPlaneHashes(client clientset.Interface, nodeName string) (map[string]string, error) {
|
||||
|
||||
mirrorPodHashes := map[string]string{}
|
||||
for _, component := range constants.MasterComponents {
|
||||
hash, err := getStaticPodSingleHash(client, nodeName, component)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mirrorPodHashes[component] = hash
|
||||
}
|
||||
return mirrorPodHashes, nil
|
||||
}
|
||||
|
||||
// getStaticSinglePodHash computes hashes for a single Static Pod resource
|
||||
func getStaticPodSingleHash(client clientset.Interface, nodeName string, component string) (string, error) {
|
||||
|
||||
staticPodName := fmt.Sprintf("%s-%s", component, nodeName)
|
||||
staticPod, err := client.CoreV1().Pods(metav1.NamespaceSystem).Get(staticPodName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
staticPodHash := staticPod.Annotations[kubetypes.ConfigHashAnnotationKey]
|
||||
fmt.Printf("Static pod: %s hash: %s\n", staticPodName, staticPodHash)
|
||||
return staticPodHash, nil
|
||||
}
|
||||
|
||||
// TryRunCommand runs a function a maximum of failureThreshold times, and retries on error. If failureThreshold is hit; the last error is returned
|
||||
func TryRunCommand(f func() error, failureThreshold int) error {
|
||||
backoff := wait.Backoff{
|
||||
Duration: 5 * time.Second,
|
||||
Factor: 2, // double the timeout for every failure
|
||||
Steps: failureThreshold,
|
||||
}
|
||||
return wait.ExponentialBackoff(backoff, func() (bool, error) {
|
||||
err := f()
|
||||
if err != nil {
|
||||
// Retry until the timeout
|
||||
return false, nil
|
||||
}
|
||||
// The last f() call was a success, return cleanly
|
||||
return true, nil
|
||||
})
|
||||
}
|
111
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/arguments.go
generated
vendored
111
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/arguments.go
generated
vendored
@ -1,111 +0,0 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// BuildArgumentListFromMap takes two string-string maps, one with the base arguments and one
|
||||
// with optional override arguments. In the return list override arguments will precede base
|
||||
// arguments
|
||||
func BuildArgumentListFromMap(baseArguments map[string]string, overrideArguments map[string]string) []string {
|
||||
var command []string
|
||||
var keys []string
|
||||
for k := range overrideArguments {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
for _, k := range keys {
|
||||
v := overrideArguments[k]
|
||||
// values of "" are allowed as well
|
||||
command = append(command, fmt.Sprintf("--%s=%s", k, v))
|
||||
}
|
||||
keys = []string{}
|
||||
for k := range baseArguments {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
for _, k := range keys {
|
||||
v := baseArguments[k]
|
||||
if _, overrideExists := overrideArguments[k]; !overrideExists {
|
||||
command = append(command, fmt.Sprintf("--%s=%s", k, v))
|
||||
}
|
||||
}
|
||||
return command
|
||||
}
|
||||
|
||||
// ParseArgumentListToMap parses a CLI argument list in the form "--foo=bar" to a string-string map
|
||||
func ParseArgumentListToMap(arguments []string) map[string]string {
|
||||
resultingMap := map[string]string{}
|
||||
for i, arg := range arguments {
|
||||
key, val, err := parseArgument(arg)
|
||||
|
||||
// Ignore if the first argument doesn't satisfy the criteria, it's most often the binary name
|
||||
// Warn in all other cases, but don't error out. This can happen only if the user has edited the argument list by hand, so they might know what they are doing
|
||||
if err != nil {
|
||||
if i != 0 {
|
||||
fmt.Printf("[kubeadm] WARNING: The component argument %q could not be parsed correctly. The argument must be of the form %q. Skipping...", arg, "--")
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
resultingMap[key] = val
|
||||
}
|
||||
return resultingMap
|
||||
}
|
||||
|
||||
// ReplaceArgument gets a command list; converts it to a map for easier modification, runs the provided function that
|
||||
// returns a new modified map, and then converts the map back to a command string slice
|
||||
func ReplaceArgument(command []string, argMutateFunc func(map[string]string) map[string]string) []string {
|
||||
argMap := ParseArgumentListToMap(command)
|
||||
|
||||
// Save the first command (the executable) if we're sure it's not an argument (i.e. no --)
|
||||
var newCommand []string
|
||||
if len(command) > 0 && !strings.HasPrefix(command[0], "--") {
|
||||
newCommand = append(newCommand, command[0])
|
||||
}
|
||||
newArgMap := argMutateFunc(argMap)
|
||||
newCommand = append(newCommand, BuildArgumentListFromMap(newArgMap, map[string]string{})...)
|
||||
return newCommand
|
||||
}
|
||||
|
||||
// parseArgument parses the argument "--foo=bar" to "foo" and "bar"
|
||||
func parseArgument(arg string) (string, string, error) {
|
||||
if !strings.HasPrefix(arg, "--") {
|
||||
return "", "", fmt.Errorf("the argument should start with '--'")
|
||||
}
|
||||
if !strings.Contains(arg, "=") {
|
||||
return "", "", fmt.Errorf("the argument should have a '=' between the flag and the value")
|
||||
}
|
||||
// Remove the starting --
|
||||
arg = strings.TrimPrefix(arg, "--")
|
||||
// Split the string on =. Return only two substrings, since we want only key/value, but the value can include '=' as well
|
||||
keyvalSlice := strings.SplitN(arg, "=", 2)
|
||||
|
||||
// Make sure both a key and value is present
|
||||
if len(keyvalSlice) != 2 {
|
||||
return "", "", fmt.Errorf("the argument must have both a key and a value")
|
||||
}
|
||||
if len(keyvalSlice[0]) == 0 {
|
||||
return "", "", fmt.Errorf("the argument must have a key")
|
||||
}
|
||||
|
||||
return keyvalSlice[0], keyvalSlice[1], nil
|
||||
}
|
339
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/arguments_test.go
generated
vendored
339
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/arguments_test.go
generated
vendored
@ -1,339 +0,0 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBuildArgumentListFromMap(t *testing.T) {
|
||||
var tests = []struct {
|
||||
base map[string]string
|
||||
overrides map[string]string
|
||||
expected []string
|
||||
}{
|
||||
{ // override an argument from the base
|
||||
base: map[string]string{
|
||||
"admission-control": "NamespaceLifecycle",
|
||||
"insecure-bind-address": "127.0.0.1",
|
||||
"allow-privileged": "true",
|
||||
},
|
||||
overrides: map[string]string{
|
||||
"admission-control": "NamespaceLifecycle,LimitRanger",
|
||||
},
|
||||
expected: []string{
|
||||
"--admission-control=NamespaceLifecycle,LimitRanger",
|
||||
"--allow-privileged=true",
|
||||
"--insecure-bind-address=127.0.0.1",
|
||||
},
|
||||
},
|
||||
{ // add an argument that is not in base
|
||||
base: map[string]string{
|
||||
"insecure-bind-address": "127.0.0.1",
|
||||
"allow-privileged": "true",
|
||||
},
|
||||
overrides: map[string]string{
|
||||
"admission-control": "NamespaceLifecycle,LimitRanger",
|
||||
},
|
||||
expected: []string{
|
||||
"--admission-control=NamespaceLifecycle,LimitRanger",
|
||||
"--allow-privileged=true",
|
||||
"--insecure-bind-address=127.0.0.1",
|
||||
},
|
||||
},
|
||||
{ // allow empty strings in base
|
||||
base: map[string]string{
|
||||
"insecure-bind-address": "127.0.0.1",
|
||||
"allow-privileged": "true",
|
||||
"something-that-allows-empty-string": "",
|
||||
},
|
||||
overrides: map[string]string{
|
||||
"admission-control": "NamespaceLifecycle,LimitRanger",
|
||||
},
|
||||
expected: []string{
|
||||
"--admission-control=NamespaceLifecycle,LimitRanger",
|
||||
"--allow-privileged=true",
|
||||
"--insecure-bind-address=127.0.0.1",
|
||||
"--something-that-allows-empty-string=",
|
||||
},
|
||||
},
|
||||
{ // allow empty strings in overrides
|
||||
base: map[string]string{
|
||||
"insecure-bind-address": "127.0.0.1",
|
||||
"allow-privileged": "true",
|
||||
"something-that-allows-empty-string": "foo",
|
||||
},
|
||||
overrides: map[string]string{
|
||||
"admission-control": "NamespaceLifecycle,LimitRanger",
|
||||
"something-that-allows-empty-string": "",
|
||||
},
|
||||
expected: []string{
|
||||
"--admission-control=NamespaceLifecycle,LimitRanger",
|
||||
"--something-that-allows-empty-string=",
|
||||
"--allow-privileged=true",
|
||||
"--insecure-bind-address=127.0.0.1",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, rt := range tests {
|
||||
actual := BuildArgumentListFromMap(rt.base, rt.overrides)
|
||||
if !reflect.DeepEqual(actual, rt.expected) {
|
||||
t.Errorf("failed BuildArgumentListFromMap:\nexpected:\n%v\nsaw:\n%v", rt.expected, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseArgumentListToMap(t *testing.T) {
|
||||
var tests = []struct {
|
||||
args []string
|
||||
expectedMap map[string]string
|
||||
}{
|
||||
{
|
||||
// normal case
|
||||
args: []string{
|
||||
"--admission-control=NamespaceLifecycle,LimitRanger",
|
||||
"--insecure-bind-address=127.0.0.1",
|
||||
"--allow-privileged=true",
|
||||
},
|
||||
expectedMap: map[string]string{
|
||||
"admission-control": "NamespaceLifecycle,LimitRanger",
|
||||
"insecure-bind-address": "127.0.0.1",
|
||||
"allow-privileged": "true",
|
||||
},
|
||||
},
|
||||
{
|
||||
// test that feature-gates is working
|
||||
args: []string{
|
||||
"--admission-control=NamespaceLifecycle,LimitRanger",
|
||||
"--insecure-bind-address=127.0.0.1",
|
||||
"--allow-privileged=true",
|
||||
"--feature-gates=EnableFoo=true,EnableBar=false",
|
||||
},
|
||||
expectedMap: map[string]string{
|
||||
"admission-control": "NamespaceLifecycle,LimitRanger",
|
||||
"insecure-bind-address": "127.0.0.1",
|
||||
"allow-privileged": "true",
|
||||
"feature-gates": "EnableFoo=true,EnableBar=false",
|
||||
},
|
||||
},
|
||||
{
|
||||
// test that a binary can be the first arg
|
||||
args: []string{
|
||||
"kube-apiserver",
|
||||
"--admission-control=NamespaceLifecycle,LimitRanger",
|
||||
"--insecure-bind-address=127.0.0.1",
|
||||
"--allow-privileged=true",
|
||||
"--feature-gates=EnableFoo=true,EnableBar=false",
|
||||
},
|
||||
expectedMap: map[string]string{
|
||||
"admission-control": "NamespaceLifecycle,LimitRanger",
|
||||
"insecure-bind-address": "127.0.0.1",
|
||||
"allow-privileged": "true",
|
||||
"feature-gates": "EnableFoo=true,EnableBar=false",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, rt := range tests {
|
||||
actualMap := ParseArgumentListToMap(rt.args)
|
||||
if !reflect.DeepEqual(actualMap, rt.expectedMap) {
|
||||
t.Errorf("failed ParseArgumentListToMap:\nexpected:\n%v\nsaw:\n%v", rt.expectedMap, actualMap)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestReplaceArgument(t *testing.T) {
|
||||
var tests = []struct {
|
||||
args []string
|
||||
mutateFunc func(map[string]string) map[string]string
|
||||
expectedArgs []string
|
||||
}{
|
||||
{
|
||||
// normal case
|
||||
args: []string{
|
||||
"kube-apiserver",
|
||||
"--admission-control=NamespaceLifecycle,LimitRanger",
|
||||
"--insecure-bind-address=127.0.0.1",
|
||||
"--allow-privileged=true",
|
||||
},
|
||||
mutateFunc: func(argMap map[string]string) map[string]string {
|
||||
argMap["admission-control"] = "NamespaceLifecycle,LimitRanger,ResourceQuota"
|
||||
return argMap
|
||||
},
|
||||
expectedArgs: []string{
|
||||
"kube-apiserver",
|
||||
"--admission-control=NamespaceLifecycle,LimitRanger,ResourceQuota",
|
||||
"--insecure-bind-address=127.0.0.1",
|
||||
"--allow-privileged=true",
|
||||
},
|
||||
},
|
||||
{
|
||||
// normal case
|
||||
args: []string{
|
||||
"kube-apiserver",
|
||||
"--admission-control=NamespaceLifecycle,LimitRanger",
|
||||
"--insecure-bind-address=127.0.0.1",
|
||||
"--allow-privileged=true",
|
||||
},
|
||||
mutateFunc: func(argMap map[string]string) map[string]string {
|
||||
argMap["new-arg-here"] = "foo"
|
||||
return argMap
|
||||
},
|
||||
expectedArgs: []string{
|
||||
"kube-apiserver",
|
||||
"--admission-control=NamespaceLifecycle,LimitRanger",
|
||||
"--insecure-bind-address=127.0.0.1",
|
||||
"--allow-privileged=true",
|
||||
"--new-arg-here=foo",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, rt := range tests {
|
||||
actualArgs := ReplaceArgument(rt.args, rt.mutateFunc)
|
||||
sort.Strings(actualArgs)
|
||||
sort.Strings(rt.expectedArgs)
|
||||
if !reflect.DeepEqual(actualArgs, rt.expectedArgs) {
|
||||
t.Errorf("failed ReplaceArgument:\nexpected:\n%v\nsaw:\n%v", rt.expectedArgs, actualArgs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoundtrip(t *testing.T) {
|
||||
var tests = []struct {
|
||||
args []string
|
||||
}{
|
||||
{
|
||||
// normal case
|
||||
args: []string{
|
||||
"--admission-control=NamespaceLifecycle,LimitRanger",
|
||||
"--insecure-bind-address=127.0.0.1",
|
||||
"--allow-privileged=true",
|
||||
},
|
||||
},
|
||||
{
|
||||
// test that feature-gates is working
|
||||
args: []string{
|
||||
"--admission-control=NamespaceLifecycle,LimitRanger",
|
||||
"--insecure-bind-address=127.0.0.1",
|
||||
"--allow-privileged=true",
|
||||
"--feature-gates=EnableFoo=true,EnableBar=false",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, rt := range tests {
|
||||
// These two methods should be each other's opposite functions, test that by chaining the methods and see if you get the same result back
|
||||
actual := BuildArgumentListFromMap(ParseArgumentListToMap(rt.args), map[string]string{})
|
||||
sort.Strings(actual)
|
||||
sort.Strings(rt.args)
|
||||
|
||||
if !reflect.DeepEqual(actual, rt.args) {
|
||||
t.Errorf("failed TestRoundtrip:\nexpected:\n%v\nsaw:\n%v", rt.args, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseArgument(t *testing.T) {
|
||||
var tests = []struct {
|
||||
arg string
|
||||
expectedKey string
|
||||
expectedVal string
|
||||
expectedErr bool
|
||||
}{
|
||||
{
|
||||
// cannot be empty
|
||||
arg: "",
|
||||
expectedErr: true,
|
||||
},
|
||||
{
|
||||
// must contain -- and =
|
||||
arg: "a",
|
||||
expectedErr: true,
|
||||
},
|
||||
{
|
||||
// must contain -- and =
|
||||
arg: "a-z",
|
||||
expectedErr: true,
|
||||
},
|
||||
{
|
||||
// must contain --
|
||||
arg: "a=b",
|
||||
expectedErr: true,
|
||||
},
|
||||
{
|
||||
// must contain a key
|
||||
arg: "--=b",
|
||||
expectedErr: true,
|
||||
},
|
||||
{
|
||||
// can contain key but no value
|
||||
arg: "--a=",
|
||||
expectedKey: "a",
|
||||
expectedVal: "",
|
||||
expectedErr: false,
|
||||
},
|
||||
{
|
||||
// simple case
|
||||
arg: "--a=b",
|
||||
expectedKey: "a",
|
||||
expectedVal: "b",
|
||||
expectedErr: false,
|
||||
},
|
||||
{
|
||||
// keys/values with '-' should be supported
|
||||
arg: "--very-long-flag-name=some-value",
|
||||
expectedKey: "very-long-flag-name",
|
||||
expectedVal: "some-value",
|
||||
expectedErr: false,
|
||||
},
|
||||
{
|
||||
// numbers should be handled correctly
|
||||
arg: "--some-number=0.2",
|
||||
expectedKey: "some-number",
|
||||
expectedVal: "0.2",
|
||||
expectedErr: false,
|
||||
},
|
||||
{
|
||||
// lists should be handled correctly
|
||||
arg: "--admission-control=foo,bar,baz",
|
||||
expectedKey: "admission-control",
|
||||
expectedVal: "foo,bar,baz",
|
||||
expectedErr: false,
|
||||
},
|
||||
{
|
||||
// more than one '=' should be allowed
|
||||
arg: "--feature-gates=EnableFoo=true,EnableBar=false",
|
||||
expectedKey: "feature-gates",
|
||||
expectedVal: "EnableFoo=true,EnableBar=false",
|
||||
expectedErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, rt := range tests {
|
||||
key, val, actual := parseArgument(rt.arg)
|
||||
if (actual != nil) != rt.expectedErr {
|
||||
t.Errorf("failed parseArgument:\nexpected error:\n%t\nsaw error:\n%v", rt.expectedErr, actual)
|
||||
}
|
||||
if (key != rt.expectedKey) || (val != rt.expectedVal) {
|
||||
t.Errorf("failed parseArgument:\nexpected key: %s\nsaw key: %s\nexpected value: %s\nsaw value: %s", rt.expectedKey, key, rt.expectedVal, val)
|
||||
}
|
||||
}
|
||||
}
|
42
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/audit/BUILD
generated
vendored
42
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/audit/BUILD
generated
vendored
@ -1,42 +0,0 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["utils.go"],
|
||||
importpath = "k8s.io/kubernetes/cmd/kubeadm/app/util/audit",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//cmd/kubeadm/app/util: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/apiserver/pkg/apis/audit/install:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/apis/audit/v1beta1:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["utils_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/apis/audit/install:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/apis/audit/v1beta1:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
73
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/audit/utils.go
generated
vendored
73
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/audit/utils.go
generated
vendored
@ -1,73 +0,0 @@
|
||||
/*
|
||||
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 audit
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
"k8s.io/apiserver/pkg/apis/audit/install"
|
||||
auditv1beta1 "k8s.io/apiserver/pkg/apis/audit/v1beta1"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/util"
|
||||
)
|
||||
|
||||
// CreateDefaultAuditLogPolicy writes the default audit log policy to disk.
|
||||
func CreateDefaultAuditLogPolicy(policyFile string) error {
|
||||
policy := auditv1beta1.Policy{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: auditv1beta1.SchemeGroupVersion.String(),
|
||||
Kind: "Policy",
|
||||
},
|
||||
Rules: []auditv1beta1.PolicyRule{
|
||||
{
|
||||
Level: auditv1beta1.LevelMetadata,
|
||||
},
|
||||
},
|
||||
}
|
||||
return writePolicyToDisk(policyFile, &policy)
|
||||
}
|
||||
|
||||
func writePolicyToDisk(policyFile string, policy *auditv1beta1.Policy) error {
|
||||
// creates target folder if not already exists
|
||||
if err := os.MkdirAll(filepath.Dir(policyFile), 0700); err != nil {
|
||||
return fmt.Errorf("failed to create directory %q: %v", filepath.Dir(policyFile), err)
|
||||
}
|
||||
|
||||
scheme := runtime.NewScheme()
|
||||
// Registers the API group with the scheme and adds types to a scheme
|
||||
install.Install(scheme)
|
||||
|
||||
codecs := serializer.NewCodecFactory(scheme)
|
||||
|
||||
// writes the policy to disk
|
||||
serialized, err := util.MarshalToYamlForCodecs(policy, auditv1beta1.SchemeGroupVersion, codecs)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal audit policy to YAML: %v", err)
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(policyFile, serialized, 0600); err != nil {
|
||||
return fmt.Errorf("failed to write audit policy to %v: %v", policyFile, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
65
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/audit/utils_test.go
generated
vendored
65
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/audit/utils_test.go
generated
vendored
@ -1,65 +0,0 @@
|
||||
/*
|
||||
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 audit
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
"k8s.io/apiserver/pkg/apis/audit/install"
|
||||
auditv1beta1 "k8s.io/apiserver/pkg/apis/audit/v1beta1"
|
||||
)
|
||||
|
||||
func cleanup(t *testing.T, path string) {
|
||||
err := os.RemoveAll(path)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to clean up %v: %v", path, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateDefaultAuditLogPolicy(t *testing.T) {
|
||||
// make a tempdir
|
||||
tempDir, err := ioutil.TempDir("/tmp", "audit-test")
|
||||
if err != nil {
|
||||
t.Fatalf("could not create a tempdir: %v", err)
|
||||
}
|
||||
defer cleanup(t, tempDir)
|
||||
auditPolicyFile := filepath.Join(tempDir, "test.yaml")
|
||||
if err = CreateDefaultAuditLogPolicy(auditPolicyFile); err != nil {
|
||||
t.Fatalf("failed to create audit log policy: %v", err)
|
||||
}
|
||||
// turn the audit log back into a policy
|
||||
policyBytes, err := ioutil.ReadFile(auditPolicyFile)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read %v: %v", auditPolicyFile, err)
|
||||
}
|
||||
scheme := runtime.NewScheme()
|
||||
install.Install(scheme)
|
||||
codecs := serializer.NewCodecFactory(scheme)
|
||||
policy := auditv1beta1.Policy{}
|
||||
err = runtime.DecodeInto(codecs.UniversalDecoder(), policyBytes, &policy)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to decode written policy: %v", err)
|
||||
}
|
||||
if policy.Kind != "Policy" {
|
||||
t.Fatalf("did not decode policy properly")
|
||||
}
|
||||
}
|
75
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/cgroupdriver.go
generated
vendored
75
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/cgroupdriver.go
generated
vendored
@ -1,75 +0,0 @@
|
||||
/*
|
||||
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 util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
utilsexec "k8s.io/utils/exec"
|
||||
)
|
||||
|
||||
// TODO: add support for detecting the cgroup driver for CRI other than
|
||||
// Docker. Currently only Docker driver detection is supported:
|
||||
// Discussion:
|
||||
// https://github.com/kubernetes/kubeadm/issues/844
|
||||
|
||||
// GetCgroupDriverDocker runs 'docker info' to obtain the docker cgroup driver
|
||||
func GetCgroupDriverDocker(execer utilsexec.Interface) (string, error) {
|
||||
info, err := callDockerInfo(execer)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return getCgroupDriverFromDockerInfo(info)
|
||||
}
|
||||
|
||||
func validateCgroupDriver(driver string) error {
|
||||
if driver != "cgroupfs" && driver != "systemd" {
|
||||
return fmt.Errorf("unknown cgroup driver %q", driver)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO: Docker 1.13 has a new way to obatain the cgroup driver:
|
||||
// docker info -f "{{.CgroupDriver}}
|
||||
// If the minimum supported Docker version in K8s becomes 1.13, move to
|
||||
// this syntax.
|
||||
func callDockerInfo(execer utilsexec.Interface) (string, error) {
|
||||
out, err := execer.Command("docker", "info").Output()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("cannot execute 'docker info': %v", err)
|
||||
}
|
||||
return string(out), nil
|
||||
}
|
||||
|
||||
func getCgroupDriverFromDockerInfo(info string) (string, error) {
|
||||
lineSeparator := ": "
|
||||
prefix := "Cgroup Driver"
|
||||
for _, line := range strings.Split(info, "\n") {
|
||||
if !strings.Contains(line, prefix+lineSeparator) {
|
||||
continue
|
||||
}
|
||||
lineSplit := strings.Split(line, lineSeparator)
|
||||
// At this point len(lineSplit) is always >= 2
|
||||
driver := lineSplit[1]
|
||||
if err := validateCgroupDriver(driver); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return driver, nil
|
||||
}
|
||||
return "", fmt.Errorf("cgroup driver is not defined in 'docker info'")
|
||||
}
|
68
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/cgroupdriver_test.go
generated
vendored
68
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/cgroupdriver_test.go
generated
vendored
@ -1,68 +0,0 @@
|
||||
/*
|
||||
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 util
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetCgroupDriverDocker(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
info string
|
||||
expectedError bool
|
||||
}{
|
||||
{
|
||||
name: "valid: value is 'cgroupfs'",
|
||||
info: `Cgroup Driver: cgroupfs`,
|
||||
expectedError: false,
|
||||
},
|
||||
{
|
||||
name: "valid: value is 'systemd'",
|
||||
info: `Cgroup Driver: systemd`,
|
||||
expectedError: false,
|
||||
},
|
||||
{
|
||||
name: "invalid: missing 'Cgroup Driver' key and value",
|
||||
info: "",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
name: "invalid: only a 'Cgroup Driver' key is present",
|
||||
info: `Cgroup Driver`,
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
name: "invalid: empty 'Cgroup Driver' value",
|
||||
info: `Cgroup Driver: `,
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
name: "invalid: unknown 'Cgroup Driver' value",
|
||||
info: `Cgroup Driver: invalid-value`,
|
||||
expectedError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
if _, err := getCgroupDriverFromDockerInfo(tc.info); (err != nil) != tc.expectedError {
|
||||
t.Fatalf("expected error: %v, saw: %v, error: %v", tc.expectedError, (err != nil), err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
70
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/BUILD
generated
vendored
70
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/BUILD
generated
vendored
@ -1,70 +0,0 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"cluster.go",
|
||||
"masterconfig.go",
|
||||
"nodeconfig.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/cmd/kubeadm/app/util/config",
|
||||
deps = [
|
||||
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
|
||||
"//cmd/kubeadm/app/apis/kubeadm/scheme:go_default_library",
|
||||
"//cmd/kubeadm/app/apis/kubeadm/v1alpha1:go_default_library",
|
||||
"//cmd/kubeadm/app/apis/kubeadm/v1alpha2:go_default_library",
|
||||
"//cmd/kubeadm/app/apis/kubeadm/validation:go_default_library",
|
||||
"//cmd/kubeadm/app/constants:go_default_library",
|
||||
"//cmd/kubeadm/app/util:go_default_library",
|
||||
"//pkg/util/node:go_default_library",
|
||||
"//pkg/util/version:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/net:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/bootstrap/token/util:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"masterconfig_test.go",
|
||||
"nodeconfig_test.go",
|
||||
],
|
||||
data = glob(["testdata/**"]),
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
|
||||
"//cmd/kubeadm/app/apis/kubeadm/scheme:go_default_library",
|
||||
"//cmd/kubeadm/app/apis/kubeadm/v1alpha1:go_default_library",
|
||||
"//cmd/kubeadm/app/apis/kubeadm/v1alpha2:go_default_library",
|
||||
"//cmd/kubeadm/app/util:go_default_library",
|
||||
"//vendor/github.com/pmezard/go-difflib/difflib: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",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
65
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/cluster.go
generated
vendored
65
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/cluster.go
generated
vendored
@ -1,65 +0,0 @@
|
||||
/*
|
||||
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 config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||
)
|
||||
|
||||
// TODO: Add unit tests for this file
|
||||
|
||||
// FetchConfigFromFileOrCluster fetches configuration required for upgrading your cluster from a file (which has precedence) or a ConfigMap in the cluster
|
||||
func FetchConfigFromFileOrCluster(client clientset.Interface, w io.Writer, logPrefix, cfgPath string) (*kubeadmapi.MasterConfiguration, error) {
|
||||
// Load the configuration from a file or the cluster
|
||||
configBytes, err := loadConfigurationBytes(client, w, logPrefix, cfgPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Take the versioned configuration populated from the file or ConfigMap, convert it to internal, default and validate
|
||||
return BytesToInternalConfig(configBytes)
|
||||
}
|
||||
|
||||
// loadConfigurationBytes loads the configuration byte slice from either a file or the cluster ConfigMap
|
||||
func loadConfigurationBytes(client clientset.Interface, w io.Writer, logPrefix, cfgPath string) ([]byte, error) {
|
||||
// The config file has the highest priority
|
||||
if cfgPath != "" {
|
||||
fmt.Fprintf(w, "[%s] Reading configuration options from a file: %s\n", logPrefix, cfgPath)
|
||||
return ioutil.ReadFile(cfgPath)
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, "[%s] Reading configuration from the cluster...\n", logPrefix)
|
||||
|
||||
configMap, err := client.CoreV1().ConfigMaps(metav1.NamespaceSystem).Get(constants.MasterConfigurationConfigMap, metav1.GetOptions{})
|
||||
if apierrors.IsNotFound(err) {
|
||||
// Return the apierror directly so the caller of this function can know what type of error occurred and act based on that
|
||||
return []byte{}, err
|
||||
} else if err != nil {
|
||||
return []byte{}, fmt.Errorf("an unexpected error happened when trying to get the ConfigMap %q in the %s namespace: %v", constants.MasterConfigurationConfigMap, metav1.NamespaceSystem, err)
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, "[%s] FYI: You can look at this config file with 'kubectl -n %s get cm %s -oyaml'\n", logPrefix, metav1.NamespaceSystem, constants.MasterConfigurationConfigMap)
|
||||
return []byte(configMap.Data[constants.MasterConfigurationConfigMapKey]), nil
|
||||
}
|
232
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/masterconfig.go
generated
vendored
232
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/masterconfig.go
generated
vendored
@ -1,232 +0,0 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
netutil "k8s.io/apimachinery/pkg/util/net"
|
||||
bootstraputil "k8s.io/client-go/tools/bootstrap/token/util"
|
||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||
kubeadmscheme "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/scheme"
|
||||
kubeadmapiv1alpha1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
|
||||
kubeadmapiv1alpha2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha2"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation"
|
||||
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
||||
"k8s.io/kubernetes/pkg/util/node"
|
||||
"k8s.io/kubernetes/pkg/util/version"
|
||||
)
|
||||
|
||||
// SetInitDynamicDefaults checks and sets configuration values for the MasterConfiguration object
|
||||
func SetInitDynamicDefaults(cfg *kubeadmapi.MasterConfiguration) error {
|
||||
|
||||
// validate cfg.API.AdvertiseAddress.
|
||||
addressIP := net.ParseIP(cfg.API.AdvertiseAddress)
|
||||
if addressIP == nil && cfg.API.AdvertiseAddress != "" {
|
||||
return fmt.Errorf("couldn't use \"%s\" as \"apiserver-advertise-address\", must be ipv4 or ipv6 address", cfg.API.AdvertiseAddress)
|
||||
}
|
||||
// Choose the right address for the API Server to advertise. If the advertise address is localhost or 0.0.0.0, the default interface's IP address is used
|
||||
// This is the same logic as the API Server uses
|
||||
ip, err := netutil.ChooseBindAddress(addressIP)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cfg.API.AdvertiseAddress = ip.String()
|
||||
ip = net.ParseIP(cfg.API.AdvertiseAddress)
|
||||
if ip.To4() != nil {
|
||||
cfg.KubeProxy.Config.BindAddress = kubeadmapiv1alpha2.DefaultProxyBindAddressv4
|
||||
} else {
|
||||
cfg.KubeProxy.Config.BindAddress = kubeadmapiv1alpha2.DefaultProxyBindAddressv6
|
||||
}
|
||||
// Resolve possible version labels and validate version string
|
||||
if err := NormalizeKubernetesVersion(cfg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Downcase SANs. Some domain names (like ELBs) have capitals in them.
|
||||
LowercaseSANs(cfg.APIServerCertSANs)
|
||||
|
||||
// Populate the .Token field with a random value if unset
|
||||
// We do this at this layer, and not the API defaulting layer
|
||||
// because of possible security concerns, and more practically
|
||||
// because we can't return errors in the API object defaulting
|
||||
// process but here we can.
|
||||
for i, bt := range cfg.BootstrapTokens {
|
||||
if bt.Token != nil && len(bt.Token.String()) > 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
tokenStr, err := bootstraputil.GenerateBootstrapToken()
|
||||
if err != nil {
|
||||
return fmt.Errorf("couldn't generate random token: %v", err)
|
||||
}
|
||||
token, err := kubeadmapi.NewBootstrapTokenString(tokenStr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cfg.BootstrapTokens[i].Token = token
|
||||
}
|
||||
|
||||
cfg.NodeRegistration.Name = node.GetHostname(cfg.NodeRegistration.Name)
|
||||
|
||||
// Only if the slice is nil, we should append the master taint. This allows the user to specify an empty slice for no default master taint
|
||||
if cfg.NodeRegistration.Taints == nil {
|
||||
cfg.NodeRegistration.Taints = []v1.Taint{kubeadmconstants.MasterTaint}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ConfigFileAndDefaultsToInternalConfig takes a path to a config file and a versioned configuration that can serve as the default config
|
||||
// If cfgPath is specified, defaultversionedcfg will always get overridden. Otherwise, the default config (often populated by flags) will be used.
|
||||
// Then the external, versioned configuration is defaulted and converted to the internal type.
|
||||
// Right thereafter, the configuration is defaulted again with dynamic values (like IP addresses of a machine, etc)
|
||||
// Lastly, the internal config is validated and returned.
|
||||
func ConfigFileAndDefaultsToInternalConfig(cfgPath string, defaultversionedcfg *kubeadmapiv1alpha2.MasterConfiguration) (*kubeadmapi.MasterConfiguration, error) {
|
||||
internalcfg := &kubeadmapi.MasterConfiguration{}
|
||||
|
||||
if cfgPath != "" {
|
||||
// Loads configuration from config file, if provided
|
||||
// Nb. --config overrides command line flags
|
||||
glog.V(1).Infoln("loading configuration from the given file")
|
||||
|
||||
b, err := ioutil.ReadFile(cfgPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to read config from %q [%v]", cfgPath, err)
|
||||
}
|
||||
return BytesToInternalConfig(b)
|
||||
}
|
||||
|
||||
// Takes passed flags into account; the defaulting is executed once again enforcing assignement of
|
||||
// static default values to cfg only for values not provided with flags
|
||||
kubeadmscheme.Scheme.Default(defaultversionedcfg)
|
||||
kubeadmscheme.Scheme.Convert(defaultversionedcfg, internalcfg, nil)
|
||||
|
||||
return defaultAndValidate(internalcfg)
|
||||
}
|
||||
|
||||
// BytesToInternalConfig converts a byte array to an internal, defaulted and validated configuration object
|
||||
func BytesToInternalConfig(b []byte) (*kubeadmapi.MasterConfiguration, error) {
|
||||
internalcfg := &kubeadmapi.MasterConfiguration{}
|
||||
|
||||
decoded, err := kubeadmutil.LoadYAML(b)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to decode config from bytes: %v", err)
|
||||
}
|
||||
|
||||
// As there was a bug in kubeadm v1.10 and earlier that made the YAML uploaded to the cluster configmap NOT have metav1.TypeMeta information
|
||||
// we need to populate this here manually. If kind or apiVersion is empty, we know the apiVersion is v1alpha1, as by the time kubeadm had this bug,
|
||||
// it could only write
|
||||
// TODO: Remove this "hack" in v1.12 when we know the ConfigMap always contains v1alpha2 content written by kubeadm v1.11. Also, we will drop support for
|
||||
// v1alpha1 in v1.12
|
||||
kind := decoded["kind"]
|
||||
apiVersion := decoded["apiVersion"]
|
||||
if kind == nil || len(kind.(string)) == 0 {
|
||||
decoded["kind"] = "MasterConfiguration"
|
||||
}
|
||||
if apiVersion == nil || len(apiVersion.(string)) == 0 {
|
||||
decoded["apiVersion"] = kubeadmapiv1alpha1.SchemeGroupVersion.String()
|
||||
}
|
||||
|
||||
// Between v1.9 and v1.10 the proxy componentconfig in the v1alpha1 MasterConfiguration changed unexpectedly, which broke unmarshalling out-of-the-box
|
||||
// Hence, we need to workaround this bug in the v1alpha1 API
|
||||
if decoded["apiVersion"] == kubeadmapiv1alpha1.SchemeGroupVersion.String() {
|
||||
v1alpha1cfg := &kubeadmapiv1alpha1.MasterConfiguration{}
|
||||
if err := kubeadmapiv1alpha1.Migrate(decoded, v1alpha1cfg, kubeadmscheme.Codecs); err != nil {
|
||||
return nil, fmt.Errorf("unable to migrate config from previous version: %v", err)
|
||||
}
|
||||
|
||||
// Default and convert to the internal version
|
||||
kubeadmscheme.Scheme.Default(v1alpha1cfg)
|
||||
kubeadmscheme.Scheme.Convert(v1alpha1cfg, internalcfg, nil)
|
||||
} else if decoded["apiVersion"] == kubeadmapiv1alpha2.SchemeGroupVersion.String() {
|
||||
v1alpha2cfg := &kubeadmapiv1alpha2.MasterConfiguration{}
|
||||
if err := runtime.DecodeInto(kubeadmscheme.Codecs.UniversalDecoder(), b, v1alpha2cfg); err != nil {
|
||||
return nil, fmt.Errorf("unable to decode config: %v", err)
|
||||
}
|
||||
|
||||
// Default and convert to the internal version
|
||||
kubeadmscheme.Scheme.Default(v1alpha2cfg)
|
||||
kubeadmscheme.Scheme.Convert(v1alpha2cfg, internalcfg, nil)
|
||||
} else {
|
||||
// TODO: Add support for an upcoming v1alpha2 API
|
||||
// TODO: In the future, we can unmarshal any two or more external types into the internal object directly using the following syntax.
|
||||
// Long-term we don't need this if/else clause. In the future this will do
|
||||
// runtime.DecodeInto(kubeadmscheme.Codecs.UniversalDecoder(kubeadmapiv1alpha2.SchemeGroupVersion, kubeadmapiv2alpha3.SchemeGroupVersion), b, internalcfg)
|
||||
return nil, fmt.Errorf("unknown API version for kubeadm configuration")
|
||||
}
|
||||
|
||||
return defaultAndValidate(internalcfg)
|
||||
}
|
||||
|
||||
func defaultAndValidate(cfg *kubeadmapi.MasterConfiguration) (*kubeadmapi.MasterConfiguration, error) {
|
||||
// Applies dynamic defaults to settings not provided with flags
|
||||
if err := SetInitDynamicDefaults(cfg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Validates cfg (flags/configs + defaults + dynamic defaults)
|
||||
if err := validation.ValidateMasterConfiguration(cfg).ToAggregate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
// NormalizeKubernetesVersion resolves version labels, sets alternative
|
||||
// image registry if requested for CI builds, and validates minimal
|
||||
// version that kubeadm supports.
|
||||
func NormalizeKubernetesVersion(cfg *kubeadmapi.MasterConfiguration) error {
|
||||
// Requested version is automatic CI build, thus use KubernetesCI Image Repository for core images
|
||||
if kubeadmutil.KubernetesIsCIVersion(cfg.KubernetesVersion) {
|
||||
cfg.CIImageRepository = kubeadmconstants.DefaultCIImageRepository
|
||||
}
|
||||
|
||||
// Parse and validate the version argument and resolve possible CI version labels
|
||||
ver, err := kubeadmutil.KubernetesReleaseVersion(cfg.KubernetesVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cfg.KubernetesVersion = ver
|
||||
|
||||
// Parse the given kubernetes version and make sure it's higher than the lowest supported
|
||||
k8sVersion, err := version.ParseSemantic(cfg.KubernetesVersion)
|
||||
if err != nil {
|
||||
return fmt.Errorf("couldn't parse kubernetes version %q: %v", cfg.KubernetesVersion, err)
|
||||
}
|
||||
if k8sVersion.LessThan(kubeadmconstants.MinimumControlPlaneVersion) {
|
||||
return fmt.Errorf("this version of kubeadm only supports deploying clusters with the control plane version >= %s. Current version: %s", kubeadmconstants.MinimumControlPlaneVersion.String(), cfg.KubernetesVersion)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// LowercaseSANs can be used to force all SANs to be lowercase so it passes IsDNS1123Subdomain
|
||||
func LowercaseSANs(sans []string) {
|
||||
for i, san := range sans {
|
||||
lowercase := strings.ToLower(san)
|
||||
if lowercase != san {
|
||||
glog.V(1).Infof("lowercasing SAN %q to %q", san, lowercase)
|
||||
sans[i] = lowercase
|
||||
}
|
||||
}
|
||||
}
|
225
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/masterconfig_test.go
generated
vendored
225
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/masterconfig_test.go
generated
vendored
@ -1,225 +0,0 @@
|
||||
/*
|
||||
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 config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/pmezard/go-difflib/difflib"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/scheme"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha2"
|
||||
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
||||
)
|
||||
|
||||
const (
|
||||
master_v1alpha1YAML = "testdata/conversion/master/v1alpha1.yaml"
|
||||
master_v1alpha1WithoutTypeMetaYAML = "testdata/conversion/master/v1alpha1_without_TypeMeta.yaml"
|
||||
master_v1alpha2YAML = "testdata/conversion/master/v1alpha2.yaml"
|
||||
master_internalYAML = "testdata/conversion/master/internal.yaml"
|
||||
master_incompleteYAML = "testdata/defaulting/master/incomplete.yaml"
|
||||
master_defaultedYAML = "testdata/defaulting/master/defaulted.yaml"
|
||||
master_invalidYAML = "testdata/validation/invalid_mastercfg.yaml"
|
||||
master_beforeUpgradeYAML = "testdata/v1alpha1_upgrade/before.yaml"
|
||||
master_afterUpgradeYAML = "testdata/v1alpha1_upgrade/after.yaml"
|
||||
)
|
||||
|
||||
func diff(expected, actual []byte) string {
|
||||
// Write out the diff
|
||||
var diffBytes bytes.Buffer
|
||||
difflib.WriteUnifiedDiff(&diffBytes, difflib.UnifiedDiff{
|
||||
A: difflib.SplitLines(string(expected)),
|
||||
B: difflib.SplitLines(string(actual)),
|
||||
FromFile: "expected",
|
||||
ToFile: "actual",
|
||||
Context: 3,
|
||||
})
|
||||
return diffBytes.String()
|
||||
}
|
||||
|
||||
func TestConfigFileAndDefaultsToInternalConfig(t *testing.T) {
|
||||
var tests = []struct {
|
||||
name, in, out string
|
||||
groupVersion schema.GroupVersion
|
||||
expectedErr bool
|
||||
}{
|
||||
// These tests are reading one file, loading it using ConfigFileAndDefaultsToInternalConfig that all of kubeadm is using for unmarshal of our API types,
|
||||
// and then marshals the internal object to the expected groupVersion
|
||||
{ // v1alpha1 (faulty) -> internal
|
||||
name: "v1alpha1WithoutTypeMetaToInternal",
|
||||
in: master_v1alpha1WithoutTypeMetaYAML,
|
||||
out: master_internalYAML,
|
||||
groupVersion: kubeadm.SchemeGroupVersion,
|
||||
},
|
||||
{ // v1alpha1 -> internal
|
||||
name: "v1alpha1ToInternal",
|
||||
in: master_v1alpha1YAML,
|
||||
out: master_internalYAML,
|
||||
groupVersion: kubeadm.SchemeGroupVersion,
|
||||
},
|
||||
{ // v1alpha2 -> internal
|
||||
name: "v1alpha2ToInternal",
|
||||
in: master_v1alpha2YAML,
|
||||
out: master_internalYAML,
|
||||
groupVersion: kubeadm.SchemeGroupVersion,
|
||||
},
|
||||
{ // v1alpha1 (faulty) -> internal -> v1alpha2
|
||||
name: "v1alpha1WithoutTypeMetaTov1alpha2",
|
||||
in: master_v1alpha1WithoutTypeMetaYAML,
|
||||
out: master_v1alpha2YAML,
|
||||
groupVersion: v1alpha2.SchemeGroupVersion,
|
||||
},
|
||||
{ // v1alpha1 -> internal -> v1alpha2
|
||||
name: "v1alpha1Tov1alpha2",
|
||||
in: master_v1alpha1YAML,
|
||||
out: master_v1alpha2YAML,
|
||||
groupVersion: v1alpha2.SchemeGroupVersion,
|
||||
},
|
||||
// These tests are reading one file that has only a subset of the fields populated, loading it using ConfigFileAndDefaultsToInternalConfig,
|
||||
// and then marshals the internal object to the expected groupVersion
|
||||
{ // v1alpha1 (faulty) -> default -> validate -> internal -> v1alpha2
|
||||
name: "incompleteYAMLToDefaultedv1alpha2",
|
||||
in: master_incompleteYAML,
|
||||
out: master_defaultedYAML,
|
||||
groupVersion: v1alpha2.SchemeGroupVersion,
|
||||
},
|
||||
{ // v1alpha1 (faulty) -> validation should fail
|
||||
name: "invalidYAMLShouldFail",
|
||||
in: master_invalidYAML,
|
||||
expectedErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, rt := range tests {
|
||||
t.Run(rt.name, func(t2 *testing.T) {
|
||||
|
||||
internalcfg, err := ConfigFileAndDefaultsToInternalConfig(rt.in, &v1alpha2.MasterConfiguration{})
|
||||
if err != nil {
|
||||
if rt.expectedErr {
|
||||
return
|
||||
}
|
||||
t2.Fatalf("couldn't unmarshal test data: %v", err)
|
||||
}
|
||||
|
||||
actual, err := kubeadmutil.MarshalToYamlForCodecs(internalcfg, rt.groupVersion, scheme.Codecs)
|
||||
if err != nil {
|
||||
t2.Fatalf("couldn't marshal internal object: %v", err)
|
||||
}
|
||||
|
||||
expected, err := ioutil.ReadFile(rt.out)
|
||||
if err != nil {
|
||||
t2.Fatalf("couldn't read test data: %v", err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(expected, actual) {
|
||||
t2.Errorf("the expected and actual output differs.\n\tin: %s\n\tout: %s\n\tgroupversion: %s\n\tdiff: \n%s\n",
|
||||
rt.in, rt.out, rt.groupVersion.String(), diff(expected, actual))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestUpgrade tests reading a faulty YAML representation of the MasterConfiguration object (as found in kubeadm clusters <= v1.9.x),
|
||||
// fixes the problems internally and verifies the marshalled output is the expected output
|
||||
func TestUpgrade(t *testing.T) {
|
||||
before, err := ioutil.ReadFile(master_beforeUpgradeYAML)
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't read test data: %v", err)
|
||||
}
|
||||
|
||||
afterExpected, err := ioutil.ReadFile(master_afterUpgradeYAML)
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't read test data: %v", err)
|
||||
}
|
||||
|
||||
decoded, err := kubeadmutil.LoadYAML(before)
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't unmarshal test yaml: %v", err)
|
||||
}
|
||||
|
||||
scheme := runtime.NewScheme()
|
||||
v1alpha1.AddToScheme(scheme)
|
||||
codecs := serializer.NewCodecFactory(scheme)
|
||||
|
||||
obj := &v1alpha1.MasterConfiguration{}
|
||||
if err := v1alpha1.Migrate(decoded, obj, codecs); err != nil {
|
||||
t.Fatalf("couldn't decode migrated object: %v", err)
|
||||
}
|
||||
|
||||
afterActual, err := kubeadmutil.MarshalToYamlForCodecs(obj, v1alpha1.SchemeGroupVersion, codecs)
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't marshal object: %v", err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(afterExpected, afterActual) {
|
||||
t.Errorf("v1alpha1 object after unmarshal, conversion and marshal didn't match expected value.\n\tdiff: \n%s\n", diff(afterExpected, afterActual))
|
||||
}
|
||||
}
|
||||
|
||||
func TestLowercaseSANs(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
in []string
|
||||
out []string
|
||||
}{
|
||||
{
|
||||
name: "empty struct",
|
||||
},
|
||||
{
|
||||
name: "already lowercase",
|
||||
in: []string{"example.k8s.io"},
|
||||
out: []string{"example.k8s.io"},
|
||||
},
|
||||
{
|
||||
name: "ip addresses and uppercase",
|
||||
in: []string{"EXAMPLE.k8s.io", "10.100.0.1"},
|
||||
out: []string{"example.k8s.io", "10.100.0.1"},
|
||||
},
|
||||
{
|
||||
name: "punycode and uppercase",
|
||||
in: []string{"xn--7gq663byk9a.xn--fiqz9s", "ANOTHEREXAMPLE.k8s.io"},
|
||||
out: []string{"xn--7gq663byk9a.xn--fiqz9s", "anotherexample.k8s.io"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
cfg := &v1alpha2.MasterConfiguration{
|
||||
APIServerCertSANs: test.in,
|
||||
}
|
||||
|
||||
LowercaseSANs(cfg.APIServerCertSANs)
|
||||
|
||||
if len(cfg.APIServerCertSANs) != len(test.out) {
|
||||
t.Fatalf("expected %d elements, got %d", len(test.out), len(cfg.APIServerCertSANs))
|
||||
}
|
||||
|
||||
for i, expected := range test.out {
|
||||
if cfg.APIServerCertSANs[i] != expected {
|
||||
t.Errorf("expected element %d to be %q, got %q", i, expected, cfg.APIServerCertSANs[i])
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
71
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/nodeconfig.go
generated
vendored
71
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/nodeconfig.go
generated
vendored
@ -1,71 +0,0 @@
|
||||
/*
|
||||
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 config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||
kubeadmscheme "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/scheme"
|
||||
kubeadmapiv1alpha1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
|
||||
kubeadmapiv1alpha2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha2"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation"
|
||||
"k8s.io/kubernetes/pkg/util/node"
|
||||
)
|
||||
|
||||
// SetJoinDynamicDefaults checks and sets configuration values for the NodeConfiguration object
|
||||
func SetJoinDynamicDefaults(cfg *kubeadmapi.NodeConfiguration) error {
|
||||
cfg.NodeRegistration.Name = node.GetHostname(cfg.NodeRegistration.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
// NodeConfigFileAndDefaultsToInternalConfig
|
||||
func NodeConfigFileAndDefaultsToInternalConfig(cfgPath string, defaultversionedcfg *kubeadmapiv1alpha2.NodeConfiguration) (*kubeadmapi.NodeConfiguration, error) {
|
||||
internalcfg := &kubeadmapi.NodeConfiguration{}
|
||||
|
||||
if cfgPath != "" {
|
||||
// Loads configuration from config file, if provided
|
||||
// Nb. --config overrides command line flags
|
||||
glog.V(1).Infoln("loading configuration from the given file")
|
||||
|
||||
b, err := ioutil.ReadFile(cfgPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to read config from %q [%v]", cfgPath, err)
|
||||
}
|
||||
runtime.DecodeInto(kubeadmscheme.Codecs.UniversalDecoder(kubeadmapiv1alpha1.SchemeGroupVersion, kubeadmapiv1alpha2.SchemeGroupVersion), b, internalcfg)
|
||||
} else {
|
||||
// Takes passed flags into account; the defaulting is executed once again enforcing assignement of
|
||||
// static default values to cfg only for values not provided with flags
|
||||
kubeadmscheme.Scheme.Default(defaultversionedcfg)
|
||||
kubeadmscheme.Scheme.Convert(defaultversionedcfg, internalcfg, nil)
|
||||
}
|
||||
|
||||
// Applies dynamic defaults to settings not provided with flags
|
||||
if err := SetJoinDynamicDefaults(internalcfg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Validates cfg (flags/configs + defaults)
|
||||
if err := validation.ValidateNodeConfiguration(internalcfg).ToAggregate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return internalcfg, nil
|
||||
}
|
108
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/nodeconfig_test.go
generated
vendored
108
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/nodeconfig_test.go
generated
vendored
@ -1,108 +0,0 @@
|
||||
/*
|
||||
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 config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/scheme"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha2"
|
||||
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
||||
)
|
||||
|
||||
const (
|
||||
node_v1alpha1YAML = "testdata/conversion/node/v1alpha1.yaml"
|
||||
node_v1alpha2YAML = "testdata/conversion/node/v1alpha2.yaml"
|
||||
node_internalYAML = "testdata/conversion/node/internal.yaml"
|
||||
node_incompleteYAML = "testdata/defaulting/node/incomplete.yaml"
|
||||
node_defaultedYAML = "testdata/defaulting/node/defaulted.yaml"
|
||||
node_invalidYAML = "testdata/validation/invalid_nodecfg.yaml"
|
||||
)
|
||||
|
||||
func TestNodeConfigFileAndDefaultsToInternalConfig(t *testing.T) {
|
||||
var tests = []struct {
|
||||
name, in, out string
|
||||
groupVersion schema.GroupVersion
|
||||
expectedErr bool
|
||||
}{
|
||||
// These tests are reading one file, loading it using NodeConfigFileAndDefaultsToInternalConfig that all of kubeadm is using for unmarshal of our API types,
|
||||
// and then marshals the internal object to the expected groupVersion
|
||||
{ // v1alpha1 -> internal
|
||||
name: "v1alpha1ToInternal",
|
||||
in: node_v1alpha1YAML,
|
||||
out: node_internalYAML,
|
||||
groupVersion: kubeadm.SchemeGroupVersion,
|
||||
},
|
||||
{ // v1alpha2 -> internal
|
||||
name: "v1alpha2ToInternal",
|
||||
in: node_v1alpha2YAML,
|
||||
out: node_internalYAML,
|
||||
groupVersion: kubeadm.SchemeGroupVersion,
|
||||
},
|
||||
{ // v1alpha1 -> internal -> v1alpha2
|
||||
name: "v1alpha1WithoutTypeMetaTov1alpha2",
|
||||
in: node_v1alpha1YAML,
|
||||
out: node_v1alpha2YAML,
|
||||
groupVersion: v1alpha2.SchemeGroupVersion,
|
||||
},
|
||||
// These tests are reading one file that has only a subset of the fields populated, loading it using NodeConfigFileAndDefaultsToInternalConfig,
|
||||
// and then marshals the internal object to the expected groupVersion
|
||||
{ // v1alpha1 -> default -> validate -> internal -> v1alpha2
|
||||
name: "incompleteYAMLToDefaulted",
|
||||
in: node_incompleteYAML,
|
||||
out: node_defaultedYAML,
|
||||
groupVersion: v1alpha2.SchemeGroupVersion,
|
||||
},
|
||||
{ // v1alpha1 (faulty) -> validation should fail
|
||||
name: "invalidYAMLShouldFail",
|
||||
in: node_invalidYAML,
|
||||
expectedErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, rt := range tests {
|
||||
t.Run(rt.name, func(t2 *testing.T) {
|
||||
|
||||
internalcfg, err := NodeConfigFileAndDefaultsToInternalConfig(rt.in, &v1alpha2.NodeConfiguration{})
|
||||
if err != nil {
|
||||
if rt.expectedErr {
|
||||
return
|
||||
}
|
||||
t2.Fatalf("couldn't unmarshal test data: %v", err)
|
||||
}
|
||||
|
||||
actual, err := kubeadmutil.MarshalToYamlForCodecs(internalcfg, rt.groupVersion, scheme.Codecs)
|
||||
if err != nil {
|
||||
t2.Fatalf("couldn't marshal internal object: %v", err)
|
||||
}
|
||||
|
||||
expected, err := ioutil.ReadFile(rt.out)
|
||||
if err != nil {
|
||||
t2.Fatalf("couldn't read test data: %v", err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(expected, actual) {
|
||||
t2.Errorf("the expected and actual output differs.\n\tin: %s\n\tout: %s\n\tgroupversion: %s\n\tdiff: \n%s\n",
|
||||
rt.in, rt.out, rt.groupVersion.String(), diff(expected, actual))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -1,161 +0,0 @@
|
||||
API:
|
||||
AdvertiseAddress: 192.168.2.2
|
||||
BindPort: 6443
|
||||
ControlPlaneEndpoint: ""
|
||||
APIServerCertSANs: null
|
||||
APIServerExtraArgs:
|
||||
authorization-mode: Node,RBAC,Webhook
|
||||
APIServerExtraVolumes: null
|
||||
AuditPolicyConfiguration:
|
||||
LogDir: /var/log/kubernetes/audit
|
||||
LogMaxAge: 2
|
||||
Path: ""
|
||||
BootstrapTokens:
|
||||
- Description: ""
|
||||
Expires: null
|
||||
Groups:
|
||||
- system:bootstrappers:kubeadm:default-node-token
|
||||
TTL: 24h0m0s
|
||||
Token: s73ybu.6tw6wnqgp5z0wb77
|
||||
Usages:
|
||||
- signing
|
||||
- authentication
|
||||
CIImageRepository: ""
|
||||
CertificatesDir: /etc/kubernetes/pki
|
||||
ClusterName: kubernetes
|
||||
ControllerManagerExtraArgs: null
|
||||
ControllerManagerExtraVolumes: null
|
||||
Etcd:
|
||||
External: null
|
||||
Local:
|
||||
DataDir: /var/lib/etcd
|
||||
ExtraArgs: null
|
||||
Image: ""
|
||||
PeerCertSANs: null
|
||||
ServerCertSANs: null
|
||||
FeatureGates: null
|
||||
ImageRepository: k8s.gcr.io
|
||||
KubeProxy:
|
||||
Config:
|
||||
bindAddress: 0.0.0.0
|
||||
clientConnection:
|
||||
acceptContentTypes: ""
|
||||
burst: 10
|
||||
contentType: application/vnd.kubernetes.protobuf
|
||||
kubeconfig: /var/lib/kube-proxy/kubeconfig.conf
|
||||
qps: 5
|
||||
clusterCIDR: ""
|
||||
configSyncPeriod: 15m0s
|
||||
conntrack:
|
||||
max: null
|
||||
maxPerCore: 32768
|
||||
min: 131072
|
||||
tcpCloseWaitTimeout: 1h0m0s
|
||||
tcpEstablishedTimeout: 24h0m0s
|
||||
enableProfiling: false
|
||||
featureGates:
|
||||
ServiceNodeExclusion: true
|
||||
SupportIPVSProxyMode: true
|
||||
healthzBindAddress: 0.0.0.0:10256
|
||||
hostnameOverride: ""
|
||||
iptables:
|
||||
masqueradeAll: false
|
||||
masqueradeBit: 14
|
||||
minSyncPeriod: 0s
|
||||
syncPeriod: 30s
|
||||
ipvs:
|
||||
excludeCIDRs: null
|
||||
minSyncPeriod: 0s
|
||||
scheduler: ""
|
||||
syncPeriod: 30s
|
||||
metricsBindAddress: 127.0.0.1:10249
|
||||
mode: ""
|
||||
nodePortAddresses: null
|
||||
oomScoreAdj: -999
|
||||
portRange: ""
|
||||
resourceContainer: /kube-proxy
|
||||
udpIdleTimeout: 250ms
|
||||
KubeletConfiguration:
|
||||
BaseConfig:
|
||||
address: 0.0.0.0
|
||||
authentication:
|
||||
anonymous:
|
||||
enabled: false
|
||||
webhook:
|
||||
cacheTTL: 2m0s
|
||||
enabled: true
|
||||
x509:
|
||||
clientCAFile: /etc/kubernetes/pki/ca.crt
|
||||
authorization:
|
||||
mode: Webhook
|
||||
webhook:
|
||||
cacheAuthorizedTTL: 5m0s
|
||||
cacheUnauthorizedTTL: 30s
|
||||
cgroupDriver: cgroupfs
|
||||
cgroupsPerQOS: true
|
||||
clusterDNS:
|
||||
- 10.96.0.10
|
||||
clusterDomain: cluster.local
|
||||
containerLogMaxFiles: 5
|
||||
containerLogMaxSize: 10Mi
|
||||
contentType: application/vnd.kubernetes.protobuf
|
||||
cpuCFSQuota: true
|
||||
cpuManagerPolicy: none
|
||||
cpuManagerReconcilePeriod: 10s
|
||||
enableControllerAttachDetach: true
|
||||
enableDebuggingHandlers: true
|
||||
enforceNodeAllocatable:
|
||||
- pods
|
||||
eventBurst: 10
|
||||
eventRecordQPS: 5
|
||||
evictionHard:
|
||||
imagefs.available: 15%
|
||||
memory.available: 100Mi
|
||||
nodefs.available: 10%
|
||||
nodefs.inodesFree: 5%
|
||||
evictionPressureTransitionPeriod: 5m0s
|
||||
failSwapOn: true
|
||||
fileCheckFrequency: 20s
|
||||
hairpinMode: promiscuous-bridge
|
||||
healthzBindAddress: 127.0.0.1
|
||||
healthzPort: 10248
|
||||
httpCheckFrequency: 20s
|
||||
imageGCHighThresholdPercent: 85
|
||||
imageGCLowThresholdPercent: 80
|
||||
imageMinimumGCAge: 2m0s
|
||||
iptablesDropBit: 15
|
||||
iptablesMasqueradeBit: 14
|
||||
kubeAPIBurst: 10
|
||||
kubeAPIQPS: 5
|
||||
makeIPTablesUtilChains: true
|
||||
maxOpenFiles: 1000000
|
||||
maxPods: 110
|
||||
nodeStatusUpdateFrequency: 10s
|
||||
oomScoreAdj: -999
|
||||
podPidsLimit: -1
|
||||
port: 10250
|
||||
registryBurst: 10
|
||||
registryPullQPS: 5
|
||||
resolvConf: /etc/resolv.conf
|
||||
rotateCertificates: true
|
||||
runtimeRequestTimeout: 2m0s
|
||||
serializeImagePulls: true
|
||||
staticPodPath: /etc/kubernetes/manifests
|
||||
streamingConnectionIdleTimeout: 4h0m0s
|
||||
syncFrequency: 1m0s
|
||||
volumeStatsAggPeriod: 1m0s
|
||||
KubernetesVersion: v1.10.2
|
||||
Networking:
|
||||
DNSDomain: cluster.local
|
||||
PodSubnet: ""
|
||||
ServiceSubnet: 10.96.0.0/12
|
||||
NodeRegistration:
|
||||
CRISocket: /var/run/dockershim.sock
|
||||
KubeletExtraArgs: null
|
||||
Name: master-1
|
||||
Taints:
|
||||
- effect: NoSchedule
|
||||
key: node-role.kubernetes.io/master
|
||||
SchedulerExtraArgs: null
|
||||
SchedulerExtraVolumes: null
|
||||
UnifiedControlPlaneImage: ""
|
@ -1,149 +0,0 @@
|
||||
api:
|
||||
advertiseAddress: 192.168.2.2
|
||||
bindPort: 6443
|
||||
controlPlaneEndpoint: ""
|
||||
apiVersion: kubeadm.k8s.io/v1alpha1
|
||||
auditPolicy:
|
||||
logDir: /var/log/kubernetes/audit
|
||||
logMaxAge: 2
|
||||
path: ""
|
||||
authorizationModes:
|
||||
- Node
|
||||
- RBAC
|
||||
- Webhook
|
||||
certificatesDir: /etc/kubernetes/pki
|
||||
cloudProvider: ""
|
||||
clusterName: kubernetes
|
||||
criSocket: /var/run/dockershim.sock
|
||||
etcd:
|
||||
caFile: ""
|
||||
certFile: ""
|
||||
dataDir: /var/lib/etcd
|
||||
endpoints: null
|
||||
image: ""
|
||||
keyFile: ""
|
||||
imageRepository: k8s.gcr.io
|
||||
kind: MasterConfiguration
|
||||
kubeProxy:
|
||||
config:
|
||||
bindAddress: 0.0.0.0
|
||||
clientConnection:
|
||||
acceptContentTypes: ""
|
||||
burst: 10
|
||||
contentType: application/vnd.kubernetes.protobuf
|
||||
kubeconfig: /var/lib/kube-proxy/kubeconfig.conf
|
||||
qps: 5
|
||||
clusterCIDR: ""
|
||||
configSyncPeriod: 15m0s
|
||||
conntrack:
|
||||
max: null
|
||||
maxPerCore: 32768
|
||||
min: 131072
|
||||
tcpCloseWaitTimeout: 1h0m0s
|
||||
tcpEstablishedTimeout: 24h0m0s
|
||||
enableProfiling: false
|
||||
featureGates:
|
||||
ServiceNodeExclusion: true
|
||||
SupportIPVSProxyMode: true
|
||||
healthzBindAddress: 0.0.0.0:10256
|
||||
hostnameOverride: ""
|
||||
iptables:
|
||||
masqueradeAll: false
|
||||
masqueradeBit: 14
|
||||
minSyncPeriod: 0s
|
||||
syncPeriod: 30s
|
||||
ipvs:
|
||||
excludeCIDRs: null
|
||||
minSyncPeriod: 0s
|
||||
scheduler: ""
|
||||
syncPeriod: 30s
|
||||
metricsBindAddress: 127.0.0.1:10249
|
||||
mode: ""
|
||||
nodePortAddresses: null
|
||||
oomScoreAdj: -999
|
||||
portRange: ""
|
||||
resourceContainer: /kube-proxy
|
||||
udpIdleTimeout: 250ms
|
||||
kubeletConfiguration:
|
||||
baseConfig:
|
||||
address: 0.0.0.0
|
||||
authentication:
|
||||
anonymous:
|
||||
enabled: false
|
||||
webhook:
|
||||
cacheTTL: 2m0s
|
||||
enabled: true
|
||||
x509:
|
||||
clientCAFile: /etc/kubernetes/pki/ca.crt
|
||||
authorization:
|
||||
mode: Webhook
|
||||
webhook:
|
||||
cacheAuthorizedTTL: 5m0s
|
||||
cacheUnauthorizedTTL: 30s
|
||||
cgroupDriver: cgroupfs
|
||||
cgroupsPerQOS: true
|
||||
clusterDNS:
|
||||
- 10.96.0.10
|
||||
clusterDomain: cluster.local
|
||||
containerLogMaxFiles: 5
|
||||
containerLogMaxSize: 10Mi
|
||||
contentType: application/vnd.kubernetes.protobuf
|
||||
cpuCFSQuota: true
|
||||
cpuManagerPolicy: none
|
||||
cpuManagerReconcilePeriod: 10s
|
||||
enableControllerAttachDetach: true
|
||||
enableDebuggingHandlers: true
|
||||
enforceNodeAllocatable:
|
||||
- pods
|
||||
eventBurst: 10
|
||||
eventRecordQPS: 5
|
||||
evictionHard:
|
||||
imagefs.available: 15%
|
||||
memory.available: 100Mi
|
||||
nodefs.available: 10%
|
||||
nodefs.inodesFree: 5%
|
||||
evictionPressureTransitionPeriod: 5m0s
|
||||
failSwapOn: true
|
||||
fileCheckFrequency: 20s
|
||||
hairpinMode: promiscuous-bridge
|
||||
healthzBindAddress: 127.0.0.1
|
||||
healthzPort: 10248
|
||||
httpCheckFrequency: 20s
|
||||
imageGCHighThresholdPercent: 85
|
||||
imageGCLowThresholdPercent: 80
|
||||
imageMinimumGCAge: 2m0s
|
||||
iptablesDropBit: 15
|
||||
iptablesMasqueradeBit: 14
|
||||
kubeAPIBurst: 10
|
||||
kubeAPIQPS: 5
|
||||
makeIPTablesUtilChains: true
|
||||
maxOpenFiles: 1000000
|
||||
maxPods: 110
|
||||
nodeStatusUpdateFrequency: 10s
|
||||
oomScoreAdj: -999
|
||||
podPidsLimit: -1
|
||||
port: 10250
|
||||
registryBurst: 10
|
||||
registryPullQPS: 5
|
||||
resolvConf: /etc/resolv.conf
|
||||
runtimeRequestTimeout: 2m0s
|
||||
serializeImagePulls: true
|
||||
staticPodPath: /etc/kubernetes/manifests
|
||||
streamingConnectionIdleTimeout: 4h0m0s
|
||||
syncFrequency: 1m0s
|
||||
volumeStatsAggPeriod: 1m0s
|
||||
kubernetesVersion: v1.10.2
|
||||
networking:
|
||||
dnsDomain: cluster.local
|
||||
podSubnet: ""
|
||||
serviceSubnet: 10.96.0.0/12
|
||||
nodeName: master-1
|
||||
privilegedPods: false
|
||||
token: s73ybu.6tw6wnqgp5z0wb77
|
||||
tokenGroups:
|
||||
- system:bootstrappers:kubeadm:default-node-token
|
||||
tokenTTL: 24h0m0s
|
||||
tokenUsages:
|
||||
- signing
|
||||
- authentication
|
||||
unifiedControlPlaneImage: ""
|
@ -1,146 +0,0 @@
|
||||
# This file don't have TypeMeta set. kubeadm should then unmarshal it as a apiVersion=kubeadm.k8s.io/v1alpha1 and kind=MasterConfiguration
|
||||
api:
|
||||
advertiseAddress: 192.168.2.2
|
||||
bindPort: 6443
|
||||
controlPlaneEndpoint: ""
|
||||
auditPolicy:
|
||||
logDir: /var/log/kubernetes/audit
|
||||
logMaxAge: 2
|
||||
path: ""
|
||||
authorizationModes:
|
||||
- Node
|
||||
- RBAC
|
||||
- Webhook
|
||||
certificatesDir: /etc/kubernetes/pki
|
||||
cloudProvider: ""
|
||||
clusterName: kubernetes
|
||||
criSocket: /var/run/dockershim.sock
|
||||
etcd:
|
||||
caFile: ""
|
||||
certFile: ""
|
||||
dataDir: /var/lib/etcd
|
||||
endpoints: null
|
||||
image: ""
|
||||
keyFile: ""
|
||||
imageRepository: k8s.gcr.io
|
||||
kubeProxy:
|
||||
config:
|
||||
bindAddress: 0.0.0.0
|
||||
clientConnection:
|
||||
acceptContentTypes: ""
|
||||
burst: 10
|
||||
contentType: application/vnd.kubernetes.protobuf
|
||||
kubeconfig: /var/lib/kube-proxy/kubeconfig.conf
|
||||
qps: 5
|
||||
clusterCIDR: ""
|
||||
configSyncPeriod: 15m0s
|
||||
conntrack:
|
||||
max: null
|
||||
maxPerCore: 32768
|
||||
min: 131072
|
||||
tcpCloseWaitTimeout: 1h0m0s
|
||||
tcpEstablishedTimeout: 24h0m0s
|
||||
enableProfiling: false
|
||||
featureGates: "SupportIPVSProxyMode=true,ServiceNodeExclusion=true"
|
||||
healthzBindAddress: 0.0.0.0:10256
|
||||
hostnameOverride: ""
|
||||
iptables:
|
||||
masqueradeAll: false
|
||||
masqueradeBit: 14
|
||||
minSyncPeriod: 0s
|
||||
syncPeriod: 30s
|
||||
ipvs:
|
||||
excludeCIDRs: null
|
||||
minSyncPeriod: 0s
|
||||
scheduler: ""
|
||||
syncPeriod: 30s
|
||||
metricsBindAddress: 127.0.0.1:10249
|
||||
mode: ""
|
||||
nodePortAddresses: null
|
||||
oomScoreAdj: -999
|
||||
portRange: ""
|
||||
resourceContainer: /kube-proxy
|
||||
udpIdleTimeout: 250ms
|
||||
kubeletConfiguration:
|
||||
baseConfig:
|
||||
address: 0.0.0.0
|
||||
authentication:
|
||||
anonymous:
|
||||
enabled: false
|
||||
webhook:
|
||||
cacheTTL: 2m0s
|
||||
enabled: true
|
||||
x509:
|
||||
clientCAFile: /etc/kubernetes/pki/ca.crt
|
||||
authorization:
|
||||
mode: Webhook
|
||||
webhook:
|
||||
cacheAuthorizedTTL: 5m0s
|
||||
cacheUnauthorizedTTL: 30s
|
||||
cgroupDriver: cgroupfs
|
||||
cgroupsPerQOS: true
|
||||
clusterDNS:
|
||||
- 10.96.0.10
|
||||
clusterDomain: cluster.local
|
||||
containerLogMaxFiles: 5
|
||||
containerLogMaxSize: 10Mi
|
||||
contentType: application/vnd.kubernetes.protobuf
|
||||
cpuCFSQuota: true
|
||||
cpuManagerPolicy: none
|
||||
cpuManagerReconcilePeriod: 10s
|
||||
enableControllerAttachDetach: true
|
||||
enableDebuggingHandlers: true
|
||||
enforceNodeAllocatable:
|
||||
- pods
|
||||
eventBurst: 10
|
||||
eventRecordQPS: 5
|
||||
evictionHard:
|
||||
imagefs.available: 15%
|
||||
memory.available: 100Mi
|
||||
nodefs.available: 10%
|
||||
nodefs.inodesFree: 5%
|
||||
evictionPressureTransitionPeriod: 5m0s
|
||||
failSwapOn: true
|
||||
fileCheckFrequency: 20s
|
||||
hairpinMode: promiscuous-bridge
|
||||
healthzBindAddress: 127.0.0.1
|
||||
healthzPort: 10248
|
||||
httpCheckFrequency: 20s
|
||||
imageGCHighThresholdPercent: 85
|
||||
imageGCLowThresholdPercent: 80
|
||||
imageMinimumGCAge: 2m0s
|
||||
iptablesDropBit: 15
|
||||
iptablesMasqueradeBit: 14
|
||||
kubeAPIBurst: 10
|
||||
kubeAPIQPS: 5
|
||||
makeIPTablesUtilChains: true
|
||||
maxOpenFiles: 1000000
|
||||
maxPods: 110
|
||||
nodeStatusUpdateFrequency: 10s
|
||||
oomScoreAdj: -999
|
||||
podPidsLimit: -1
|
||||
port: 10250
|
||||
registryBurst: 10
|
||||
registryPullQPS: 5
|
||||
resolvConf: /etc/resolv.conf
|
||||
runtimeRequestTimeout: 2m0s
|
||||
serializeImagePulls: true
|
||||
staticPodPath: /etc/kubernetes/manifests
|
||||
streamingConnectionIdleTimeout: 4h0m0s
|
||||
syncFrequency: 1m0s
|
||||
volumeStatsAggPeriod: 1m0s
|
||||
kubernetesVersion: v1.10.2
|
||||
networking:
|
||||
dnsDomain: cluster.local
|
||||
podSubnet: ""
|
||||
serviceSubnet: 10.96.0.0/12
|
||||
nodeName: master-1
|
||||
privilegedPods: false
|
||||
token: s73ybu.6tw6wnqgp5z0wb77
|
||||
tokenGroups:
|
||||
- system:bootstrappers:kubeadm:default-node-token
|
||||
tokenTTL: 24h0m0s
|
||||
tokenUsages:
|
||||
- signing
|
||||
- authentication
|
||||
unifiedControlPlaneImage: ""
|
@ -1,148 +0,0 @@
|
||||
api:
|
||||
advertiseAddress: 192.168.2.2
|
||||
bindPort: 6443
|
||||
controlPlaneEndpoint: ""
|
||||
apiServerExtraArgs:
|
||||
authorization-mode: Node,RBAC,Webhook
|
||||
apiVersion: kubeadm.k8s.io/v1alpha2
|
||||
auditPolicy:
|
||||
logDir: /var/log/kubernetes/audit
|
||||
logMaxAge: 2
|
||||
path: ""
|
||||
bootstrapTokens:
|
||||
- groups:
|
||||
- system:bootstrappers:kubeadm:default-node-token
|
||||
token: s73ybu.6tw6wnqgp5z0wb77
|
||||
ttl: 24h0m0s
|
||||
usages:
|
||||
- signing
|
||||
- authentication
|
||||
certificatesDir: /etc/kubernetes/pki
|
||||
clusterName: kubernetes
|
||||
etcd:
|
||||
local:
|
||||
dataDir: /var/lib/etcd
|
||||
image: ""
|
||||
imageRepository: k8s.gcr.io
|
||||
kind: MasterConfiguration
|
||||
kubeProxy:
|
||||
config:
|
||||
bindAddress: 0.0.0.0
|
||||
clientConnection:
|
||||
acceptContentTypes: ""
|
||||
burst: 10
|
||||
contentType: application/vnd.kubernetes.protobuf
|
||||
kubeconfig: /var/lib/kube-proxy/kubeconfig.conf
|
||||
qps: 5
|
||||
clusterCIDR: ""
|
||||
configSyncPeriod: 15m0s
|
||||
conntrack:
|
||||
max: null
|
||||
maxPerCore: 32768
|
||||
min: 131072
|
||||
tcpCloseWaitTimeout: 1h0m0s
|
||||
tcpEstablishedTimeout: 24h0m0s
|
||||
enableProfiling: false
|
||||
featureGates:
|
||||
ServiceNodeExclusion: true
|
||||
SupportIPVSProxyMode: true
|
||||
healthzBindAddress: 0.0.0.0:10256
|
||||
hostnameOverride: ""
|
||||
iptables:
|
||||
masqueradeAll: false
|
||||
masqueradeBit: 14
|
||||
minSyncPeriod: 0s
|
||||
syncPeriod: 30s
|
||||
ipvs:
|
||||
excludeCIDRs: null
|
||||
minSyncPeriod: 0s
|
||||
scheduler: ""
|
||||
syncPeriod: 30s
|
||||
metricsBindAddress: 127.0.0.1:10249
|
||||
mode: ""
|
||||
nodePortAddresses: null
|
||||
oomScoreAdj: -999
|
||||
portRange: ""
|
||||
resourceContainer: /kube-proxy
|
||||
udpIdleTimeout: 250ms
|
||||
kubeletConfiguration:
|
||||
baseConfig:
|
||||
address: 0.0.0.0
|
||||
authentication:
|
||||
anonymous:
|
||||
enabled: false
|
||||
webhook:
|
||||
cacheTTL: 2m0s
|
||||
enabled: true
|
||||
x509:
|
||||
clientCAFile: /etc/kubernetes/pki/ca.crt
|
||||
authorization:
|
||||
mode: Webhook
|
||||
webhook:
|
||||
cacheAuthorizedTTL: 5m0s
|
||||
cacheUnauthorizedTTL: 30s
|
||||
cgroupDriver: cgroupfs
|
||||
cgroupsPerQOS: true
|
||||
clusterDNS:
|
||||
- 10.96.0.10
|
||||
clusterDomain: cluster.local
|
||||
containerLogMaxFiles: 5
|
||||
containerLogMaxSize: 10Mi
|
||||
contentType: application/vnd.kubernetes.protobuf
|
||||
cpuCFSQuota: true
|
||||
cpuManagerPolicy: none
|
||||
cpuManagerReconcilePeriod: 10s
|
||||
enableControllerAttachDetach: true
|
||||
enableDebuggingHandlers: true
|
||||
enforceNodeAllocatable:
|
||||
- pods
|
||||
eventBurst: 10
|
||||
eventRecordQPS: 5
|
||||
evictionHard:
|
||||
imagefs.available: 15%
|
||||
memory.available: 100Mi
|
||||
nodefs.available: 10%
|
||||
nodefs.inodesFree: 5%
|
||||
evictionPressureTransitionPeriod: 5m0s
|
||||
failSwapOn: true
|
||||
fileCheckFrequency: 20s
|
||||
hairpinMode: promiscuous-bridge
|
||||
healthzBindAddress: 127.0.0.1
|
||||
healthzPort: 10248
|
||||
httpCheckFrequency: 20s
|
||||
imageGCHighThresholdPercent: 85
|
||||
imageGCLowThresholdPercent: 80
|
||||
imageMinimumGCAge: 2m0s
|
||||
iptablesDropBit: 15
|
||||
iptablesMasqueradeBit: 14
|
||||
kubeAPIBurst: 10
|
||||
kubeAPIQPS: 5
|
||||
makeIPTablesUtilChains: true
|
||||
maxOpenFiles: 1000000
|
||||
maxPods: 110
|
||||
nodeStatusUpdateFrequency: 10s
|
||||
oomScoreAdj: -999
|
||||
podPidsLimit: -1
|
||||
port: 10250
|
||||
registryBurst: 10
|
||||
registryPullQPS: 5
|
||||
resolvConf: /etc/resolv.conf
|
||||
rotateCertificates: true
|
||||
runtimeRequestTimeout: 2m0s
|
||||
serializeImagePulls: true
|
||||
staticPodPath: /etc/kubernetes/manifests
|
||||
streamingConnectionIdleTimeout: 4h0m0s
|
||||
syncFrequency: 1m0s
|
||||
volumeStatsAggPeriod: 1m0s
|
||||
kubernetesVersion: v1.10.2
|
||||
networking:
|
||||
dnsDomain: cluster.local
|
||||
podSubnet: ""
|
||||
serviceSubnet: 10.96.0.0/12
|
||||
nodeRegistration:
|
||||
criSocket: /var/run/dockershim.sock
|
||||
name: master-1
|
||||
taints:
|
||||
- effect: NoSchedule
|
||||
key: node-role.kubernetes.io/master
|
||||
unifiedControlPlaneImage: ""
|
@ -1,17 +0,0 @@
|
||||
CACertPath: /etc/kubernetes/pki/ca.crt
|
||||
ClusterName: kubernetes
|
||||
DiscoveryFile: ""
|
||||
DiscoveryTimeout: 5m0s
|
||||
DiscoveryToken: abcdef.0123456789abcdef
|
||||
DiscoveryTokenAPIServers:
|
||||
- kube-apiserver:6443
|
||||
DiscoveryTokenCACertHashes: null
|
||||
DiscoveryTokenUnsafeSkipCAVerification: true
|
||||
FeatureGates: null
|
||||
NodeRegistration:
|
||||
CRISocket: /var/run/dockershim.sock
|
||||
KubeletExtraArgs: null
|
||||
Name: master-1
|
||||
Taints: null
|
||||
TLSBootstrapToken: abcdef.0123456789abcdef
|
||||
Token: abcdef.0123456789abcdef
|
@ -1,14 +0,0 @@
|
||||
apiVersion: kubeadm.k8s.io/v1alpha1
|
||||
kind: NodeConfiguration
|
||||
caCertPath: /etc/kubernetes/pki/ca.crt
|
||||
clusterName: kubernetes
|
||||
criSocket: /var/run/dockershim.sock
|
||||
discoveryFile: ""
|
||||
discoveryTimeout: 5m0s
|
||||
discoveryToken: abcdef.0123456789abcdef
|
||||
discoveryTokenAPIServers:
|
||||
- kube-apiserver:6443
|
||||
discoveryTokenUnsafeSkipCAVerification: true
|
||||
nodeName: master-1
|
||||
tlsBootstrapToken: abcdef.0123456789abcdef
|
||||
token: abcdef.0123456789abcdef
|
@ -1,15 +0,0 @@
|
||||
apiVersion: kubeadm.k8s.io/v1alpha2
|
||||
caCertPath: /etc/kubernetes/pki/ca.crt
|
||||
clusterName: kubernetes
|
||||
discoveryFile: ""
|
||||
discoveryTimeout: 5m0s
|
||||
discoveryToken: abcdef.0123456789abcdef
|
||||
discoveryTokenAPIServers:
|
||||
- kube-apiserver:6443
|
||||
discoveryTokenUnsafeSkipCAVerification: true
|
||||
kind: NodeConfiguration
|
||||
nodeRegistration:
|
||||
criSocket: /var/run/dockershim.sock
|
||||
name: master-1
|
||||
tlsBootstrapToken: abcdef.0123456789abcdef
|
||||
token: abcdef.0123456789abcdef
|
@ -1,143 +0,0 @@
|
||||
api:
|
||||
advertiseAddress: 192.168.2.2
|
||||
bindPort: 6443
|
||||
controlPlaneEndpoint: ""
|
||||
apiVersion: kubeadm.k8s.io/v1alpha2
|
||||
auditPolicy:
|
||||
logDir: /var/log/kubernetes/audit
|
||||
logMaxAge: 2
|
||||
path: ""
|
||||
bootstrapTokens:
|
||||
- groups:
|
||||
- system:bootstrappers:kubeadm:default-node-token
|
||||
token: s73ybu.6tw6wnqgp5z0wb77
|
||||
ttl: 24h0m0s
|
||||
usages:
|
||||
- signing
|
||||
- authentication
|
||||
certificatesDir: /var/lib/kubernetes/pki
|
||||
clusterName: kubernetes
|
||||
etcd:
|
||||
local:
|
||||
dataDir: /var/lib/etcd
|
||||
image: ""
|
||||
imageRepository: my-company.com
|
||||
kind: MasterConfiguration
|
||||
kubeProxy:
|
||||
config:
|
||||
bindAddress: 0.0.0.0
|
||||
clientConnection:
|
||||
acceptContentTypes: ""
|
||||
burst: 10
|
||||
contentType: application/vnd.kubernetes.protobuf
|
||||
kubeconfig: /var/lib/kube-proxy/kubeconfig.conf
|
||||
qps: 5
|
||||
clusterCIDR: ""
|
||||
configSyncPeriod: 15m0s
|
||||
conntrack:
|
||||
max: null
|
||||
maxPerCore: 32768
|
||||
min: 131072
|
||||
tcpCloseWaitTimeout: 1h0m0s
|
||||
tcpEstablishedTimeout: 24h0m0s
|
||||
enableProfiling: false
|
||||
healthzBindAddress: 0.0.0.0:10256
|
||||
hostnameOverride: ""
|
||||
iptables:
|
||||
masqueradeAll: false
|
||||
masqueradeBit: 14
|
||||
minSyncPeriod: 0s
|
||||
syncPeriod: 30s
|
||||
ipvs:
|
||||
excludeCIDRs: null
|
||||
minSyncPeriod: 0s
|
||||
scheduler: ""
|
||||
syncPeriod: 30s
|
||||
metricsBindAddress: 127.0.0.1:10249
|
||||
mode: ""
|
||||
nodePortAddresses: null
|
||||
oomScoreAdj: -999
|
||||
portRange: ""
|
||||
resourceContainer: /kube-proxy
|
||||
udpIdleTimeout: 250ms
|
||||
kubeletConfiguration:
|
||||
baseConfig:
|
||||
address: 0.0.0.0
|
||||
authentication:
|
||||
anonymous:
|
||||
enabled: false
|
||||
webhook:
|
||||
cacheTTL: 2m0s
|
||||
enabled: true
|
||||
x509:
|
||||
clientCAFile: /etc/kubernetes/pki/ca.crt
|
||||
authorization:
|
||||
mode: Webhook
|
||||
webhook:
|
||||
cacheAuthorizedTTL: 5m0s
|
||||
cacheUnauthorizedTTL: 30s
|
||||
cgroupDriver: cgroupfs
|
||||
cgroupsPerQOS: true
|
||||
clusterDNS:
|
||||
- 10.192.0.10
|
||||
clusterDomain: cluster.global
|
||||
containerLogMaxFiles: 5
|
||||
containerLogMaxSize: 10Mi
|
||||
contentType: application/vnd.kubernetes.protobuf
|
||||
cpuCFSQuota: true
|
||||
cpuManagerPolicy: none
|
||||
cpuManagerReconcilePeriod: 10s
|
||||
enableControllerAttachDetach: true
|
||||
enableDebuggingHandlers: true
|
||||
enforceNodeAllocatable:
|
||||
- pods
|
||||
eventBurst: 10
|
||||
eventRecordQPS: 5
|
||||
evictionHard:
|
||||
imagefs.available: 15%
|
||||
memory.available: 100Mi
|
||||
nodefs.available: 10%
|
||||
nodefs.inodesFree: 5%
|
||||
evictionPressureTransitionPeriod: 5m0s
|
||||
failSwapOn: true
|
||||
fileCheckFrequency: 20s
|
||||
hairpinMode: promiscuous-bridge
|
||||
healthzBindAddress: 127.0.0.1
|
||||
healthzPort: 10248
|
||||
httpCheckFrequency: 20s
|
||||
imageGCHighThresholdPercent: 85
|
||||
imageGCLowThresholdPercent: 80
|
||||
imageMinimumGCAge: 2m0s
|
||||
iptablesDropBit: 15
|
||||
iptablesMasqueradeBit: 14
|
||||
kubeAPIBurst: 10
|
||||
kubeAPIQPS: 5
|
||||
makeIPTablesUtilChains: true
|
||||
maxOpenFiles: 1000000
|
||||
maxPods: 110
|
||||
nodeStatusUpdateFrequency: 10s
|
||||
oomScoreAdj: -999
|
||||
podPidsLimit: -1
|
||||
port: 10250
|
||||
registryBurst: 10
|
||||
registryPullQPS: 5
|
||||
resolvConf: /etc/resolv.conf
|
||||
rotateCertificates: true
|
||||
runtimeRequestTimeout: 2m0s
|
||||
serializeImagePulls: true
|
||||
staticPodPath: /etc/kubernetes/manifests
|
||||
streamingConnectionIdleTimeout: 4h0m0s
|
||||
syncFrequency: 1m0s
|
||||
volumeStatsAggPeriod: 1m0s
|
||||
kubernetesVersion: v1.10.2
|
||||
networking:
|
||||
dnsDomain: cluster.global
|
||||
podSubnet: ""
|
||||
serviceSubnet: 10.196.0.0/12
|
||||
nodeRegistration:
|
||||
criSocket: /var/run/criruntime.sock
|
||||
name: master-1
|
||||
taints:
|
||||
- effect: NoSchedule
|
||||
key: node-role.kubernetes.io/master
|
||||
unifiedControlPlaneImage: ""
|
@ -1,15 +0,0 @@
|
||||
# This file _should_ set TypeMeta, but at some point earlier we supported deserializing MasterConfigurations without TypeMeta, so we need to support that as long as we
|
||||
# support the v1alpha1 API. In the meantime kubeadm will treat this as v1alpha1 automatically when unmarshalling.
|
||||
api:
|
||||
advertiseAddress: 192.168.2.2
|
||||
bindPort: 6443
|
||||
certificatesDir: /var/lib/kubernetes/pki
|
||||
clusterName: kubernetes
|
||||
criSocket: /var/run/criruntime.sock
|
||||
imageRepository: my-company.com
|
||||
kubernetesVersion: v1.10.2
|
||||
networking:
|
||||
dnsDomain: cluster.global
|
||||
serviceSubnet: 10.196.0.0/12
|
||||
nodeName: master-1
|
||||
token: s73ybu.6tw6wnqgp5z0wb77
|
@ -1,15 +0,0 @@
|
||||
apiVersion: kubeadm.k8s.io/v1alpha2
|
||||
caCertPath: /etc/kubernetes/pki/ca.crt
|
||||
clusterName: kubernetes
|
||||
discoveryFile: ""
|
||||
discoveryTimeout: 5m0s
|
||||
discoveryToken: abcdef.0123456789abcdef
|
||||
discoveryTokenAPIServers:
|
||||
- kube-apiserver:6443
|
||||
discoveryTokenUnsafeSkipCAVerification: true
|
||||
kind: NodeConfiguration
|
||||
nodeRegistration:
|
||||
criSocket: /var/run/dockershim.sock
|
||||
name: thegopher
|
||||
tlsBootstrapToken: abcdef.0123456789abcdef
|
||||
token: abcdef.0123456789abcdef
|
@ -1,7 +0,0 @@
|
||||
apiVersion: kubeadm.k8s.io/v1alpha1
|
||||
kind: NodeConfiguration
|
||||
discoveryTokenAPIServers:
|
||||
- kube-apiserver:6443
|
||||
discoveryTokenUnsafeSkipCAVerification: true
|
||||
nodeName: thegopher
|
||||
token: abcdef.0123456789abcdef
|
@ -1,73 +0,0 @@
|
||||
api:
|
||||
advertiseAddress: 172.31.93.180
|
||||
bindPort: 6443
|
||||
controlPlaneEndpoint: ""
|
||||
apiVersion: kubeadm.k8s.io/v1alpha1
|
||||
auditPolicy:
|
||||
logDir: ""
|
||||
path: ""
|
||||
authorizationModes:
|
||||
- Node
|
||||
- RBAC
|
||||
certificatesDir: /etc/kubernetes/pki
|
||||
cloudProvider: aws
|
||||
etcd:
|
||||
caFile: ""
|
||||
certFile: ""
|
||||
dataDir: /var/lib/etcd
|
||||
endpoints: null
|
||||
image: ""
|
||||
keyFile: ""
|
||||
imageRepository: gcr.io/google_containers
|
||||
kind: MasterConfiguration
|
||||
kubeProxy:
|
||||
config:
|
||||
bindAddress: 0.0.0.0
|
||||
clientConnection:
|
||||
acceptContentTypes: ""
|
||||
burst: 10
|
||||
contentType: application/vnd.kubernetes.protobuf
|
||||
kubeconfig: /var/lib/kube-proxy/kubeconfig.conf
|
||||
qps: 5
|
||||
clusterCIDR: 192.168.0.0/16
|
||||
configSyncPeriod: 15m0s
|
||||
conntrack:
|
||||
max: null
|
||||
maxPerCore: 32768
|
||||
min: 131072
|
||||
tcpCloseWaitTimeout: 1h0m0s
|
||||
tcpEstablishedTimeout: 24h0m0s
|
||||
enableProfiling: false
|
||||
featureGates:
|
||||
ServiceNodeExclusion: true
|
||||
SupportIPVSProxyMode: true
|
||||
healthzBindAddress: 0.0.0.0:10256
|
||||
hostnameOverride: ""
|
||||
iptables:
|
||||
masqueradeAll: false
|
||||
masqueradeBit: 14
|
||||
minSyncPeriod: 0s
|
||||
syncPeriod: 30s
|
||||
ipvs:
|
||||
excludeCIDRs: null
|
||||
minSyncPeriod: 0s
|
||||
scheduler: ""
|
||||
syncPeriod: 30s
|
||||
metricsBindAddress: 127.0.0.1:10249
|
||||
mode: ""
|
||||
nodePortAddresses: null
|
||||
oomScoreAdj: -999
|
||||
portRange: ""
|
||||
resourceContainer: /kube-proxy
|
||||
udpIdleTimeout: 0s
|
||||
kubeletConfiguration: {}
|
||||
kubernetesVersion: v1.9.6
|
||||
networking:
|
||||
dnsDomain: cluster.local
|
||||
podSubnet: 192.168.0.0/16
|
||||
serviceSubnet: 10.96.0.0/12
|
||||
nodeName: ip-172-31-93-180.ec2.internal
|
||||
privilegedPods: false
|
||||
token: 8d69af.cd3e1c58f6228dfc
|
||||
tokenTTL: 24h0m0s
|
||||
unifiedControlPlaneImage: ""
|
@ -1,64 +0,0 @@
|
||||
# This MasterConfiguration object is wrong in two ways: it hasn't TypeMeta set, and .kubeProxy.config.featureGates is a string as it was in v1.9
|
||||
# In v1.10 however, it changed in an inbackwards-compatible way to a map[string]string, so we have to workaround that to unmarshal this object
|
||||
api:
|
||||
advertiseAddress: 172.31.93.180
|
||||
bindPort: 6443
|
||||
authorizationModes:
|
||||
- Node
|
||||
- RBAC
|
||||
certificatesDir: /etc/kubernetes/pki
|
||||
cloudProvider: aws
|
||||
etcd:
|
||||
caFile: ""
|
||||
certFile: ""
|
||||
dataDir: /var/lib/etcd
|
||||
endpoints: null
|
||||
image: ""
|
||||
keyFile: ""
|
||||
imageRepository: gcr.io/google_containers
|
||||
kubeProxy:
|
||||
config:
|
||||
bindAddress: 0.0.0.0
|
||||
clientConnection:
|
||||
acceptContentTypes: ""
|
||||
burst: 10
|
||||
contentType: application/vnd.kubernetes.protobuf
|
||||
kubeconfig: /var/lib/kube-proxy/kubeconfig.conf
|
||||
qps: 5
|
||||
clusterCIDR: 192.168.0.0/16
|
||||
configSyncPeriod: 15m0s
|
||||
conntrack:
|
||||
max: null
|
||||
maxPerCore: 32768
|
||||
min: 131072
|
||||
tcpCloseWaitTimeout: 1h0m0s
|
||||
tcpEstablishedTimeout: 24h0m0s
|
||||
enableProfiling: false
|
||||
featureGates: "SupportIPVSProxyMode=true,ServiceNodeExclusion=true"
|
||||
healthzBindAddress: 0.0.0.0:10256
|
||||
hostnameOverride: ""
|
||||
iptables:
|
||||
masqueradeAll: false
|
||||
masqueradeBit: 14
|
||||
minSyncPeriod: 0s
|
||||
syncPeriod: 30s
|
||||
ipvs:
|
||||
minSyncPeriod: 0s
|
||||
scheduler: ""
|
||||
syncPeriod: 30s
|
||||
metricsBindAddress: 127.0.0.1:10249
|
||||
mode: ""
|
||||
oomScoreAdj: -999
|
||||
portRange: ""
|
||||
resourceContainer: /kube-proxy
|
||||
udpTimeoutMilliseconds: 250ms
|
||||
kubeletConfiguration: {}
|
||||
kubernetesVersion: v1.9.6
|
||||
networking:
|
||||
dnsDomain: cluster.local
|
||||
podSubnet: 192.168.0.0/16
|
||||
serviceSubnet: 10.96.0.0/12
|
||||
nodeName: ip-172-31-93-180.ec2.internal
|
||||
token: 8d69af.cd3e1c58f6228dfc
|
||||
tokenTTL: 24h0m0s
|
||||
unifiedControlPlaneImage: ""
|
@ -1,12 +0,0 @@
|
||||
api:
|
||||
bindPort: 0
|
||||
certificatesDir: relativepath
|
||||
clusterName: kubernetes
|
||||
criSocket: relativepath
|
||||
imageRepository: my-company.com
|
||||
kubernetesVersion: v1.10.2
|
||||
networking:
|
||||
dnsDomain: cluster.GLOBAL
|
||||
serviceSubnet: 10.196.1000.0/100
|
||||
nodeName: MASTER
|
||||
token: s7bu.6tw6wn
|
@ -1,11 +0,0 @@
|
||||
apiVersion: kubeadm.k8s.io/v1alpha1
|
||||
kind: NodeConfiguration
|
||||
caCertPath: relativepath
|
||||
criSocket: relativepath
|
||||
discoveryFile: relativepath
|
||||
discoveryTimeout: not-a-time
|
||||
discoveryTokenAPIServers:
|
||||
- INVALID_URL
|
||||
discoveryTokenUnsafeSkipCAVerification: false
|
||||
nodeName: NODE-1
|
||||
token: invalidtoken
|
31
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/copy.go
generated
vendored
31
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/copy.go
generated
vendored
@ -1,31 +0,0 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
// CopyDir copies the content of a folder
|
||||
func CopyDir(src string, dst string) error {
|
||||
cmd := exec.Command("cp", "-r", src, dst)
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
28
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/dryrun/BUILD
generated
vendored
28
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/dryrun/BUILD
generated
vendored
@ -1,28 +0,0 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["dryrun.go"],
|
||||
importpath = "k8s.io/kubernetes/cmd/kubeadm/app/util/dryrun",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//cmd/kubeadm/app/constants:go_default_library",
|
||||
"//cmd/kubeadm/app/util/apiclient:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
135
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/dryrun/dryrun.go
generated
vendored
135
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/dryrun/dryrun.go
generated
vendored
@ -1,135 +0,0 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package dryrun
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/errors"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient"
|
||||
)
|
||||
|
||||
// FileToPrint represents a temporary file on disk that might want to be aliased when printing
|
||||
// Useful for things like loading a file from /tmp/ but saying to the user "Would write file foo to /etc/kubernetes/..."
|
||||
type FileToPrint struct {
|
||||
RealPath string
|
||||
PrintPath string
|
||||
}
|
||||
|
||||
// NewFileToPrint makes a new instance of FileToPrint with the specified arguments
|
||||
func NewFileToPrint(realPath, printPath string) FileToPrint {
|
||||
return FileToPrint{
|
||||
RealPath: realPath,
|
||||
PrintPath: printPath,
|
||||
}
|
||||
}
|
||||
|
||||
// PrintDryRunFile is a helper method around PrintDryRunFiles
|
||||
func PrintDryRunFile(fileName, realDir, printDir string, w io.Writer) error {
|
||||
return PrintDryRunFiles([]FileToPrint{
|
||||
NewFileToPrint(filepath.Join(realDir, fileName), filepath.Join(printDir, fileName)),
|
||||
}, w)
|
||||
}
|
||||
|
||||
// PrintDryRunFiles prints the contents of the FileToPrints given to it to the writer w
|
||||
func PrintDryRunFiles(files []FileToPrint, w io.Writer) error {
|
||||
errs := []error{}
|
||||
for _, file := range files {
|
||||
if len(file.RealPath) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
fileBytes, err := ioutil.ReadFile(file.RealPath)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Make it possible to fake the path of the file; i.e. you may want to tell the user
|
||||
// "Here is what would be written to /etc/kubernetes/admin.conf", although you wrote it to /tmp/kubeadm-dryrun/admin.conf and are loading it from there
|
||||
// Fall back to the "real" path if PrintPath is not set
|
||||
outputFilePath := file.PrintPath
|
||||
if len(outputFilePath) == 0 {
|
||||
outputFilePath = file.RealPath
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, "[dryrun] Would write file %q with content:\n", outputFilePath)
|
||||
apiclient.PrintBytesWithLinePrefix(w, fileBytes, "\t")
|
||||
}
|
||||
return errors.NewAggregate(errs)
|
||||
}
|
||||
|
||||
// Waiter is an implementation of apiclient.Waiter that should be used for dry-running
|
||||
type Waiter struct{}
|
||||
|
||||
// NewWaiter returns a new Waiter object that talks to the given Kubernetes cluster
|
||||
func NewWaiter() apiclient.Waiter {
|
||||
return &Waiter{}
|
||||
}
|
||||
|
||||
// WaitForAPI just returns a dummy nil, to indicate that the program should just proceed
|
||||
func (w *Waiter) WaitForAPI() error {
|
||||
fmt.Println("[dryrun] Would wait for the API Server's /healthz endpoint to return 'ok'")
|
||||
return nil
|
||||
}
|
||||
|
||||
// WaitForPodsWithLabel just returns a dummy nil, to indicate that the program should just proceed
|
||||
func (w *Waiter) WaitForPodsWithLabel(kvLabel string) error {
|
||||
fmt.Printf("[dryrun] Would wait for the Pods with the label %q in the %s namespace to become Running\n", kvLabel, metav1.NamespaceSystem)
|
||||
return nil
|
||||
}
|
||||
|
||||
// WaitForPodToDisappear just returns a dummy nil, to indicate that the program should just proceed
|
||||
func (w *Waiter) WaitForPodToDisappear(podName string) error {
|
||||
fmt.Printf("[dryrun] Would wait for the %q Pod in the %s namespace to be deleted\n", podName, metav1.NamespaceSystem)
|
||||
return nil
|
||||
}
|
||||
|
||||
// WaitForHealthyKubelet blocks until the kubelet /healthz endpoint returns 'ok'
|
||||
func (w *Waiter) WaitForHealthyKubelet(_ time.Duration, healthzEndpoint string) error {
|
||||
fmt.Printf("[dryrun] Would make sure the kubelet %q endpoint is healthy\n", healthzEndpoint)
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetTimeout is a no-op; we don't wait in this implementation
|
||||
func (w *Waiter) SetTimeout(_ time.Duration) {}
|
||||
|
||||
// WaitForStaticPodControlPlaneHashes returns an empty hash for all control plane images;
|
||||
func (w *Waiter) WaitForStaticPodControlPlaneHashes(_ string) (map[string]string, error) {
|
||||
return map[string]string{
|
||||
constants.KubeAPIServer: "",
|
||||
constants.KubeControllerManager: "",
|
||||
constants.KubeScheduler: "",
|
||||
}, nil
|
||||
}
|
||||
|
||||
// WaitForStaticPodSingleHash returns an empty hash
|
||||
// but the empty strings there are needed
|
||||
func (w *Waiter) WaitForStaticPodSingleHash(_ string, _ string) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// WaitForStaticPodHashChange returns a dummy nil error in order for the flow to just continue as we're dryrunning
|
||||
func (w *Waiter) WaitForStaticPodHashChange(_, _, _ string) error {
|
||||
return nil
|
||||
}
|
119
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/endpoint.go
generated
vendored
119
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/endpoint.go
generated
vendored
@ -1,119 +0,0 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/validation"
|
||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||
)
|
||||
|
||||
// GetMasterEndpoint returns a properly formatted endpoint for the control plane built according following rules:
|
||||
// - If the api.ControlPlaneEndpoint is defined, use it.
|
||||
// - if the api.ControlPlaneEndpoint is defined but without a port number, use the api.ControlPlaneEndpoint + api.BindPort is used.
|
||||
// - Otherwise, in case the api.ControlPlaneEndpoint is not defined, use the api.AdvertiseAddress + the api.BindPort.
|
||||
func GetMasterEndpoint(api *kubeadmapi.API) (string, error) {
|
||||
// parse the bind port
|
||||
var bindPort = strconv.Itoa(int(api.BindPort))
|
||||
if _, err := parsePort(bindPort); err != nil {
|
||||
return "", fmt.Errorf("invalid value %q given for api.bindPort: %s", api.BindPort, err)
|
||||
}
|
||||
|
||||
// parse the AdvertiseAddress
|
||||
var ip = net.ParseIP(api.AdvertiseAddress)
|
||||
if ip == nil {
|
||||
return "", fmt.Errorf("invalid value `%s` given for api.advertiseAddress", api.AdvertiseAddress)
|
||||
}
|
||||
|
||||
// set the master url using cfg.API.AdvertiseAddress + the cfg.API.BindPort
|
||||
masterURL := &url.URL{
|
||||
Scheme: "https",
|
||||
Host: net.JoinHostPort(ip.String(), bindPort),
|
||||
}
|
||||
|
||||
// if the controlplane endpoint is defined
|
||||
if len(api.ControlPlaneEndpoint) > 0 {
|
||||
// parse the controlplane endpoint
|
||||
var host, port string
|
||||
var err error
|
||||
if host, port, err = ParseHostPort(api.ControlPlaneEndpoint); err != nil {
|
||||
return "", fmt.Errorf("invalid value %q given for api.controlPlaneEndpoint: %s", api.ControlPlaneEndpoint, err)
|
||||
}
|
||||
|
||||
// if a port is provided within the controlPlaneAddress warn the users we are using it, else use the bindport
|
||||
if port != "" {
|
||||
fmt.Println("[endpoint] WARNING: port specified in api.controlPlaneEndpoint overrides api.bindPort in the controlplane address")
|
||||
} else {
|
||||
port = bindPort
|
||||
}
|
||||
|
||||
// overrides the master url using the controlPlaneAddress (and eventually the bindport)
|
||||
masterURL = &url.URL{
|
||||
Scheme: "https",
|
||||
Host: net.JoinHostPort(host, port),
|
||||
}
|
||||
}
|
||||
|
||||
return masterURL.String(), nil
|
||||
}
|
||||
|
||||
// ParseHostPort parses a network address of the form "host:port", "ipv4:port", "[ipv6]:port" into host and port;
|
||||
// ":port" can be eventually omitted.
|
||||
// If the string is not a valid representation of network address, ParseHostPort returns an error.
|
||||
func ParseHostPort(hostport string) (string, string, error) {
|
||||
var host, port string
|
||||
var err error
|
||||
|
||||
// try to split host and port
|
||||
if host, port, err = net.SplitHostPort(hostport); err != nil {
|
||||
// if SplitHostPort returns an error, the entire hostport is considered as host
|
||||
host = hostport
|
||||
}
|
||||
|
||||
// if port is defined, parse and validate it
|
||||
if port != "" {
|
||||
if _, err := parsePort(port); err != nil {
|
||||
return "", "", fmt.Errorf("port must be a valid number between 1 and 65535, inclusive")
|
||||
}
|
||||
}
|
||||
|
||||
// if host is a valid IP, returns it
|
||||
if ip := net.ParseIP(host); ip != nil {
|
||||
return host, port, nil
|
||||
}
|
||||
|
||||
// if host is a validate RFC-1123 subdomain, returns it
|
||||
if errs := validation.IsDNS1123Subdomain(host); len(errs) == 0 {
|
||||
return host, port, nil
|
||||
}
|
||||
|
||||
return "", "", fmt.Errorf("host must be a valid IP address or a valid RFC-1123 DNS subdomain")
|
||||
}
|
||||
|
||||
// ParsePort parses a string representing a TCP port.
|
||||
// If the string is not a valid representation of a TCP port, ParsePort returns an error.
|
||||
func parsePort(port string) (int, error) {
|
||||
if portInt, err := strconv.Atoi(port); err == nil && (1 <= portInt && portInt <= 65535) {
|
||||
return portInt, nil
|
||||
}
|
||||
|
||||
return 0, fmt.Errorf("port must be a valid number between 1 and 65535, inclusive")
|
||||
}
|
345
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/endpoint_test.go
generated
vendored
345
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/endpoint_test.go
generated
vendored
@ -1,345 +0,0 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||
)
|
||||
|
||||
func TestGetMasterEndpoint(t *testing.T) {
|
||||
var tests = []struct {
|
||||
name string
|
||||
api *kubeadmapi.API
|
||||
expectedEndpoint string
|
||||
expectedError bool
|
||||
}{
|
||||
{
|
||||
name: "use ControlPlaneEndpoint (dns) if fully defined",
|
||||
api: &kubeadmapi.API{
|
||||
ControlPlaneEndpoint: "cp.k8s.io:1234",
|
||||
BindPort: 4567,
|
||||
AdvertiseAddress: "4.5.6.7",
|
||||
},
|
||||
expectedEndpoint: "https://cp.k8s.io:1234",
|
||||
},
|
||||
{
|
||||
name: "use ControlPlaneEndpoint (ipv4) if fully defined",
|
||||
api: &kubeadmapi.API{
|
||||
ControlPlaneEndpoint: "1.2.3.4:1234",
|
||||
BindPort: 4567,
|
||||
AdvertiseAddress: "4.5.6.7",
|
||||
},
|
||||
expectedEndpoint: "https://1.2.3.4:1234",
|
||||
},
|
||||
{
|
||||
name: "use ControlPlaneEndpoint (ipv6) if fully defined",
|
||||
api: &kubeadmapi.API{
|
||||
ControlPlaneEndpoint: "[2001:db8::1]:1234",
|
||||
BindPort: 4567,
|
||||
AdvertiseAddress: "4.5.6.7",
|
||||
},
|
||||
expectedEndpoint: "https://[2001:db8::1]:1234",
|
||||
},
|
||||
{
|
||||
name: "use ControlPlaneEndpoint (dns) + BindPort if ControlPlaneEndpoint defined without port",
|
||||
api: &kubeadmapi.API{
|
||||
ControlPlaneEndpoint: "cp.k8s.io",
|
||||
BindPort: 4567,
|
||||
AdvertiseAddress: "4.5.6.7",
|
||||
},
|
||||
expectedEndpoint: "https://cp.k8s.io:4567",
|
||||
},
|
||||
{
|
||||
name: "use ControlPlaneEndpoint (ipv4) + BindPort if ControlPlaneEndpoint defined without port",
|
||||
api: &kubeadmapi.API{
|
||||
ControlPlaneEndpoint: "1.2.3.4",
|
||||
BindPort: 4567,
|
||||
AdvertiseAddress: "4.5.6.7",
|
||||
},
|
||||
expectedEndpoint: "https://1.2.3.4:4567",
|
||||
},
|
||||
{
|
||||
name: "use ControlPlaneEndpoint (ipv6) + BindPort if ControlPlaneEndpoint defined without port",
|
||||
api: &kubeadmapi.API{
|
||||
ControlPlaneEndpoint: "2001:db8::1",
|
||||
BindPort: 4567,
|
||||
AdvertiseAddress: "4.5.6.7",
|
||||
},
|
||||
expectedEndpoint: "https://[2001:db8::1]:4567",
|
||||
},
|
||||
{
|
||||
name: "use AdvertiseAddress (ipv4) + BindPort if ControlPlaneEndpoint is not defined",
|
||||
api: &kubeadmapi.API{
|
||||
BindPort: 4567,
|
||||
AdvertiseAddress: "4.5.6.7",
|
||||
},
|
||||
expectedEndpoint: "https://4.5.6.7:4567",
|
||||
},
|
||||
{
|
||||
name: "use AdvertiseAddress (ipv6) + BindPort if ControlPlaneEndpoint is not defined",
|
||||
api: &kubeadmapi.API{
|
||||
BindPort: 4567,
|
||||
AdvertiseAddress: "2001:db8::1",
|
||||
},
|
||||
expectedEndpoint: "https://[2001:db8::1]:4567",
|
||||
},
|
||||
{
|
||||
name: "fail if invalid BindPort",
|
||||
api: &kubeadmapi.API{
|
||||
BindPort: 0,
|
||||
},
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
name: "fail if invalid ControlPlaneEndpoint (dns)",
|
||||
api: &kubeadmapi.API{
|
||||
ControlPlaneEndpoint: "bad!!.cp.k8s.io",
|
||||
BindPort: 4567,
|
||||
},
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
name: "fail if invalid ControlPlaneEndpoint (ip4)",
|
||||
api: &kubeadmapi.API{
|
||||
ControlPlaneEndpoint: "1..0",
|
||||
BindPort: 4567,
|
||||
},
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
name: "fail if invalid ControlPlaneEndpoint (ip6)",
|
||||
api: &kubeadmapi.API{
|
||||
ControlPlaneEndpoint: "1200::AB00:1234::2552:7777:1313",
|
||||
BindPort: 4567,
|
||||
},
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
name: "fail if invalid ControlPlaneEndpoint (port)",
|
||||
api: &kubeadmapi.API{
|
||||
ControlPlaneEndpoint: "cp.k8s.io:0",
|
||||
BindPort: 4567,
|
||||
},
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
name: "fail if invalid AdvertiseAddress (ip4)",
|
||||
api: &kubeadmapi.API{
|
||||
AdvertiseAddress: "1..0",
|
||||
BindPort: 4567,
|
||||
},
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
name: "fail if invalid AdvertiseAddress (ip6)",
|
||||
api: &kubeadmapi.API{
|
||||
AdvertiseAddress: "1200::AB00:1234::2552:7777:1313",
|
||||
BindPort: 4567,
|
||||
},
|
||||
expectedError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, rt := range tests {
|
||||
actualEndpoint, actualError := GetMasterEndpoint(rt.api)
|
||||
|
||||
if (actualError != nil) && !rt.expectedError {
|
||||
t.Errorf("%s unexpected failure: %v", rt.name, actualError)
|
||||
continue
|
||||
} else if (actualError == nil) && rt.expectedError {
|
||||
t.Errorf("%s passed when expected to fail", rt.name)
|
||||
continue
|
||||
}
|
||||
|
||||
if actualEndpoint != rt.expectedEndpoint {
|
||||
t.Errorf("%s returned invalid endpoint %s, expected %s", rt.name, actualEndpoint, rt.expectedEndpoint)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseHostPort(t *testing.T) {
|
||||
|
||||
var tests = []struct {
|
||||
name string
|
||||
hostport string
|
||||
expectedHost string
|
||||
expectedPort string
|
||||
expectedError bool
|
||||
}{
|
||||
{
|
||||
name: "valid dns",
|
||||
hostport: "cp.k8s.io",
|
||||
expectedHost: "cp.k8s.io",
|
||||
expectedPort: "",
|
||||
},
|
||||
{
|
||||
name: "valid dns:port",
|
||||
hostport: "cp.k8s.io:1234",
|
||||
expectedHost: "cp.k8s.io",
|
||||
expectedPort: "1234",
|
||||
},
|
||||
{
|
||||
name: "valid ip4",
|
||||
hostport: "1.2.3.4",
|
||||
expectedHost: "1.2.3.4",
|
||||
expectedPort: "",
|
||||
},
|
||||
{
|
||||
name: "valid ipv4:port",
|
||||
hostport: "1.2.3.4:1234",
|
||||
expectedHost: "1.2.3.4",
|
||||
expectedPort: "1234",
|
||||
},
|
||||
{
|
||||
name: "valid ipv6",
|
||||
hostport: "2001:db8::1",
|
||||
expectedHost: "2001:db8::1",
|
||||
expectedPort: "",
|
||||
},
|
||||
{
|
||||
name: "valid ipv6:port",
|
||||
hostport: "[2001:db8::1]:1234",
|
||||
expectedHost: "2001:db8::1",
|
||||
expectedPort: "1234",
|
||||
},
|
||||
{
|
||||
name: "invalid port(not a number)",
|
||||
hostport: "cp.k8s.io:aaa",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
name: "invalid port(out of range, positive port number)",
|
||||
hostport: "cp.k8s.io:987654321",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
name: "invalid port(out of range, negative port number)",
|
||||
hostport: "cp.k8s.io:-987654321",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
name: "invalid port(out of range, negative port number)",
|
||||
hostport: "cp.k8s.io:123:123",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
name: "invalid dns",
|
||||
hostport: "bad!!cp.k8s.io",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
name: "invalid valid dns:port",
|
||||
hostport: "bad!!cp.k8s.io:1234",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
name: "invalid ip4, but valid DNS",
|
||||
hostport: "259.2.3.4",
|
||||
expectedHost: "259.2.3.4",
|
||||
},
|
||||
{
|
||||
name: "invalid ip4",
|
||||
hostport: "1..3.4",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
name: "invalid ip4(2):port",
|
||||
hostport: "1..3.4:1234",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
name: "invalid ipv6",
|
||||
hostport: "1200::AB00:1234::2552:7777:1313",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
name: "invalid ipv6:port",
|
||||
hostport: "[1200::AB00:1234::2552:7777:1313]:1234",
|
||||
expectedError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, rt := range tests {
|
||||
actualHost, actualPort, actualError := ParseHostPort(rt.hostport)
|
||||
|
||||
if (actualError != nil) && !rt.expectedError {
|
||||
t.Errorf("%s unexpected failure: %v", rt.name, actualError)
|
||||
continue
|
||||
} else if (actualError == nil) && rt.expectedError {
|
||||
t.Errorf("%s passed when expected to fail", rt.name)
|
||||
continue
|
||||
}
|
||||
|
||||
if actualHost != rt.expectedHost {
|
||||
t.Errorf("%s returned invalid host %s, expected %s", rt.name, actualHost, rt.expectedHost)
|
||||
continue
|
||||
}
|
||||
|
||||
if actualPort != rt.expectedPort {
|
||||
t.Errorf("%s returned invalid port %s, expected %s", rt.name, actualPort, rt.expectedPort)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParsePort(t *testing.T) {
|
||||
|
||||
var tests = []struct {
|
||||
name string
|
||||
port string
|
||||
expectedPort int
|
||||
expectedError bool
|
||||
}{
|
||||
{
|
||||
name: "valid port",
|
||||
port: "1234",
|
||||
expectedPort: 1234,
|
||||
},
|
||||
{
|
||||
name: "invalid port (not a number)",
|
||||
port: "a",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
name: "invalid port (<1)",
|
||||
port: "-10",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
name: "invalid port (>65535)",
|
||||
port: "66535",
|
||||
expectedError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, rt := range tests {
|
||||
actualPort, actualError := parsePort(rt.port)
|
||||
|
||||
if (actualError != nil) && !rt.expectedError {
|
||||
t.Errorf("%s unexpected failure: %v", rt.name, actualError)
|
||||
continue
|
||||
} else if (actualError == nil) && rt.expectedError {
|
||||
t.Errorf("%s passed when expected to fail", rt.name)
|
||||
continue
|
||||
}
|
||||
|
||||
if actualPort != rt.expectedPort {
|
||||
t.Errorf("%s returned invalid port %d, expected %d", rt.name, actualPort, rt.expectedPort)
|
||||
}
|
||||
}
|
||||
}
|
87
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/error.go
generated
vendored
87
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/error.go
generated
vendored
@ -1,87 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 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 util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
// DefaultErrorExitCode defines exit the code for failed action generally
|
||||
DefaultErrorExitCode = 1
|
||||
// PreFlightExitCode defines exit the code for preflight checks
|
||||
PreFlightExitCode = 2
|
||||
// ValidationExitCode defines the exit code validation checks
|
||||
ValidationExitCode = 3
|
||||
)
|
||||
|
||||
// fatal prints the message if set and then exits.
|
||||
func fatal(msg string, code int) {
|
||||
if len(msg) > 0 {
|
||||
// add newline if needed
|
||||
if !strings.HasSuffix(msg, "\n") {
|
||||
msg += "\n"
|
||||
}
|
||||
|
||||
fmt.Fprint(os.Stderr, msg)
|
||||
}
|
||||
os.Exit(code)
|
||||
}
|
||||
|
||||
// CheckErr prints a user friendly error to STDERR and exits with a non-zero
|
||||
// exit code. Unrecognized errors will be printed with an "error: " prefix.
|
||||
//
|
||||
// This method is generic to the command in use and may be used by non-Kubectl
|
||||
// commands.
|
||||
func CheckErr(err error) {
|
||||
checkErr(err, fatal)
|
||||
}
|
||||
|
||||
// preflightError allows us to know if the error is a preflight error or not
|
||||
// defining the interface here avoids an import cycle of pulling in preflight into the util package
|
||||
type preflightError interface {
|
||||
Preflight() bool
|
||||
}
|
||||
|
||||
// checkErr formats a given error as a string and calls the passed handleErr
|
||||
// func with that string and an exit code.
|
||||
func checkErr(err error, handleErr func(string, int)) {
|
||||
switch err.(type) {
|
||||
case nil:
|
||||
return
|
||||
case preflightError:
|
||||
handleErr(err.Error(), PreFlightExitCode)
|
||||
case utilerrors.Aggregate:
|
||||
handleErr(err.Error(), ValidationExitCode)
|
||||
|
||||
default:
|
||||
handleErr(err.Error(), DefaultErrorExitCode)
|
||||
}
|
||||
}
|
||||
|
||||
// FormatErrMsg returns a human-readable string describing the slice of errors passed to the function
|
||||
func FormatErrMsg(errs []error) string {
|
||||
var errMsg string
|
||||
for _, err := range errs {
|
||||
errMsg = fmt.Sprintf("%s\t- %s\n", errMsg, err.Error())
|
||||
}
|
||||
return errMsg
|
||||
}
|
85
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/error_test.go
generated
vendored
85
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/error_test.go
generated
vendored
@ -1,85 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 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 util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type pferror struct{}
|
||||
|
||||
func (p *pferror) Preflight() bool { return true }
|
||||
func (p *pferror) Error() string { return "" }
|
||||
func TestCheckErr(t *testing.T) {
|
||||
var codeReturned int
|
||||
errHandle := func(err string, code int) {
|
||||
codeReturned = code
|
||||
}
|
||||
|
||||
var tokenTest = []struct {
|
||||
e error
|
||||
expected int
|
||||
}{
|
||||
{nil, 0},
|
||||
{fmt.Errorf(""), DefaultErrorExitCode},
|
||||
{&pferror{}, PreFlightExitCode},
|
||||
}
|
||||
|
||||
for _, rt := range tokenTest {
|
||||
codeReturned = 0
|
||||
checkErr(rt.e, errHandle)
|
||||
if codeReturned != rt.expected {
|
||||
t.Errorf(
|
||||
"failed checkErr:\n\texpected: %d\n\t actual: %d",
|
||||
rt.expected,
|
||||
codeReturned,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatErrMsg(t *testing.T) {
|
||||
errMsg1 := "specified version to upgrade to v1.9.0-alpha.3 is equal to or lower than the cluster version v1.10.0-alpha.0.69+638add6ddfb6d2. Downgrades are not supported yet"
|
||||
errMsg2 := "specified version to upgrade to v1.9.0-alpha.3 is higher than the kubeadm version v1.9.0-alpha.1.3121+84178212527295-dirty. Upgrade kubeadm first using the tool you used to install kubeadm"
|
||||
|
||||
testCases := []struct {
|
||||
errs []error
|
||||
expect string
|
||||
}{
|
||||
{
|
||||
errs: []error{
|
||||
fmt.Errorf(errMsg1),
|
||||
fmt.Errorf(errMsg2),
|
||||
},
|
||||
expect: "\t- " + errMsg1 + "\n" + "\t- " + errMsg2 + "\n",
|
||||
},
|
||||
{
|
||||
errs: []error{
|
||||
fmt.Errorf(errMsg1),
|
||||
},
|
||||
expect: "\t- " + errMsg1 + "\n",
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
got := FormatErrMsg(testCase.errs)
|
||||
if got != testCase.expect {
|
||||
t.Errorf("FormatErrMsg error, expect: %v, got: %v", testCase.expect, got)
|
||||
}
|
||||
}
|
||||
}
|
39
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/etcd/BUILD
generated
vendored
39
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/etcd/BUILD
generated
vendored
@ -1,39 +0,0 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["etcd.go"],
|
||||
importpath = "k8s.io/kubernetes/cmd/kubeadm/app/util/etcd",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
|
||||
"//cmd/kubeadm/app/constants:go_default_library",
|
||||
"//cmd/kubeadm/app/util/staticpod:go_default_library",
|
||||
"//vendor/github.com/coreos/etcd/clientv3:go_default_library",
|
||||
"//vendor/github.com/coreos/etcd/pkg/transport:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["etcd_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
|
||||
"//cmd/kubeadm/test:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
224
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/etcd/etcd.go
generated
vendored
224
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/etcd/etcd.go
generated
vendored
@ -1,224 +0,0 @@
|
||||
/*
|
||||
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 etcd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/etcd/clientv3"
|
||||
"github.com/coreos/etcd/pkg/transport"
|
||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/util/staticpod"
|
||||
)
|
||||
|
||||
// ClusterInterrogator is an interface to get etcd cluster related information
|
||||
type ClusterInterrogator interface {
|
||||
ClusterAvailable() (bool, error)
|
||||
GetClusterStatus() (map[string]*clientv3.StatusResponse, error)
|
||||
GetClusterVersions() (map[string]string, error)
|
||||
GetVersion() (string, error)
|
||||
HasTLS() bool
|
||||
WaitForClusterAvailable(delay time.Duration, retries int, retryInterval time.Duration) (bool, error)
|
||||
}
|
||||
|
||||
// Client provides connection parameters for an etcd cluster
|
||||
type Client struct {
|
||||
Endpoints []string
|
||||
TLS *tls.Config
|
||||
}
|
||||
|
||||
// HasTLS returns true if etcd is configured for TLS
|
||||
func (c Client) HasTLS() bool {
|
||||
return c.TLS != nil
|
||||
}
|
||||
|
||||
// PodManifestsHaveTLS reads the etcd staticpod manifest from disk and returns false if the TLS flags
|
||||
// are missing from the command list. If all the flags are present it returns true.
|
||||
func PodManifestsHaveTLS(ManifestDir string) (bool, error) {
|
||||
etcdPodPath := constants.GetStaticPodFilepath(constants.Etcd, ManifestDir)
|
||||
etcdPod, err := staticpod.ReadStaticPodFromDisk(etcdPodPath)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to check if etcd pod implements TLS: %v", err)
|
||||
}
|
||||
|
||||
tlsFlags := []string{
|
||||
"--cert-file=",
|
||||
"--key-file=",
|
||||
"--trusted-ca-file=",
|
||||
"--client-cert-auth=",
|
||||
"--peer-cert-file=",
|
||||
"--peer-key-file=",
|
||||
"--peer-trusted-ca-file=",
|
||||
"--peer-client-cert-auth=",
|
||||
}
|
||||
FlagLoop:
|
||||
for _, flag := range tlsFlags {
|
||||
for _, container := range etcdPod.Spec.Containers {
|
||||
for _, arg := range container.Command {
|
||||
if strings.Contains(arg, flag) {
|
||||
continue FlagLoop
|
||||
}
|
||||
}
|
||||
}
|
||||
// flag not found in any container
|
||||
return false, nil
|
||||
}
|
||||
// all flags were found in container args; pod fully implements TLS
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// New creates a new EtcdCluster client
|
||||
func New(endpoints []string, ca, cert, key string) (*Client, error) {
|
||||
client := Client{Endpoints: endpoints}
|
||||
|
||||
if ca != "" || cert != "" || key != "" {
|
||||
tlsInfo := transport.TLSInfo{
|
||||
CertFile: cert,
|
||||
KeyFile: key,
|
||||
TrustedCAFile: ca,
|
||||
}
|
||||
tlsConfig, err := tlsInfo.ClientConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client.TLS = tlsConfig
|
||||
}
|
||||
|
||||
return &client, nil
|
||||
}
|
||||
|
||||
// NewFromStaticPod creates a GenericClient from the given endpoints, manifestDir, and certificatesDir
|
||||
func NewFromStaticPod(endpoints []string, manifestDir string, certificatesDir string) (*Client, error) {
|
||||
hasTLS, err := PodManifestsHaveTLS(manifestDir)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not read manifests from: %s, error: %v", manifestDir, err)
|
||||
}
|
||||
if hasTLS {
|
||||
return New(
|
||||
endpoints,
|
||||
filepath.Join(certificatesDir, constants.EtcdCACertName),
|
||||
filepath.Join(certificatesDir, constants.EtcdHealthcheckClientCertName),
|
||||
filepath.Join(certificatesDir, constants.EtcdHealthcheckClientKeyName),
|
||||
)
|
||||
}
|
||||
return New(endpoints, "", "", "")
|
||||
}
|
||||
|
||||
// GetVersion returns the etcd version of the cluster.
|
||||
// An error is returned if the version of all endpoints do not match
|
||||
func (c Client) GetVersion() (string, error) {
|
||||
var clusterVersion string
|
||||
|
||||
versions, err := c.GetClusterVersions()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for _, v := range versions {
|
||||
if clusterVersion != "" && clusterVersion != v {
|
||||
return "", fmt.Errorf("etcd cluster contains endpoints with mismatched versions: %v", versions)
|
||||
}
|
||||
clusterVersion = v
|
||||
}
|
||||
if clusterVersion == "" {
|
||||
return "", fmt.Errorf("could not determine cluster etcd version")
|
||||
}
|
||||
return clusterVersion, nil
|
||||
}
|
||||
|
||||
// GetClusterVersions returns a map of the endpoints and their associated versions
|
||||
func (c Client) GetClusterVersions() (map[string]string, error) {
|
||||
versions := make(map[string]string)
|
||||
statuses, err := c.GetClusterStatus()
|
||||
if err != nil {
|
||||
return versions, err
|
||||
}
|
||||
|
||||
for ep, status := range statuses {
|
||||
versions[ep] = status.Version
|
||||
}
|
||||
return versions, nil
|
||||
}
|
||||
|
||||
// ClusterAvailable returns true if the cluster status indicates the cluster is available.
|
||||
func (c Client) ClusterAvailable() (bool, error) {
|
||||
_, err := c.GetClusterStatus()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// GetClusterStatus returns nil for status Up or error for status Down
|
||||
func (c Client) GetClusterStatus() (map[string]*clientv3.StatusResponse, error) {
|
||||
cli, err := clientv3.New(clientv3.Config{
|
||||
Endpoints: c.Endpoints,
|
||||
DialTimeout: 5 * time.Second,
|
||||
TLS: c.TLS,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer cli.Close()
|
||||
|
||||
clusterStatus := make(map[string]*clientv3.StatusResponse)
|
||||
for _, ep := range c.Endpoints {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
resp, err := cli.Status(ctx, ep)
|
||||
cancel()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
clusterStatus[ep] = resp
|
||||
}
|
||||
return clusterStatus, nil
|
||||
}
|
||||
|
||||
// WaitForClusterAvailable returns true if all endpoints in the cluster are available after an initial delay and retry attempts, an error is returned otherwise
|
||||
func (c Client) WaitForClusterAvailable(delay time.Duration, retries int, retryInterval time.Duration) (bool, error) {
|
||||
fmt.Printf("[util/etcd] Waiting %v for initial delay\n", delay)
|
||||
time.Sleep(delay)
|
||||
for i := 0; i < retries; i++ {
|
||||
if i > 0 {
|
||||
fmt.Printf("[util/etcd] Waiting %v until next retry\n", retryInterval)
|
||||
time.Sleep(retryInterval)
|
||||
}
|
||||
fmt.Printf("[util/etcd] Attempting to see if all cluster endpoints are available %d/%d\n", i+1, retries)
|
||||
resp, err := c.ClusterAvailable()
|
||||
if err != nil {
|
||||
switch err {
|
||||
case context.DeadlineExceeded:
|
||||
fmt.Println("[util/etcd] Attempt timed out")
|
||||
default:
|
||||
fmt.Printf("[util/etcd] Attempt failed with error: %v\n", err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
return false, fmt.Errorf("timeout waiting for etcd cluster to be available")
|
||||
}
|
||||
|
||||
// CheckConfigurationIsHA returns true if the given MasterConfiguration etcd block appears to be an HA configuration.
|
||||
func CheckConfigurationIsHA(cfg *kubeadmapi.Etcd) bool {
|
||||
return cfg.External != nil && len(cfg.External.Endpoints) > 1
|
||||
}
|
310
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/etcd/etcd_test.go
generated
vendored
310
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/etcd/etcd_test.go
generated
vendored
@ -1,310 +0,0 @@
|
||||
/*
|
||||
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 etcd
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||
testutil "k8s.io/kubernetes/cmd/kubeadm/test"
|
||||
)
|
||||
|
||||
const (
|
||||
secureEtcdPod = `# generated by kubeadm v1.10.0
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
annotations:
|
||||
scheduler.alpha.kubernetes.io/critical-pod: ""
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
component: etcd
|
||||
tier: control-plane
|
||||
name: etcd
|
||||
namespace: kube-system
|
||||
spec:
|
||||
containers:
|
||||
- command:
|
||||
- etcd
|
||||
- --advertise-client-urls=https://127.0.0.1:2379
|
||||
- --data-dir=/var/lib/etcd
|
||||
- --peer-key-file=/etc/kubernetes/pki/etcd/peer.key
|
||||
- --peer-trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt
|
||||
- --listen-client-urls=https://127.0.0.1:2379
|
||||
- --peer-client-cert-auth=true
|
||||
- --cert-file=/etc/kubernetes/pki/etcd/server.crt
|
||||
- --key-file=/etc/kubernetes/pki/etcd/server.key
|
||||
- --trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt
|
||||
- --peer-cert-file=/etc/kubernetes/pki/etcd/peer.crt
|
||||
- --client-cert-auth=true
|
||||
image: k8s.gcr.io/etcd-amd64:3.1.12
|
||||
livenessProbe:
|
||||
exec:
|
||||
command:
|
||||
- /bin/sh
|
||||
- -ec
|
||||
- ETCDCTL_API=3 etcdctl --endpoints=127.0.0.1:2379 --cacert=/etc/kubernetes/pki/etcd/ca.crt
|
||||
--cert=/etc/kubernetes/pki/etcd/healthcheck-client.crt --key=/etc/kubernetes/pki/etcd/healthcheck-client.key
|
||||
get foo
|
||||
failureThreshold: 8
|
||||
initialDelaySeconds: 15
|
||||
timeoutSeconds: 15
|
||||
name: etcd
|
||||
resources: {}
|
||||
volumeMounts:
|
||||
- mountPath: /var/lib/etcd
|
||||
name: etcd-data
|
||||
- mountPath: /etc/kubernetes/pki/etcd
|
||||
name: etcd-certs
|
||||
hostNetwork: true
|
||||
volumes:
|
||||
- hostPath:
|
||||
path: /var/lib/etcd
|
||||
type: DirectoryOrCreate
|
||||
name: etcd-data
|
||||
- hostPath:
|
||||
path: /etc/kubernetes/pki/etcd
|
||||
type: DirectoryOrCreate
|
||||
name: etcd-certs
|
||||
status: {}
|
||||
`
|
||||
secureExposedEtcdPod = `
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
annotations:
|
||||
scheduler.alpha.kubernetes.io/critical-pod: ""
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
component: etcd
|
||||
tier: control-plane
|
||||
name: etcd
|
||||
namespace: kube-system
|
||||
spec:
|
||||
containers:
|
||||
- command:
|
||||
- etcd
|
||||
- --advertise-client-urls=https://10.0.5.5:2379
|
||||
- --data-dir=/var/lib/etcd
|
||||
- --peer-key-file=/etc/kubernetes/pki/etcd/peer.key
|
||||
- --peer-trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt
|
||||
- --listen-client-urls=https://[::0:0]:2379
|
||||
- --peer-client-cert-auth=true
|
||||
- --cert-file=/etc/kubernetes/pki/etcd/server.crt
|
||||
- --key-file=/etc/kubernetes/pki/etcd/server.key
|
||||
- --trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt
|
||||
- --peer-cert-file=/etc/kubernetes/pki/etcd/peer.crt
|
||||
- --client-cert-auth=true
|
||||
image: k8s.gcr.io/etcd-amd64:3.1.12
|
||||
livenessProbe:
|
||||
exec:
|
||||
command:
|
||||
- /bin/sh
|
||||
- -ec
|
||||
- ETCDCTL_API=3 etcdctl --endpoints=https://[::1]:2379 --cacert=/etc/kubernetes/pki/etcd/ca.crt
|
||||
--cert=/etc/kubernetes/pki/etcd/healthcheck-client.crt --key=/etc/kubernetes/pki/etcd/healthcheck-client.key
|
||||
get foo
|
||||
failureThreshold: 8
|
||||
initialDelaySeconds: 15
|
||||
timeoutSeconds: 15
|
||||
name: etcd
|
||||
resources: {}
|
||||
volumeMounts:
|
||||
- mountPath: /var/lib/etcd
|
||||
name: etcd-data
|
||||
- mountPath: /etc/kubernetes/pki/etcd
|
||||
name: etcd-certs
|
||||
hostNetwork: true
|
||||
volumes:
|
||||
- hostPath:
|
||||
path: /var/lib/etcd
|
||||
type: DirectoryOrCreate
|
||||
name: etcd-data
|
||||
- hostPath:
|
||||
path: /etc/kubernetes/pki/etcd
|
||||
type: DirectoryOrCreate
|
||||
name: etcd-certs
|
||||
status: {}
|
||||
`
|
||||
insecureEtcdPod = `# generated by kubeadm v1.9.6
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
annotations:
|
||||
scheduler.alpha.kubernetes.io/critical-pod: ""
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
component: etcd
|
||||
tier: control-plane
|
||||
name: etcd
|
||||
namespace: kube-system
|
||||
spec:
|
||||
containers:
|
||||
- command:
|
||||
- etcd
|
||||
- --listen-client-urls=http://127.0.0.1:2379
|
||||
- --advertise-client-urls=http://127.0.0.1:2379
|
||||
- --data-dir=/var/lib/etcd
|
||||
image: gcr.io/google_containers/etcd-amd64:3.1.11
|
||||
livenessProbe:
|
||||
failureThreshold: 8
|
||||
httpGet:
|
||||
host: 127.0.0.1
|
||||
path: /health
|
||||
port: 2379
|
||||
scheme: HTTP
|
||||
initialDelaySeconds: 15
|
||||
timeoutSeconds: 15
|
||||
name: etcd
|
||||
resources: {}
|
||||
volumeMounts:
|
||||
- mountPath: /var/lib/etcd
|
||||
name: etcd
|
||||
hostNetwork: true
|
||||
volumes:
|
||||
- hostPath:
|
||||
path: /var/lib/etcd
|
||||
type: DirectoryOrCreate
|
||||
name: etcd
|
||||
status: {}
|
||||
`
|
||||
invalidPod = `---{ broken yaml @@@`
|
||||
)
|
||||
|
||||
func TestPodManifestHasTLS(t *testing.T) {
|
||||
tests := []struct {
|
||||
description string
|
||||
podYaml string
|
||||
hasTLS bool
|
||||
expectErr bool
|
||||
writeManifest bool
|
||||
}{
|
||||
{
|
||||
description: "secure etcd returns true",
|
||||
podYaml: secureEtcdPod,
|
||||
hasTLS: true,
|
||||
writeManifest: true,
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
description: "secure exposed etcd returns true",
|
||||
podYaml: secureExposedEtcdPod,
|
||||
hasTLS: true,
|
||||
writeManifest: true,
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
description: "insecure etcd returns false",
|
||||
podYaml: insecureEtcdPod,
|
||||
hasTLS: false,
|
||||
writeManifest: true,
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
description: "invalid pod fails to unmarshal",
|
||||
podYaml: invalidPod,
|
||||
hasTLS: false,
|
||||
writeManifest: true,
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
description: "non-existent file returns error",
|
||||
podYaml: ``,
|
||||
hasTLS: false,
|
||||
writeManifest: false,
|
||||
expectErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, rt := range tests {
|
||||
tmpdir := testutil.SetupTempDir(t)
|
||||
defer os.RemoveAll(tmpdir)
|
||||
|
||||
manifestPath := filepath.Join(tmpdir, "etcd.yaml")
|
||||
if rt.writeManifest {
|
||||
err := ioutil.WriteFile(manifestPath, []byte(rt.podYaml), 0644)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to write pod manifest\n%s\n\tfatal error: %v", rt.description, err)
|
||||
}
|
||||
}
|
||||
|
||||
hasTLS, actualErr := PodManifestsHaveTLS(tmpdir)
|
||||
if (actualErr != nil) != rt.expectErr {
|
||||
t.Errorf(
|
||||
"PodManifestHasTLS failed\n%s\n\texpected error: %t\n\tgot: %t\n\tactual error: %v",
|
||||
rt.description,
|
||||
rt.expectErr,
|
||||
(actualErr != nil),
|
||||
actualErr,
|
||||
)
|
||||
}
|
||||
|
||||
if hasTLS != rt.hasTLS {
|
||||
t.Errorf("PodManifestHasTLS failed\n%s\n\texpected hasTLS: %t\n\tgot: %t", rt.description, rt.hasTLS, hasTLS)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckConfigurationIsHA(t *testing.T) {
|
||||
var tests = []struct {
|
||||
name string
|
||||
cfg *kubeadmapi.Etcd
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
name: "HA etcd",
|
||||
cfg: &kubeadmapi.Etcd{
|
||||
External: &kubeadmapi.ExternalEtcd{
|
||||
Endpoints: []string{"10.100.0.1:2379", "10.100.0.2:2379", "10.100.0.3:2379"},
|
||||
},
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "single External etcd",
|
||||
cfg: &kubeadmapi.Etcd{
|
||||
External: &kubeadmapi.ExternalEtcd{
|
||||
Endpoints: []string{"10.100.0.1:2379"},
|
||||
},
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "local etcd",
|
||||
cfg: &kubeadmapi.Etcd{
|
||||
Local: &kubeadmapi.LocalEtcd{},
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "empty etcd struct",
|
||||
cfg: &kubeadmapi.Etcd{},
|
||||
expected: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
if isHA := CheckConfigurationIsHA(test.cfg); isHA != test.expected {
|
||||
t.Errorf("expected isHA to be %v, got %v", test.expected, isHA)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
37
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig/BUILD
generated
vendored
37
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig/BUILD
generated
vendored
@ -1,37 +0,0 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["kubeconfig_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["kubeconfig.go"],
|
||||
importpath = "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig",
|
||||
deps = [
|
||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/clientcmd:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/clientcmd/api:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
112
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig/kubeconfig.go
generated
vendored
112
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig/kubeconfig.go
generated
vendored
@ -1,112 +0,0 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package kubeconfig
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
)
|
||||
|
||||
// CreateBasic creates a basic, general KubeConfig object that then can be extended
|
||||
func CreateBasic(serverURL string, clusterName string, userName string, caCert []byte) *clientcmdapi.Config {
|
||||
// Use the cluster and the username as the context name
|
||||
contextName := fmt.Sprintf("%s@%s", userName, clusterName)
|
||||
|
||||
return &clientcmdapi.Config{
|
||||
Clusters: map[string]*clientcmdapi.Cluster{
|
||||
clusterName: {
|
||||
Server: serverURL,
|
||||
CertificateAuthorityData: caCert,
|
||||
},
|
||||
},
|
||||
Contexts: map[string]*clientcmdapi.Context{
|
||||
contextName: {
|
||||
Cluster: clusterName,
|
||||
AuthInfo: userName,
|
||||
},
|
||||
},
|
||||
AuthInfos: map[string]*clientcmdapi.AuthInfo{},
|
||||
CurrentContext: contextName,
|
||||
}
|
||||
}
|
||||
|
||||
// CreateWithCerts creates a KubeConfig object with access to the API server with client certificates
|
||||
func CreateWithCerts(serverURL, clusterName, userName string, caCert []byte, clientKey []byte, clientCert []byte) *clientcmdapi.Config {
|
||||
config := CreateBasic(serverURL, clusterName, userName, caCert)
|
||||
config.AuthInfos[userName] = &clientcmdapi.AuthInfo{
|
||||
ClientKeyData: clientKey,
|
||||
ClientCertificateData: clientCert,
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
// CreateWithToken creates a KubeConfig object with access to the API server with a token
|
||||
func CreateWithToken(serverURL, clusterName, userName string, caCert []byte, token string) *clientcmdapi.Config {
|
||||
config := CreateBasic(serverURL, clusterName, userName, caCert)
|
||||
config.AuthInfos[userName] = &clientcmdapi.AuthInfo{
|
||||
Token: token,
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
// ClientSetFromFile returns a ready-to-use client from a KubeConfig file
|
||||
func ClientSetFromFile(path string) (*clientset.Clientset, error) {
|
||||
config, err := clientcmd.LoadFromFile(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to load admin kubeconfig [%v]", err)
|
||||
}
|
||||
return ToClientSet(config)
|
||||
}
|
||||
|
||||
// ToClientSet converts a KubeConfig object to a client
|
||||
func ToClientSet(config *clientcmdapi.Config) (*clientset.Clientset, error) {
|
||||
clientConfig, err := clientcmd.NewDefaultClientConfig(*config, &clientcmd.ConfigOverrides{}).ClientConfig()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create API client configuration from kubeconfig: %v", err)
|
||||
}
|
||||
|
||||
client, err := clientset.NewForConfig(clientConfig)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create API client: %v", err)
|
||||
}
|
||||
return client, nil
|
||||
}
|
||||
|
||||
// WriteToDisk writes a KubeConfig object down to disk with mode 0600
|
||||
func WriteToDisk(filename string, kubeconfig *clientcmdapi.Config) error {
|
||||
err := clientcmd.WriteToFile(*kubeconfig, filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetClusterFromKubeConfig returns the default Cluster of the specified KubeConfig
|
||||
func GetClusterFromKubeConfig(config *clientcmdapi.Config) *clientcmdapi.Cluster {
|
||||
// If there is an unnamed cluster object, use it
|
||||
if config.Clusters[""] != nil {
|
||||
return config.Clusters[""]
|
||||
}
|
||||
if config.Contexts[config.CurrentContext] != nil {
|
||||
return config.Clusters[config.Contexts[config.CurrentContext].Cluster]
|
||||
}
|
||||
return nil
|
||||
}
|
180
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig/kubeconfig_test.go
generated
vendored
180
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig/kubeconfig_test.go
generated
vendored
@ -1,180 +0,0 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package kubeconfig
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const (
|
||||
configOut1 = `apiVersion: v1
|
||||
clusters:
|
||||
- cluster:
|
||||
server: ""
|
||||
name: k8s
|
||||
contexts:
|
||||
- context:
|
||||
cluster: k8s
|
||||
user: user1
|
||||
name: user1@k8s
|
||||
current-context: user1@k8s
|
||||
kind: Config
|
||||
preferences: {}
|
||||
users:
|
||||
- name: user1
|
||||
user:
|
||||
token: abc
|
||||
`
|
||||
configOut2 = `apiVersion: v1
|
||||
clusters:
|
||||
- cluster:
|
||||
server: localhost:8080
|
||||
name: kubernetes
|
||||
contexts:
|
||||
- context:
|
||||
cluster: kubernetes
|
||||
user: user2
|
||||
name: user2@kubernetes
|
||||
current-context: user2@kubernetes
|
||||
kind: Config
|
||||
preferences: {}
|
||||
users:
|
||||
- name: user2
|
||||
user:
|
||||
token: cba
|
||||
`
|
||||
)
|
||||
|
||||
type configClient struct {
|
||||
clusterName string
|
||||
userName string
|
||||
serverURL string
|
||||
caCert []byte
|
||||
}
|
||||
|
||||
type configClientWithCerts struct {
|
||||
clientKey []byte
|
||||
clientCert []byte
|
||||
}
|
||||
|
||||
type configClientWithToken struct {
|
||||
token string
|
||||
}
|
||||
|
||||
func TestCreateWithCerts(t *testing.T) {
|
||||
var createBasicTest = []struct {
|
||||
cc configClient
|
||||
ccWithCerts configClientWithCerts
|
||||
expected string
|
||||
}{
|
||||
{configClient{}, configClientWithCerts{}, ""},
|
||||
{configClient{clusterName: "kubernetes"}, configClientWithCerts{}, ""},
|
||||
}
|
||||
for _, rt := range createBasicTest {
|
||||
cwc := CreateWithCerts(
|
||||
rt.cc.serverURL,
|
||||
rt.cc.clusterName,
|
||||
rt.cc.userName,
|
||||
rt.cc.caCert,
|
||||
rt.ccWithCerts.clientKey,
|
||||
rt.ccWithCerts.clientCert,
|
||||
)
|
||||
if cwc.Kind != rt.expected {
|
||||
t.Errorf(
|
||||
"failed CreateWithCerts:\n\texpected: %s\n\t actual: %s",
|
||||
rt.expected,
|
||||
cwc.Kind,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateWithToken(t *testing.T) {
|
||||
var createBasicTest = []struct {
|
||||
cc configClient
|
||||
ccWithToken configClientWithToken
|
||||
expected string
|
||||
}{
|
||||
{configClient{}, configClientWithToken{}, ""},
|
||||
{configClient{clusterName: "kubernetes"}, configClientWithToken{}, ""},
|
||||
}
|
||||
for _, rt := range createBasicTest {
|
||||
cwc := CreateWithToken(
|
||||
rt.cc.serverURL,
|
||||
rt.cc.clusterName,
|
||||
rt.cc.userName,
|
||||
rt.cc.caCert,
|
||||
rt.ccWithToken.token,
|
||||
)
|
||||
if cwc.Kind != rt.expected {
|
||||
t.Errorf(
|
||||
"failed CreateWithToken:\n\texpected: %s\n\t actual: %s",
|
||||
rt.expected,
|
||||
cwc.Kind,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteKubeconfigToDisk(t *testing.T) {
|
||||
tmpdir, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't create tmpdir")
|
||||
}
|
||||
defer os.RemoveAll(tmpdir)
|
||||
|
||||
var writeConfig = []struct {
|
||||
name string
|
||||
cc configClient
|
||||
ccWithToken configClientWithToken
|
||||
expected error
|
||||
file []byte
|
||||
}{
|
||||
{"test1", configClient{clusterName: "k8s", userName: "user1"}, configClientWithToken{token: "abc"}, nil, []byte(configOut1)},
|
||||
{"test2", configClient{clusterName: "kubernetes", userName: "user2", serverURL: "localhost:8080"}, configClientWithToken{token: "cba"}, nil, []byte(configOut2)},
|
||||
}
|
||||
for _, rt := range writeConfig {
|
||||
c := CreateWithToken(
|
||||
rt.cc.serverURL,
|
||||
rt.cc.clusterName,
|
||||
rt.cc.userName,
|
||||
rt.cc.caCert,
|
||||
rt.ccWithToken.token,
|
||||
)
|
||||
configPath := fmt.Sprintf("%s/etc/kubernetes/%s.conf", tmpdir, rt.name)
|
||||
err := WriteToDisk(configPath, c)
|
||||
if err != rt.expected {
|
||||
t.Errorf(
|
||||
"failed WriteToDisk with an error:\n\texpected: %s\n\t actual: %s",
|
||||
rt.expected,
|
||||
err,
|
||||
)
|
||||
}
|
||||
newFile, _ := ioutil.ReadFile(configPath)
|
||||
if !bytes.Equal(newFile, rt.file) {
|
||||
t.Errorf(
|
||||
"failed WriteToDisk config write:\n\texpected: %s\n\t actual: %s",
|
||||
rt.file,
|
||||
newFile,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
134
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/marshal.go
generated
vendored
134
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/marshal.go
generated
vendored
@ -1,134 +0,0 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
clientsetscheme "k8s.io/client-go/kubernetes/scheme"
|
||||
kubeadmapiv1alpha1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
|
||||
)
|
||||
|
||||
// MarshalToYaml marshals an object into yaml.
|
||||
func MarshalToYaml(obj runtime.Object, gv schema.GroupVersion) ([]byte, error) {
|
||||
return MarshalToYamlForCodecs(obj, gv, clientsetscheme.Codecs)
|
||||
}
|
||||
|
||||
// MarshalToYamlForCodecs marshals an object into yaml using the specified codec
|
||||
// TODO: Is specifying the gv really needed here?
|
||||
// TODO: Can we support json out of the box easily here?
|
||||
func MarshalToYamlForCodecs(obj runtime.Object, gv schema.GroupVersion, codecs serializer.CodecFactory) ([]byte, error) {
|
||||
mediaType := "application/yaml"
|
||||
info, ok := runtime.SerializerInfoForMediaType(codecs.SupportedMediaTypes(), mediaType)
|
||||
if !ok {
|
||||
return []byte{}, fmt.Errorf("unsupported media type %q", mediaType)
|
||||
}
|
||||
|
||||
encoder := codecs.EncoderForVersion(info.Serializer, gv)
|
||||
return runtime.Encode(encoder, obj)
|
||||
}
|
||||
|
||||
// UnmarshalFromYaml unmarshals yaml into an object.
|
||||
func UnmarshalFromYaml(buffer []byte, gv schema.GroupVersion) (runtime.Object, error) {
|
||||
return UnmarshalFromYamlForCodecs(buffer, gv, clientsetscheme.Codecs)
|
||||
}
|
||||
|
||||
// UnmarshalFromYamlForCodecs unmarshals yaml into an object using the specified codec
|
||||
// TODO: Is specifying the gv really needed here?
|
||||
// TODO: Can we support json out of the box easily here?
|
||||
func UnmarshalFromYamlForCodecs(buffer []byte, gv schema.GroupVersion, codecs serializer.CodecFactory) (runtime.Object, error) {
|
||||
mediaType := "application/yaml"
|
||||
info, ok := runtime.SerializerInfoForMediaType(codecs.SupportedMediaTypes(), mediaType)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unsupported media type %q", mediaType)
|
||||
}
|
||||
|
||||
decoder := codecs.DecoderToVersion(info.Serializer, gv)
|
||||
return runtime.Decode(decoder, buffer)
|
||||
}
|
||||
|
||||
// GroupVersionKindFromBytes parses the bytes and returns the gvk
|
||||
func GroupVersionKindFromBytes(buffer []byte, codecs serializer.CodecFactory) (schema.GroupVersionKind, error) {
|
||||
|
||||
decoded, err := LoadYAML(buffer)
|
||||
if err != nil {
|
||||
return schema.EmptyObjectKind.GroupVersionKind(), fmt.Errorf("unable to decode config from bytes: %v", err)
|
||||
}
|
||||
kindStr, apiVersionStr := "", ""
|
||||
|
||||
// As there was a bug in kubeadm v1.10 and earlier that made the YAML uploaded to the cluster configmap NOT have metav1.TypeMeta information
|
||||
// we need to populate this here manually. If kind or apiVersion is empty, we know the apiVersion is v1alpha1, as by the time kubeadm had this bug,
|
||||
// it could only write
|
||||
// TODO: Remove this "hack" in v1.12 when we know the ConfigMap always contains v1alpha2 content written by kubeadm v1.11. Also, we will drop support for
|
||||
// v1alpha1 in v1.12
|
||||
kind := decoded["kind"]
|
||||
apiVersion := decoded["apiVersion"]
|
||||
if kind == nil || len(kind.(string)) == 0 {
|
||||
kindStr = "MasterConfiguration"
|
||||
} else {
|
||||
kindStr = kind.(string)
|
||||
}
|
||||
if apiVersion == nil || len(apiVersion.(string)) == 0 {
|
||||
apiVersionStr = kubeadmapiv1alpha1.SchemeGroupVersion.String()
|
||||
} else {
|
||||
apiVersionStr = apiVersion.(string)
|
||||
}
|
||||
gv, err := schema.ParseGroupVersion(apiVersionStr)
|
||||
if err != nil {
|
||||
return schema.EmptyObjectKind.GroupVersionKind(), fmt.Errorf("unable to parse apiVersion: %v", err)
|
||||
}
|
||||
|
||||
return gv.WithKind(kindStr), nil
|
||||
}
|
||||
|
||||
// LoadYAML is a small wrapper around go-yaml that ensures all nested structs are map[string]interface{} instead of map[interface{}]interface{}.
|
||||
func LoadYAML(bytes []byte) (map[string]interface{}, error) {
|
||||
var decoded map[interface{}]interface{}
|
||||
if err := yaml.Unmarshal(bytes, &decoded); err != nil {
|
||||
return map[string]interface{}{}, fmt.Errorf("couldn't unmarshal YAML: %v", err)
|
||||
}
|
||||
|
||||
converted, ok := convert(decoded).(map[string]interface{})
|
||||
if !ok {
|
||||
return map[string]interface{}{}, errors.New("yaml is not a map")
|
||||
}
|
||||
|
||||
return converted, nil
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/questions/40737122/convert-yaml-to-json-without-struct-golang
|
||||
func convert(i interface{}) interface{} {
|
||||
switch x := i.(type) {
|
||||
case map[interface{}]interface{}:
|
||||
m2 := map[string]interface{}{}
|
||||
for k, v := range x {
|
||||
m2[k.(string)] = convert(v)
|
||||
}
|
||||
return m2
|
||||
case []interface{}:
|
||||
for i, v := range x {
|
||||
x[i] = convert(v)
|
||||
}
|
||||
}
|
||||
return i
|
||||
}
|
125
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/marshal_test.go
generated
vendored
125
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/marshal_test.go
generated
vendored
@ -1,125 +0,0 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/scheme"
|
||||
kubeadmapiext "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
|
||||
)
|
||||
|
||||
func TestMarshalUnmarshalYaml(t *testing.T) {
|
||||
pod := &corev1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "someName",
|
||||
Namespace: "testNamespace",
|
||||
Labels: map[string]string{
|
||||
"test": "yes",
|
||||
},
|
||||
},
|
||||
Spec: corev1.PodSpec{
|
||||
RestartPolicy: corev1.RestartPolicyAlways,
|
||||
},
|
||||
}
|
||||
|
||||
bytes, err := MarshalToYaml(pod, corev1.SchemeGroupVersion)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error marshalling: %v", err)
|
||||
}
|
||||
|
||||
t.Logf("\n%s", bytes)
|
||||
|
||||
obj2, err := UnmarshalFromYaml(bytes, corev1.SchemeGroupVersion)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error marshalling: %v", err)
|
||||
}
|
||||
|
||||
pod2, ok := obj2.(*corev1.Pod)
|
||||
if !ok {
|
||||
t.Fatal("did not get a Pod")
|
||||
}
|
||||
|
||||
if pod2.Name != pod.Name {
|
||||
t.Errorf("expected %q, got %q", pod.Name, pod2.Name)
|
||||
}
|
||||
|
||||
if pod2.Namespace != pod.Namespace {
|
||||
t.Errorf("expected %q, got %q", pod.Namespace, pod2.Namespace)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(pod2.Labels, pod.Labels) {
|
||||
t.Errorf("expected %v, got %v", pod.Labels, pod2.Labels)
|
||||
}
|
||||
|
||||
if pod2.Spec.RestartPolicy != pod.Spec.RestartPolicy {
|
||||
t.Errorf("expected %q, got %q", pod.Spec.RestartPolicy, pod2.Spec.RestartPolicy)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalUnmarshalToYamlForCodecs(t *testing.T) {
|
||||
cfg := &kubeadmapiext.MasterConfiguration{
|
||||
API: kubeadmapiext.API{
|
||||
AdvertiseAddress: "10.100.0.1",
|
||||
BindPort: 4332,
|
||||
},
|
||||
NodeName: "testNode",
|
||||
NoTaintMaster: true,
|
||||
Networking: kubeadmapiext.Networking{
|
||||
ServiceSubnet: "10.100.0.0/24",
|
||||
PodSubnet: "10.100.1.0/24",
|
||||
},
|
||||
}
|
||||
|
||||
bytes, err := MarshalToYamlForCodecs(cfg, kubeadmapiext.SchemeGroupVersion, scheme.Codecs)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error marshalling MasterConfiguration: %v", err)
|
||||
}
|
||||
t.Logf("\n%s", bytes)
|
||||
|
||||
obj, err := UnmarshalFromYamlForCodecs(bytes, kubeadmapiext.SchemeGroupVersion, scheme.Codecs)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error unmarshalling MasterConfiguration: %v", err)
|
||||
}
|
||||
|
||||
cfg2, ok := obj.(*kubeadmapiext.MasterConfiguration)
|
||||
if !ok {
|
||||
t.Fatal("did not get MasterConfiguration back")
|
||||
}
|
||||
|
||||
if cfg2.API.AdvertiseAddress != cfg.API.AdvertiseAddress {
|
||||
t.Errorf("expected %q, got %q", cfg.API.AdvertiseAddress, cfg2.API.AdvertiseAddress)
|
||||
}
|
||||
if cfg2.API.BindPort != cfg.API.BindPort {
|
||||
t.Errorf("expected %d, got %d", cfg.API.BindPort, cfg2.API.BindPort)
|
||||
}
|
||||
if cfg2.NodeName != cfg.NodeName {
|
||||
t.Errorf("expected %q, got %q", cfg.NodeName, cfg2.NodeName)
|
||||
}
|
||||
if cfg2.NoTaintMaster != cfg.NoTaintMaster {
|
||||
t.Errorf("expected %v, got %v", cfg.NoTaintMaster, cfg2.NoTaintMaster)
|
||||
}
|
||||
if cfg2.Networking.ServiceSubnet != cfg.Networking.ServiceSubnet {
|
||||
t.Errorf("expected %v, got %v", cfg.Networking.ServiceSubnet, cfg2.Networking.ServiceSubnet)
|
||||
}
|
||||
if cfg2.Networking.PodSubnet != cfg.Networking.PodSubnet {
|
||||
t.Errorf("expected %v, got %v", cfg.Networking.PodSubnet, cfg2.Networking.PodSubnet)
|
||||
}
|
||||
}
|
32
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/pubkeypin/BUILD
generated
vendored
32
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/pubkeypin/BUILD
generated
vendored
@ -1,32 +0,0 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["pubkeypin_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["pubkeypin.go"],
|
||||
importpath = "k8s.io/kubernetes/cmd/kubeadm/app/util/pubkeypin",
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
108
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/pubkeypin/pubkeypin.go
generated
vendored
108
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/pubkeypin/pubkeypin.go
generated
vendored
@ -1,108 +0,0 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package pubkeypin provides primitives for x509 public key pinning in the
|
||||
// style of RFC7469.
|
||||
package pubkeypin
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
// formatSHA256 is the prefix for pins that are full-length SHA-256 hashes encoded in base 16 (hex)
|
||||
formatSHA256 = "sha256"
|
||||
)
|
||||
|
||||
// Set is a set of pinned x509 public keys.
|
||||
type Set struct {
|
||||
sha256Hashes map[string]bool
|
||||
}
|
||||
|
||||
// NewSet returns a new, empty PubKeyPinSet
|
||||
func NewSet() *Set {
|
||||
return &Set{make(map[string]bool)}
|
||||
}
|
||||
|
||||
// Allow adds an allowed public key hash to the Set
|
||||
func (s *Set) Allow(pubKeyHashes ...string) error {
|
||||
for _, pubKeyHash := range pubKeyHashes {
|
||||
parts := strings.Split(pubKeyHash, ":")
|
||||
if len(parts) != 2 {
|
||||
return fmt.Errorf("invalid public key hash, expected \"format:value\"")
|
||||
}
|
||||
format, value := parts[0], parts[1]
|
||||
|
||||
switch strings.ToLower(format) {
|
||||
case "sha256":
|
||||
return s.allowSHA256(value)
|
||||
default:
|
||||
return fmt.Errorf("unknown hash format %q", format)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check if a certificate matches one of the public keys in the set
|
||||
func (s *Set) Check(certificate *x509.Certificate) error {
|
||||
if s.checkSHA256(certificate) {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("public key %s not pinned", Hash(certificate))
|
||||
}
|
||||
|
||||
// Empty returns true if the Set contains no pinned public keys.
|
||||
func (s *Set) Empty() bool {
|
||||
return len(s.sha256Hashes) == 0
|
||||
}
|
||||
|
||||
// Hash calculates the SHA-256 hash of the Subject Public Key Information (SPKI)
|
||||
// object in an x509 certificate (in DER encoding). It returns the full hash as a
|
||||
// hex encoded string (suitable for passing to Set.Allow).
|
||||
func Hash(certificate *x509.Certificate) string {
|
||||
spkiHash := sha256.Sum256(certificate.RawSubjectPublicKeyInfo)
|
||||
return formatSHA256 + ":" + strings.ToLower(hex.EncodeToString(spkiHash[:]))
|
||||
}
|
||||
|
||||
// allowSHA256 validates a "sha256" format hash and adds a canonical version of it into the Set
|
||||
func (s *Set) allowSHA256(hash string) error {
|
||||
// validate that the hash is the right length to be a full SHA-256 hash
|
||||
hashLength := hex.DecodedLen(len(hash))
|
||||
if hashLength != sha256.Size {
|
||||
return fmt.Errorf("expected a %d byte SHA-256 hash, found %d bytes", sha256.Size, hashLength)
|
||||
}
|
||||
|
||||
// validate that the hash is valid hex
|
||||
_, err := hex.DecodeString(hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// in the end, just store the original hex string in memory (in lowercase)
|
||||
s.sha256Hashes[strings.ToLower(hash)] = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// checkSHA256 returns true if the certificate's "sha256" hash is pinned in the Set
|
||||
func (s *Set) checkSHA256(certificate *x509.Certificate) bool {
|
||||
actualHash := sha256.Sum256(certificate.RawSubjectPublicKeyInfo)
|
||||
actualHashHex := strings.ToLower(hex.EncodeToString(actualHash[:]))
|
||||
return s.sha256Hashes[actualHashHex]
|
||||
}
|
158
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/pubkeypin/pubkeypin_test.go
generated
vendored
158
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/pubkeypin/pubkeypin_test.go
generated
vendored
@ -1,158 +0,0 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package pubkeypin
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// testCertPEM is a simple self-signed test certificate issued with the openssl CLI:
|
||||
// openssl req -new -newkey rsa:2048 -days 36500 -nodes -x509 -keyout /dev/null -out test.crt
|
||||
const testCertPEM = `
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDRDCCAiygAwIBAgIJAJgVaCXvC6HkMA0GCSqGSIb3DQEBBQUAMB8xHTAbBgNV
|
||||
BAMTFGt1YmVhZG0ta2V5cGlucy10ZXN0MCAXDTE3MDcwNTE3NDMxMFoYDzIxMTcw
|
||||
NjExMTc0MzEwWjAfMR0wGwYDVQQDExRrdWJlYWRtLWtleXBpbnMtdGVzdDCCASIw
|
||||
DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK0ba8mHU9UtYlzM1Own2Fk/XGjR
|
||||
J4uJQvSeGLtz1hID1IA0dLwruvgLCPadXEOw/f/IWIWcmT+ZmvIHZKa/woq2iHi5
|
||||
+HLhXs7aG4tjKGLYhag1hLjBI7icqV7ovkjdGAt9pWkxEzhIYClFMXDjKpMSynu+
|
||||
YX6nZ9tic1cOkHmx2yiZdMkuriRQnpTOa7bb03OC1VfGl7gHlOAIYaj4539WCOr8
|
||||
+ACTUMJUFEHcRZ2o8a/v6F9GMK+7SC8SJUI+GuroXqlMAdhEv4lX5Co52enYaClN
|
||||
+D9FJLRpBv2YfiCQdJRaiTvCBSxEFz6BN+PtP5l2Hs703ZWEkOqCByM6HV8CAwEA
|
||||
AaOBgDB+MB0GA1UdDgQWBBRQgUX8MhK2rWBWQiPHWcKzoWDH5DBPBgNVHSMESDBG
|
||||
gBRQgUX8MhK2rWBWQiPHWcKzoWDH5KEjpCEwHzEdMBsGA1UEAxMUa3ViZWFkbS1r
|
||||
ZXlwaW5zLXRlc3SCCQCYFWgl7wuh5DAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEB
|
||||
BQUAA4IBAQCaAUif7Pfx3X0F08cxhx8/Hdx4jcJw6MCq6iq6rsXM32ge43t8OHKC
|
||||
pJW08dk58a3O1YQSMMvD6GJDAiAfXzfwcwY6j258b1ZlI9Ag0VokvhMl/XfdCsdh
|
||||
AWImnL1t4hvU5jLaImUUMlYxMcSfHBGAm7WJIZ2LdEfg6YWfZh+WGbg1W7uxLxk6
|
||||
y4h5rWdNnzBHWAGf7zJ0oEDV6W6RSwNXtC0JNnLaeIUm/6xdSddJlQPwUv8YH4jX
|
||||
c1vuFqTnJBPcb7W//R/GI2Paicm1cmns9NLnPR35exHxFTy+D1yxmGokpoPMdife
|
||||
aH+sfuxT8xeTPb3kjzF9eJTlnEquUDLM
|
||||
-----END CERTIFICATE-----`
|
||||
|
||||
// expectedHash can be verified using the openssl CLI:
|
||||
// openssl x509 -pubkey -in test.crt openssl rsa -pubin -outform der 2>&/dev/null | openssl dgst -sha256 -hex
|
||||
const expectedHash = `sha256:345959acb2c3b2feb87d281961c893f62a314207ef02599f1cc4a5fb255480b3`
|
||||
|
||||
// testCert2PEM is a second test cert generated the same way as testCertPEM
|
||||
const testCert2PEM = `
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIID9jCCAt6gAwIBAgIJAN5MXZDic7qYMA0GCSqGSIb3DQEBBQUAMFkxCzAJBgNV
|
||||
BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
|
||||
aWRnaXRzIFB0eSBMdGQxEjAQBgNVBAMTCXRlc3RDZXJ0MjAgFw0xNzA3MjQxNjA0
|
||||
MDFaGA8yMTE3MDYzMDE2MDQwMVowWTELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNv
|
||||
bWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAG
|
||||
A1UEAxMJdGVzdENlcnQyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
|
||||
0brwpJYN2ytPWzRBtZSVc3dhkQlA59AzxzqeLLkano0Pxo9NIc3T/y58nnRI8uaS
|
||||
I1P7BzUfJTiUEvmAtX8NggqKK4ld/gPrU+IRww1CUYS4KCkA/0d0ctPy0JwBCjD+
|
||||
b57G3rmNE8c+0jns6J96ZzNtqmv6N+ZlFBAXm1p4S+k0kGi5+hoQ6H7SYXjk2lG+
|
||||
r/8jPQEjy/NSdw1dcCA0Nc6o+hPr32927dS6J9KOhBeXNYUNdbuDDmroM9/gN2e/
|
||||
YMSA1olLeDPQ7Xvhk0PIyEDnHh83AffPCx5yM3htVRGddjIsPAVUJEL3z5leJtxe
|
||||
fzyPghOhHJY0PXqznDQTcwIDAQABo4G+MIG7MB0GA1UdDgQWBBRP0IJqv/5rQ4Uf
|
||||
SByl77dJeEapRDCBiwYDVR0jBIGDMIGAgBRP0IJqv/5rQ4UfSByl77dJeEapRKFd
|
||||
pFswWTELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoT
|
||||
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAGA1UEAxMJdGVzdENlcnQyggkA
|
||||
3kxdkOJzupgwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEA0RIMHc10
|
||||
wHHPMh9UflqBgDMF7gfbOL0juJfGloAOcohWWfMZBBJ0CQKMy3xRyoK3HmbW1eeb
|
||||
iATjesw7t4VEAwf7mgKAd+eTfWYB952uq5qYJ2TI28mSofEq1Wz3RmrNkC1KCBs1
|
||||
u+YMFGwyl6necV9zKCeiju4jeovI1GA38TvH7MgYln6vMJ+FbgOXj7XCpek7dQiY
|
||||
KGaeSSH218mGNQaWRQw2Sm3W6cFdANoCJUph4w18s7gjtFpfV63s80hXRps+vEyv
|
||||
jEQMEQpG8Ss7HGJLGLBw/xAmG0e//XS/o2dDonbGbvzToFByz8OGxjMhk6yV6hdd
|
||||
+iyvsLAw/MYMSA==
|
||||
-----END CERTIFICATE-----
|
||||
`
|
||||
|
||||
// testCert is a small helper to get a test x509.Certificate from the PEM constants
|
||||
func testCert(t *testing.T, pemString string) *x509.Certificate {
|
||||
// Decode the example certificate from a PEM file into a PEM block
|
||||
pemBlock, _ := pem.Decode([]byte(pemString))
|
||||
if pemBlock == nil {
|
||||
t.Fatal("failed to parse test certificate PEM")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Parse the PEM block into an x509.Certificate
|
||||
result, err := x509.ParseCertificate(pemBlock.Bytes)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to parse test certificate: %v", err)
|
||||
return nil
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func TestSet(t *testing.T) {
|
||||
s := NewSet()
|
||||
if !s.Empty() {
|
||||
t.Error("expected a new set to be empty")
|
||||
return
|
||||
}
|
||||
err := s.Allow("xyz")
|
||||
if err == nil || !s.Empty() {
|
||||
t.Error("expected allowing junk to fail")
|
||||
return
|
||||
}
|
||||
|
||||
err = s.Allow("0011223344")
|
||||
if err == nil || !s.Empty() {
|
||||
t.Error("expected allowing something too short to fail")
|
||||
return
|
||||
}
|
||||
|
||||
err = s.Allow(expectedHash + expectedHash)
|
||||
if err == nil || !s.Empty() {
|
||||
t.Error("expected allowing something too long to fail")
|
||||
return
|
||||
}
|
||||
|
||||
err = s.Check(testCert(t, testCertPEM))
|
||||
if err == nil {
|
||||
t.Error("expected test cert to not be allowed (yet)")
|
||||
return
|
||||
}
|
||||
|
||||
err = s.Allow(strings.ToUpper(expectedHash))
|
||||
if err != nil || s.Empty() {
|
||||
t.Error("expected allowing uppercase expectedHash to succeed")
|
||||
return
|
||||
}
|
||||
|
||||
err = s.Check(testCert(t, testCertPEM))
|
||||
if err != nil {
|
||||
t.Errorf("expected test cert to be allowed, but got back: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
err = s.Check(testCert(t, testCert2PEM))
|
||||
if err == nil {
|
||||
t.Error("expected the second test cert to be disallowed")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestHash(t *testing.T) {
|
||||
actualHash := Hash(testCert(t, testCertPEM))
|
||||
if actualHash != expectedHash {
|
||||
t.Errorf(
|
||||
"failed to Hash() to the expected value\n\texpected: %q\n\t actual: %q",
|
||||
expectedHash,
|
||||
actualHash,
|
||||
)
|
||||
}
|
||||
}
|
52
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/staticpod/BUILD
generated
vendored
52
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/staticpod/BUILD
generated
vendored
@ -1,52 +0,0 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["utils_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
|
||||
"//cmd/kubeadm/app/constants:go_default_library",
|
||||
"//cmd/kubeadm/app/features:go_default_library",
|
||||
"//cmd/kubeadm/test: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/util/intstr:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["utils.go"],
|
||||
importpath = "k8s.io/kubernetes/cmd/kubeadm/app/util/staticpod",
|
||||
deps = [
|
||||
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
|
||||
"//cmd/kubeadm/app/constants:go_default_library",
|
||||
"//cmd/kubeadm/app/features:go_default_library",
|
||||
"//cmd/kubeadm/app/util:go_default_library",
|
||||
"//pkg/kubelet/types:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/intstr:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
289
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/staticpod/utils.go
generated
vendored
289
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/staticpod/utils.go
generated
vendored
@ -1,289 +0,0 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package staticpod
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/features"
|
||||
|
||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/util"
|
||||
kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
|
||||
)
|
||||
|
||||
const (
|
||||
// kubeControllerManagerAddressArg represents the address argument of the kube-controller-manager configuration.
|
||||
kubeControllerManagerAddressArg = "address"
|
||||
|
||||
// kubeSchedulerAddressArg represents the address argument of the kube-scheduler configuration.
|
||||
kubeSchedulerAddressArg = "address"
|
||||
|
||||
// etcdListenClientURLsArg represents the listen-client-urls argument of the etcd configuration.
|
||||
etcdListenClientURLsArg = "listen-client-urls"
|
||||
)
|
||||
|
||||
// ComponentPod returns a Pod object from the container and volume specifications
|
||||
func ComponentPod(container v1.Container, volumes map[string]v1.Volume) v1.Pod {
|
||||
return v1.Pod{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "v1",
|
||||
Kind: "Pod",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: container.Name,
|
||||
Namespace: metav1.NamespaceSystem,
|
||||
Annotations: map[string]string{kubetypes.CriticalPodAnnotationKey: ""},
|
||||
// The component and tier labels are useful for quickly identifying the control plane Pods when doing a .List()
|
||||
// against Pods in the kube-system namespace. Can for example be used together with the WaitForPodsWithLabel function
|
||||
Labels: map[string]string{"component": container.Name, "tier": "control-plane"},
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{container},
|
||||
PriorityClassName: "system-cluster-critical",
|
||||
HostNetwork: true,
|
||||
Volumes: VolumeMapToSlice(volumes),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// ComponentResources returns the v1.ResourceRequirements object needed for allocating a specified amount of the CPU
|
||||
func ComponentResources(cpu string) v1.ResourceRequirements {
|
||||
return v1.ResourceRequirements{
|
||||
Requests: v1.ResourceList{
|
||||
v1.ResourceName(v1.ResourceCPU): resource.MustParse(cpu),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// ComponentProbe is a helper function building a ready v1.Probe object from some simple parameters
|
||||
func ComponentProbe(cfg *kubeadmapi.MasterConfiguration, componentName string, port int, path string, scheme v1.URIScheme) *v1.Probe {
|
||||
return &v1.Probe{
|
||||
Handler: v1.Handler{
|
||||
HTTPGet: &v1.HTTPGetAction{
|
||||
Host: GetProbeAddress(cfg, componentName),
|
||||
Path: path,
|
||||
Port: intstr.FromInt(port),
|
||||
Scheme: scheme,
|
||||
},
|
||||
},
|
||||
InitialDelaySeconds: 15,
|
||||
TimeoutSeconds: 15,
|
||||
FailureThreshold: 8,
|
||||
}
|
||||
}
|
||||
|
||||
// EtcdProbe is a helper function for building a shell-based, etcdctl v1.Probe object to healthcheck etcd
|
||||
func EtcdProbe(cfg *kubeadmapi.MasterConfiguration, componentName string, port int, certsDir string, CACertName string, CertName string, KeyName string) *v1.Probe {
|
||||
tlsFlags := fmt.Sprintf("--cacert=%[1]s/%[2]s --cert=%[1]s/%[3]s --key=%[1]s/%[4]s", certsDir, CACertName, CertName, KeyName)
|
||||
// etcd pod is alive if a linearizable get succeeds.
|
||||
cmd := fmt.Sprintf("ETCDCTL_API=3 etcdctl --endpoints=https://[%s]:%d %s get foo", GetProbeAddress(cfg, componentName), port, tlsFlags)
|
||||
|
||||
return &v1.Probe{
|
||||
Handler: v1.Handler{
|
||||
Exec: &v1.ExecAction{
|
||||
Command: []string{"/bin/sh", "-ec", cmd},
|
||||
},
|
||||
},
|
||||
InitialDelaySeconds: 15,
|
||||
TimeoutSeconds: 15,
|
||||
FailureThreshold: 8,
|
||||
}
|
||||
}
|
||||
|
||||
// NewVolume creates a v1.Volume with a hostPath mount to the specified location
|
||||
func NewVolume(name, path string, pathType *v1.HostPathType) v1.Volume {
|
||||
return v1.Volume{
|
||||
Name: name,
|
||||
VolumeSource: v1.VolumeSource{
|
||||
HostPath: &v1.HostPathVolumeSource{
|
||||
Path: path,
|
||||
Type: pathType,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// NewVolumeMount creates a v1.VolumeMount to the specified location
|
||||
func NewVolumeMount(name, path string, readOnly bool) v1.VolumeMount {
|
||||
return v1.VolumeMount{
|
||||
Name: name,
|
||||
MountPath: path,
|
||||
ReadOnly: readOnly,
|
||||
}
|
||||
}
|
||||
|
||||
// VolumeMapToSlice returns a slice of volumes from a map's values
|
||||
func VolumeMapToSlice(volumes map[string]v1.Volume) []v1.Volume {
|
||||
v := make([]v1.Volume, 0, len(volumes))
|
||||
|
||||
for _, vol := range volumes {
|
||||
v = append(v, vol)
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
// VolumeMountMapToSlice returns a slice of volumes from a map's values
|
||||
func VolumeMountMapToSlice(volumeMounts map[string]v1.VolumeMount) []v1.VolumeMount {
|
||||
v := make([]v1.VolumeMount, 0, len(volumeMounts))
|
||||
|
||||
for _, volMount := range volumeMounts {
|
||||
v = append(v, volMount)
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
// GetExtraParameters builds a list of flag arguments two string-string maps, one with default, base commands and one with overrides
|
||||
func GetExtraParameters(overrides map[string]string, defaults map[string]string) []string {
|
||||
var command []string
|
||||
for k, v := range overrides {
|
||||
if len(v) > 0 {
|
||||
command = append(command, fmt.Sprintf("--%s=%s", k, v))
|
||||
}
|
||||
}
|
||||
for k, v := range defaults {
|
||||
if _, overrideExists := overrides[k]; !overrideExists {
|
||||
command = append(command, fmt.Sprintf("--%s=%s", k, v))
|
||||
}
|
||||
}
|
||||
return command
|
||||
}
|
||||
|
||||
// WriteStaticPodToDisk writes a static pod file to disk
|
||||
func WriteStaticPodToDisk(componentName, manifestDir string, pod v1.Pod) error {
|
||||
|
||||
// creates target folder if not already exists
|
||||
if err := os.MkdirAll(manifestDir, 0700); err != nil {
|
||||
return fmt.Errorf("failed to create directory %q: %v", manifestDir, err)
|
||||
}
|
||||
|
||||
// writes the pod to disk
|
||||
serialized, err := util.MarshalToYaml(&pod, v1.SchemeGroupVersion)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal manifest for %q to YAML: %v", componentName, err)
|
||||
}
|
||||
|
||||
filename := kubeadmconstants.GetStaticPodFilepath(componentName, manifestDir)
|
||||
|
||||
if err := ioutil.WriteFile(filename, serialized, 0600); err != nil {
|
||||
return fmt.Errorf("failed to write static pod manifest file for %q (%q): %v", componentName, filename, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadStaticPodFromDisk reads a static pod file from disk
|
||||
func ReadStaticPodFromDisk(manifestPath string) (*v1.Pod, error) {
|
||||
buf, err := ioutil.ReadFile(manifestPath)
|
||||
if err != nil {
|
||||
return &v1.Pod{}, fmt.Errorf("failed to read manifest for %q: %v", manifestPath, err)
|
||||
}
|
||||
|
||||
obj, err := util.UnmarshalFromYaml(buf, v1.SchemeGroupVersion)
|
||||
if err != nil {
|
||||
return &v1.Pod{}, fmt.Errorf("failed to unmarshal manifest for %q from YAML: %v", manifestPath, err)
|
||||
}
|
||||
|
||||
pod := obj.(*v1.Pod)
|
||||
|
||||
return pod, nil
|
||||
}
|
||||
|
||||
// GetProbeAddress returns an IP address or 127.0.0.1 to use for liveness probes
|
||||
// in static pod manifests.
|
||||
func GetProbeAddress(cfg *kubeadmapi.MasterConfiguration, componentName string) string {
|
||||
switch {
|
||||
case componentName == kubeadmconstants.KubeAPIServer:
|
||||
// In the case of a self-hosted deployment, the initial host on which kubeadm --init is run,
|
||||
// will generate a DaemonSet with a nodeSelector such that all nodes with the label
|
||||
// node-role.kubernetes.io/master='' will have the API server deployed to it. Since the init
|
||||
// is run only once on an initial host, the API advertise address will be invalid for any
|
||||
// future hosts that do not have the same address. Furthermore, since liveness and readiness
|
||||
// probes do not support the Downward API we cannot dynamically set the advertise address to
|
||||
// the node's IP. The only option then is to use localhost.
|
||||
if features.Enabled(cfg.FeatureGates, features.SelfHosting) {
|
||||
return "127.0.0.1"
|
||||
} else if cfg.API.AdvertiseAddress != "" {
|
||||
return cfg.API.AdvertiseAddress
|
||||
}
|
||||
case componentName == kubeadmconstants.KubeControllerManager:
|
||||
if addr, exists := cfg.ControllerManagerExtraArgs[kubeControllerManagerAddressArg]; exists {
|
||||
return addr
|
||||
}
|
||||
case componentName == kubeadmconstants.KubeScheduler:
|
||||
if addr, exists := cfg.SchedulerExtraArgs[kubeSchedulerAddressArg]; exists {
|
||||
return addr
|
||||
}
|
||||
case componentName == kubeadmconstants.Etcd:
|
||||
if cfg.Etcd.Local != nil && cfg.Etcd.Local.ExtraArgs != nil {
|
||||
if arg, exists := cfg.Etcd.Local.ExtraArgs[etcdListenClientURLsArg]; exists {
|
||||
// Use the first url in the listen-client-urls if multiple url's are specified.
|
||||
if strings.ContainsAny(arg, ",") {
|
||||
arg = strings.Split(arg, ",")[0]
|
||||
}
|
||||
parsedURL, err := url.Parse(arg)
|
||||
if err != nil || parsedURL.Hostname() == "" {
|
||||
break
|
||||
}
|
||||
// Return the IP if the URL contains an address instead of a name.
|
||||
if ip := net.ParseIP(parsedURL.Hostname()); ip != nil {
|
||||
// etcdctl doesn't support auto-converting zero addresses into loopback addresses
|
||||
if ip.Equal(net.IPv4zero) {
|
||||
return "127.0.0.1"
|
||||
}
|
||||
if ip.Equal(net.IPv6zero) {
|
||||
return net.IPv6loopback.String()
|
||||
}
|
||||
return ip.String()
|
||||
}
|
||||
// Use the local resolver to try resolving the name within the URL.
|
||||
// If the name can not be resolved, return an IPv4 loopback address.
|
||||
// Otherwise, select the first valid IPv4 address.
|
||||
// If the name does not resolve to an IPv4 address, select the first valid IPv6 address.
|
||||
addrs, err := net.LookupIP(parsedURL.Hostname())
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
var ip net.IP
|
||||
for _, addr := range addrs {
|
||||
if addr.To4() != nil {
|
||||
ip = addr
|
||||
break
|
||||
}
|
||||
if addr.To16() != nil && ip == nil {
|
||||
ip = addr
|
||||
}
|
||||
}
|
||||
return ip.String()
|
||||
}
|
||||
}
|
||||
}
|
||||
return "127.0.0.1"
|
||||
}
|
609
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/staticpod/utils_test.go
generated
vendored
609
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/staticpod/utils_test.go
generated
vendored
@ -1,609 +0,0 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package staticpod
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/features"
|
||||
|
||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||
testutil "k8s.io/kubernetes/cmd/kubeadm/test"
|
||||
)
|
||||
|
||||
func TestComponentResources(t *testing.T) {
|
||||
a := ComponentResources("250m")
|
||||
if a.Requests == nil {
|
||||
t.Errorf(
|
||||
"failed componentResources, return value was nil",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestComponentProbe(t *testing.T) {
|
||||
var tests = []struct {
|
||||
name string
|
||||
cfg *kubeadmapi.MasterConfiguration
|
||||
component string
|
||||
port int
|
||||
path string
|
||||
scheme v1.URIScheme
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "default apiserver advertise address with http",
|
||||
cfg: &kubeadmapi.MasterConfiguration{
|
||||
API: kubeadmapi.API{
|
||||
AdvertiseAddress: "",
|
||||
},
|
||||
},
|
||||
component: kubeadmconstants.KubeAPIServer,
|
||||
port: 1,
|
||||
path: "foo",
|
||||
scheme: v1.URISchemeHTTP,
|
||||
expected: "127.0.0.1",
|
||||
},
|
||||
{
|
||||
name: "default apiserver advertise address with http",
|
||||
cfg: &kubeadmapi.MasterConfiguration{
|
||||
API: kubeadmapi.API{
|
||||
AdvertiseAddress: "1.2.3.4",
|
||||
},
|
||||
FeatureGates: map[string]bool{
|
||||
features.SelfHosting: true,
|
||||
},
|
||||
},
|
||||
component: kubeadmconstants.KubeAPIServer,
|
||||
port: 1,
|
||||
path: "foo",
|
||||
scheme: v1.URISchemeHTTP,
|
||||
expected: "127.0.0.1",
|
||||
},
|
||||
{
|
||||
name: "default apiserver advertise address with https",
|
||||
cfg: &kubeadmapi.MasterConfiguration{
|
||||
API: kubeadmapi.API{
|
||||
AdvertiseAddress: "",
|
||||
},
|
||||
},
|
||||
component: kubeadmconstants.KubeAPIServer,
|
||||
port: 2,
|
||||
path: "bar",
|
||||
scheme: v1.URISchemeHTTPS,
|
||||
expected: "127.0.0.1",
|
||||
},
|
||||
{
|
||||
name: "valid ipv4 apiserver advertise address with http",
|
||||
cfg: &kubeadmapi.MasterConfiguration{
|
||||
API: kubeadmapi.API{
|
||||
AdvertiseAddress: "1.2.3.4",
|
||||
},
|
||||
},
|
||||
component: kubeadmconstants.KubeAPIServer,
|
||||
port: 1,
|
||||
path: "foo",
|
||||
scheme: v1.URISchemeHTTP,
|
||||
expected: "1.2.3.4",
|
||||
},
|
||||
{
|
||||
name: "valid ipv6 apiserver advertise address with http",
|
||||
cfg: &kubeadmapi.MasterConfiguration{
|
||||
API: kubeadmapi.API{
|
||||
AdvertiseAddress: "2001:db8::1",
|
||||
},
|
||||
},
|
||||
component: kubeadmconstants.KubeAPIServer,
|
||||
port: 1,
|
||||
path: "foo",
|
||||
scheme: v1.URISchemeHTTP,
|
||||
expected: "2001:db8::1",
|
||||
},
|
||||
{
|
||||
name: "valid IPv4 controller-manager probe",
|
||||
cfg: &kubeadmapi.MasterConfiguration{
|
||||
ControllerManagerExtraArgs: map[string]string{"address": "1.2.3.4"},
|
||||
},
|
||||
component: kubeadmconstants.KubeControllerManager,
|
||||
port: 1,
|
||||
path: "foo",
|
||||
scheme: v1.URISchemeHTTP,
|
||||
expected: "1.2.3.4",
|
||||
},
|
||||
{
|
||||
name: "valid IPv6 controller-manager probe",
|
||||
cfg: &kubeadmapi.MasterConfiguration{
|
||||
ControllerManagerExtraArgs: map[string]string{"address": "2001:db8::1"},
|
||||
},
|
||||
component: kubeadmconstants.KubeControllerManager,
|
||||
port: 1,
|
||||
path: "foo",
|
||||
scheme: v1.URISchemeHTTP,
|
||||
expected: "2001:db8::1",
|
||||
},
|
||||
{
|
||||
name: "valid IPv4 scheduler probe",
|
||||
cfg: &kubeadmapi.MasterConfiguration{
|
||||
SchedulerExtraArgs: map[string]string{"address": "1.2.3.4"},
|
||||
},
|
||||
component: kubeadmconstants.KubeScheduler,
|
||||
port: 1,
|
||||
path: "foo",
|
||||
scheme: v1.URISchemeHTTP,
|
||||
expected: "1.2.3.4",
|
||||
},
|
||||
{
|
||||
name: "valid IPv6 scheduler probe",
|
||||
cfg: &kubeadmapi.MasterConfiguration{
|
||||
SchedulerExtraArgs: map[string]string{"address": "2001:db8::1"},
|
||||
},
|
||||
component: kubeadmconstants.KubeScheduler,
|
||||
port: 1,
|
||||
path: "foo",
|
||||
scheme: v1.URISchemeHTTP,
|
||||
expected: "2001:db8::1",
|
||||
},
|
||||
}
|
||||
for _, rt := range tests {
|
||||
actual := ComponentProbe(rt.cfg, rt.component, rt.port, rt.path, rt.scheme)
|
||||
if actual.Handler.HTTPGet.Host != rt.expected {
|
||||
t.Errorf("%s test case failed:\n\texpected: %s\n\t actual: %s",
|
||||
rt.name, rt.expected,
|
||||
actual.Handler.HTTPGet.Host)
|
||||
}
|
||||
if actual.Handler.HTTPGet.Port != intstr.FromInt(rt.port) {
|
||||
t.Errorf("%s test case failed:\n\texpected: %v\n\t actual: %v",
|
||||
rt.name, rt.port,
|
||||
actual.Handler.HTTPGet.Port)
|
||||
}
|
||||
if actual.Handler.HTTPGet.Path != rt.path {
|
||||
t.Errorf("%s test case failed:\n\texpected: %s\n\t actual: %s",
|
||||
rt.name, rt.path,
|
||||
actual.Handler.HTTPGet.Path)
|
||||
}
|
||||
if actual.Handler.HTTPGet.Scheme != rt.scheme {
|
||||
t.Errorf("%s test case failed:\n\texpected: %v\n\t actual: %v",
|
||||
rt.name, rt.scheme,
|
||||
actual.Handler.HTTPGet.Scheme)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEtcdProbe(t *testing.T) {
|
||||
var tests = []struct {
|
||||
name string
|
||||
cfg *kubeadmapi.MasterConfiguration
|
||||
component string
|
||||
port int
|
||||
certsDir string
|
||||
cacert string
|
||||
cert string
|
||||
key string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "valid etcd probe using listen-client-urls IPv4 addresses",
|
||||
cfg: &kubeadmapi.MasterConfiguration{
|
||||
Etcd: kubeadmapi.Etcd{
|
||||
Local: &kubeadmapi.LocalEtcd{
|
||||
ExtraArgs: map[string]string{
|
||||
"listen-client-urls": "http://1.2.3.4:2379,http://4.3.2.1:2379"},
|
||||
},
|
||||
},
|
||||
},
|
||||
component: kubeadmconstants.Etcd,
|
||||
port: 1,
|
||||
certsDir: "secretsA",
|
||||
cacert: "ca1",
|
||||
cert: "cert1",
|
||||
key: "key1",
|
||||
expected: "ETCDCTL_API=3 etcdctl --endpoints=https://[1.2.3.4]:1 --cacert=secretsA/ca1 --cert=secretsA/cert1 --key=secretsA/key1 get foo",
|
||||
},
|
||||
{
|
||||
name: "valid etcd probe using listen-client-urls unspecified IPv6 address",
|
||||
cfg: &kubeadmapi.MasterConfiguration{
|
||||
Etcd: kubeadmapi.Etcd{
|
||||
Local: &kubeadmapi.LocalEtcd{
|
||||
ExtraArgs: map[string]string{
|
||||
"listen-client-urls": "http://[0:0:0:0:0:0:0:0]:2379"},
|
||||
},
|
||||
},
|
||||
},
|
||||
component: kubeadmconstants.Etcd,
|
||||
port: 1,
|
||||
certsDir: "secretsB",
|
||||
cacert: "ca2",
|
||||
cert: "cert2",
|
||||
key: "key2",
|
||||
expected: "ETCDCTL_API=3 etcdctl --endpoints=https://[::1]:1 --cacert=secretsB/ca2 --cert=secretsB/cert2 --key=secretsB/key2 get foo",
|
||||
},
|
||||
{
|
||||
name: "valid etcd probe using listen-client-urls unspecified IPv6 address 2",
|
||||
cfg: &kubeadmapi.MasterConfiguration{
|
||||
Etcd: kubeadmapi.Etcd{
|
||||
Local: &kubeadmapi.LocalEtcd{
|
||||
ExtraArgs: map[string]string{
|
||||
"listen-client-urls": "http://[::0:0]:2379"},
|
||||
},
|
||||
},
|
||||
},
|
||||
component: kubeadmconstants.Etcd,
|
||||
port: 1,
|
||||
certsDir: "secretsB",
|
||||
cacert: "ca2",
|
||||
cert: "cert2",
|
||||
key: "key2",
|
||||
expected: "ETCDCTL_API=3 etcdctl --endpoints=https://[::1]:1 --cacert=secretsB/ca2 --cert=secretsB/cert2 --key=secretsB/key2 get foo",
|
||||
},
|
||||
{
|
||||
name: "valid etcd probe using listen-client-urls unspecified IPv6 address 3",
|
||||
cfg: &kubeadmapi.MasterConfiguration{
|
||||
Etcd: kubeadmapi.Etcd{
|
||||
Local: &kubeadmapi.LocalEtcd{
|
||||
ExtraArgs: map[string]string{
|
||||
"listen-client-urls": "http://[::]:2379"},
|
||||
},
|
||||
},
|
||||
},
|
||||
component: kubeadmconstants.Etcd,
|
||||
port: 1,
|
||||
certsDir: "secretsB",
|
||||
cacert: "ca2",
|
||||
cert: "cert2",
|
||||
key: "key2",
|
||||
expected: "ETCDCTL_API=3 etcdctl --endpoints=https://[::1]:1 --cacert=secretsB/ca2 --cert=secretsB/cert2 --key=secretsB/key2 get foo",
|
||||
},
|
||||
{
|
||||
name: "valid etcd probe using listen-client-urls unspecified IPv4 address",
|
||||
cfg: &kubeadmapi.MasterConfiguration{
|
||||
Etcd: kubeadmapi.Etcd{
|
||||
Local: &kubeadmapi.LocalEtcd{
|
||||
ExtraArgs: map[string]string{
|
||||
"listen-client-urls": "http://1.2.3.4:2379,http://4.3.2.1:2379"},
|
||||
},
|
||||
},
|
||||
},
|
||||
component: kubeadmconstants.Etcd,
|
||||
port: 1,
|
||||
certsDir: "secretsA",
|
||||
cacert: "ca1",
|
||||
cert: "cert1",
|
||||
key: "key1",
|
||||
expected: "ETCDCTL_API=3 etcdctl --endpoints=https://[1.2.3.4]:1 --cacert=secretsA/ca1 --cert=secretsA/cert1 --key=secretsA/key1 get foo",
|
||||
},
|
||||
{
|
||||
name: "valid etcd probe using listen-client-urls IPv6 addresses",
|
||||
cfg: &kubeadmapi.MasterConfiguration{
|
||||
Etcd: kubeadmapi.Etcd{
|
||||
Local: &kubeadmapi.LocalEtcd{
|
||||
ExtraArgs: map[string]string{
|
||||
"listen-client-urls": "http://[2001:db8::1]:2379,http://[2001:db8::2]:2379"},
|
||||
},
|
||||
},
|
||||
},
|
||||
component: kubeadmconstants.Etcd,
|
||||
port: 1,
|
||||
certsDir: "secretsB",
|
||||
cacert: "ca2",
|
||||
cert: "cert2",
|
||||
key: "key2",
|
||||
expected: "ETCDCTL_API=3 etcdctl --endpoints=https://[2001:db8::1]:1 --cacert=secretsB/ca2 --cert=secretsB/cert2 --key=secretsB/key2 get foo",
|
||||
},
|
||||
{
|
||||
name: "valid IPv4 etcd probe using hostname for listen-client-urls",
|
||||
cfg: &kubeadmapi.MasterConfiguration{
|
||||
Etcd: kubeadmapi.Etcd{
|
||||
Local: &kubeadmapi.LocalEtcd{
|
||||
ExtraArgs: map[string]string{
|
||||
"listen-client-urls": "http://localhost:2379"},
|
||||
},
|
||||
},
|
||||
},
|
||||
component: kubeadmconstants.Etcd,
|
||||
port: 1,
|
||||
certsDir: "secretsC",
|
||||
cacert: "ca3",
|
||||
cert: "cert3",
|
||||
key: "key3",
|
||||
expected: "ETCDCTL_API=3 etcdctl --endpoints=https://[127.0.0.1]:1 --cacert=secretsC/ca3 --cert=secretsC/cert3 --key=secretsC/key3 get foo",
|
||||
},
|
||||
}
|
||||
for _, rt := range tests {
|
||||
actual := EtcdProbe(rt.cfg, rt.component, rt.port, rt.certsDir, rt.cacert, rt.cert, rt.key)
|
||||
if actual.Handler.Exec.Command[2] != rt.expected {
|
||||
t.Errorf("%s test case failed:\n\texpected: %s\n\t actual: %s",
|
||||
rt.name, rt.expected,
|
||||
actual.Handler.Exec.Command[2])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestComponentPod(t *testing.T) {
|
||||
var tests = []struct {
|
||||
name string
|
||||
expected v1.Pod
|
||||
}{
|
||||
{
|
||||
name: "foo",
|
||||
expected: v1.Pod{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "v1",
|
||||
Kind: "Pod",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
Namespace: "kube-system",
|
||||
Annotations: map[string]string{"scheduler.alpha.kubernetes.io/critical-pod": ""},
|
||||
Labels: map[string]string{"component": "foo", "tier": "control-plane"},
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
Name: "foo",
|
||||
},
|
||||
},
|
||||
PriorityClassName: "system-cluster-critical",
|
||||
HostNetwork: true,
|
||||
Volumes: []v1.Volume{},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, rt := range tests {
|
||||
c := v1.Container{Name: rt.name}
|
||||
actual := ComponentPod(c, map[string]v1.Volume{})
|
||||
if !reflect.DeepEqual(rt.expected, actual) {
|
||||
t.Errorf(
|
||||
"failed componentPod:\n\texpected: %v\n\t actual: %v",
|
||||
rt.expected,
|
||||
actual,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewVolume(t *testing.T) {
|
||||
hostPathDirectoryOrCreate := v1.HostPathDirectoryOrCreate
|
||||
var tests = []struct {
|
||||
name string
|
||||
path string
|
||||
expected v1.Volume
|
||||
pathType *v1.HostPathType
|
||||
}{
|
||||
{
|
||||
name: "foo",
|
||||
path: "/etc/foo",
|
||||
expected: v1.Volume{
|
||||
Name: "foo",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
HostPath: &v1.HostPathVolumeSource{
|
||||
Path: "/etc/foo",
|
||||
Type: &hostPathDirectoryOrCreate,
|
||||
},
|
||||
},
|
||||
},
|
||||
pathType: &hostPathDirectoryOrCreate,
|
||||
},
|
||||
}
|
||||
|
||||
for _, rt := range tests {
|
||||
actual := NewVolume(rt.name, rt.path, rt.pathType)
|
||||
if !reflect.DeepEqual(actual, rt.expected) {
|
||||
t.Errorf(
|
||||
"failed newVolume:\n\texpected: %v\n\t actual: %v",
|
||||
rt.expected,
|
||||
actual,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewVolumeMount(t *testing.T) {
|
||||
var tests = []struct {
|
||||
name string
|
||||
path string
|
||||
ro bool
|
||||
expected v1.VolumeMount
|
||||
}{
|
||||
{
|
||||
name: "foo",
|
||||
path: "/etc/foo",
|
||||
ro: false,
|
||||
expected: v1.VolumeMount{
|
||||
Name: "foo",
|
||||
MountPath: "/etc/foo",
|
||||
ReadOnly: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bar",
|
||||
path: "/etc/foo/bar",
|
||||
ro: true,
|
||||
expected: v1.VolumeMount{
|
||||
Name: "bar",
|
||||
MountPath: "/etc/foo/bar",
|
||||
ReadOnly: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, rt := range tests {
|
||||
actual := NewVolumeMount(rt.name, rt.path, rt.ro)
|
||||
if !reflect.DeepEqual(actual, rt.expected) {
|
||||
t.Errorf(
|
||||
"failed newVolumeMount:\n\texpected: %v\n\t actual: %v",
|
||||
rt.expected,
|
||||
actual,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
func TestVolumeMapToSlice(t *testing.T) {
|
||||
testVolumes := map[string]v1.Volume{
|
||||
"foo": {
|
||||
Name: "foo",
|
||||
},
|
||||
}
|
||||
volumeSlice := VolumeMapToSlice(testVolumes)
|
||||
if len(volumeSlice) != 1 {
|
||||
t.Errorf("Expected slice length of 1, got %d", len(volumeSlice))
|
||||
}
|
||||
if volumeSlice[0].Name != "foo" {
|
||||
t.Errorf("Expected volume name \"foo\", got %s", volumeSlice[0].Name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVolumeMountMapToSlice(t *testing.T) {
|
||||
testVolumeMounts := map[string]v1.VolumeMount{
|
||||
"foo": {
|
||||
Name: "foo",
|
||||
},
|
||||
}
|
||||
volumeMountSlice := VolumeMountMapToSlice(testVolumeMounts)
|
||||
if len(volumeMountSlice) != 1 {
|
||||
t.Errorf("Expected slice length of 1, got %d", len(volumeMountSlice))
|
||||
}
|
||||
if volumeMountSlice[0].Name != "foo" {
|
||||
t.Errorf("Expected volume mount name \"foo\", got %s", volumeMountSlice[0].Name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetExtraParameters(t *testing.T) {
|
||||
var tests = []struct {
|
||||
overrides map[string]string
|
||||
defaults map[string]string
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
overrides: map[string]string{
|
||||
"admission-control": "NamespaceLifecycle,LimitRanger",
|
||||
},
|
||||
defaults: map[string]string{
|
||||
"admission-control": "NamespaceLifecycle",
|
||||
"insecure-bind-address": "127.0.0.1",
|
||||
"allow-privileged": "true",
|
||||
},
|
||||
expected: []string{
|
||||
"--admission-control=NamespaceLifecycle,LimitRanger",
|
||||
"--insecure-bind-address=127.0.0.1",
|
||||
"--allow-privileged=true",
|
||||
},
|
||||
},
|
||||
{
|
||||
overrides: map[string]string{
|
||||
"admission-control": "NamespaceLifecycle,LimitRanger",
|
||||
},
|
||||
defaults: map[string]string{
|
||||
"insecure-bind-address": "127.0.0.1",
|
||||
"allow-privileged": "true",
|
||||
},
|
||||
expected: []string{
|
||||
"--admission-control=NamespaceLifecycle,LimitRanger",
|
||||
"--insecure-bind-address=127.0.0.1",
|
||||
"--allow-privileged=true",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, rt := range tests {
|
||||
actual := GetExtraParameters(rt.overrides, rt.defaults)
|
||||
sort.Strings(actual)
|
||||
sort.Strings(rt.expected)
|
||||
if !reflect.DeepEqual(actual, rt.expected) {
|
||||
t.Errorf("failed getExtraParameters:\nexpected:\n%v\nsaw:\n%v", rt.expected, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
validPod = `
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
labels:
|
||||
component: etcd
|
||||
tier: control-plane
|
||||
name: etcd
|
||||
namespace: kube-system
|
||||
spec:
|
||||
containers:
|
||||
- image: gcr.io/google_containers/etcd-amd64:3.1.11
|
||||
status: {}
|
||||
`
|
||||
invalidPod = `---{ broken yaml @@@`
|
||||
)
|
||||
|
||||
func TestReadStaticPodFromDisk(t *testing.T) {
|
||||
tests := []struct {
|
||||
description string
|
||||
podYaml string
|
||||
expectErr bool
|
||||
writeManifest bool
|
||||
}{
|
||||
{
|
||||
description: "valid pod is marshaled",
|
||||
podYaml: validPod,
|
||||
writeManifest: true,
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
description: "invalid pod fails to unmarshal",
|
||||
podYaml: invalidPod,
|
||||
writeManifest: true,
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
description: "non-existent file returns error",
|
||||
podYaml: ``,
|
||||
writeManifest: false,
|
||||
expectErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, rt := range tests {
|
||||
tmpdir := testutil.SetupTempDir(t)
|
||||
defer os.RemoveAll(tmpdir)
|
||||
|
||||
manifestPath := filepath.Join(tmpdir, "pod.yaml")
|
||||
if rt.writeManifest {
|
||||
err := ioutil.WriteFile(manifestPath, []byte(rt.podYaml), 0644)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to write pod manifest\n%s\n\tfatal error: %v", rt.description, err)
|
||||
}
|
||||
}
|
||||
|
||||
_, actualErr := ReadStaticPodFromDisk(manifestPath)
|
||||
if (actualErr != nil) != rt.expectErr {
|
||||
t.Errorf(
|
||||
"ReadStaticPodFromDisk failed\n%s\n\texpected error: %t\n\tgot: %t\n\tactual error: %v",
|
||||
rt.description,
|
||||
rt.expectErr,
|
||||
(actualErr != nil),
|
||||
actualErr,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
37
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/template.go
generated
vendored
37
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/template.go
generated
vendored
@ -1,37 +0,0 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
// ParseTemplate validates and parses passed as argument template
|
||||
func ParseTemplate(strtmpl string, obj interface{}) ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
tmpl, err := template.New("template").Parse(strtmpl)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error when parsing template: %v", err)
|
||||
}
|
||||
err = tmpl.Execute(&buf, obj)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error when executing template: %v", err)
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
90
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/template_test.go
generated
vendored
90
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/template_test.go
generated
vendored
@ -1,90 +0,0 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
const (
|
||||
validTmpl = "image: {{ .ImageRepository }}/pause-{{ .Arch }}:3.1"
|
||||
validTmplOut = "image: k8s.gcr.io/pause-amd64:3.1"
|
||||
doNothing = "image: k8s.gcr.io/pause-amd64:3.1"
|
||||
invalidTmpl1 = "{{ .baz }/d}"
|
||||
invalidTmpl2 = "{{ !foobar }}"
|
||||
)
|
||||
|
||||
func TestParseTemplate(t *testing.T) {
|
||||
var tmplTests = []struct {
|
||||
template string
|
||||
data interface{}
|
||||
output string
|
||||
errExpected bool
|
||||
}{
|
||||
// should parse a valid template and set the right values
|
||||
{
|
||||
template: validTmpl,
|
||||
data: struct{ ImageRepository, Arch string }{
|
||||
ImageRepository: "k8s.gcr.io",
|
||||
Arch: "amd64",
|
||||
},
|
||||
output: validTmplOut,
|
||||
errExpected: false,
|
||||
},
|
||||
// should noop if there aren't any {{ .foo }} present
|
||||
{
|
||||
template: doNothing,
|
||||
data: struct{ ImageRepository, Arch string }{
|
||||
ImageRepository: "k8s.gcr.io",
|
||||
Arch: "amd64",
|
||||
},
|
||||
output: doNothing,
|
||||
errExpected: false,
|
||||
},
|
||||
// invalid syntax, passing nil
|
||||
{
|
||||
template: invalidTmpl1,
|
||||
data: nil,
|
||||
output: "",
|
||||
errExpected: true,
|
||||
},
|
||||
// invalid syntax
|
||||
{
|
||||
template: invalidTmpl2,
|
||||
data: struct{}{},
|
||||
output: "",
|
||||
errExpected: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tmplTests {
|
||||
outbytes, err := ParseTemplate(tt.template, tt.data)
|
||||
if tt.errExpected != (err != nil) {
|
||||
t.Errorf(
|
||||
"failed TestParseTemplate:\n\texpected err: %t\n\t actual: %s",
|
||||
tt.errExpected,
|
||||
err,
|
||||
)
|
||||
}
|
||||
if tt.output != string(outbytes) {
|
||||
t.Errorf(
|
||||
"failed TestParseTemplate:\n\texpected bytes: %s\n\t actual: %s",
|
||||
tt.output,
|
||||
outbytes,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
150
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/version.go
generated
vendored
150
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/version.go
generated
vendored
@ -1,150 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 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 util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
netutil "k8s.io/apimachinery/pkg/util/net"
|
||||
)
|
||||
|
||||
var (
|
||||
kubeReleaseBucketURL = "https://dl.k8s.io"
|
||||
kubeReleaseRegex = regexp.MustCompile(`^v?(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)([-0-9a-zA-Z_\.+]*)?$`)
|
||||
kubeReleaseLabelRegex = regexp.MustCompile(`^[[:lower:]]+(-[-\w_\.]+)?$`)
|
||||
kubeBucketPrefixes = regexp.MustCompile(`^((release|ci|ci-cross)/)?([-\w_\.+]+)$`)
|
||||
)
|
||||
|
||||
// KubernetesReleaseVersion is helper function that can fetch
|
||||
// available version information from release servers based on
|
||||
// label names, like "stable" or "latest".
|
||||
//
|
||||
// If argument is already semantic version string, it
|
||||
// will return same string.
|
||||
//
|
||||
// In case of labels, it tries to fetch from release
|
||||
// servers and then return actual semantic version.
|
||||
//
|
||||
// Available names on release servers:
|
||||
// stable (latest stable release)
|
||||
// stable-1 (latest stable release in 1.x)
|
||||
// stable-1.0 (and similarly 1.1, 1.2, 1.3, ...)
|
||||
// latest (latest release, including alpha/beta)
|
||||
// latest-1 (latest release in 1.x, including alpha/beta)
|
||||
// latest-1.0 (and similarly 1.1, 1.2, 1.3, ...)
|
||||
func KubernetesReleaseVersion(version string) (string, error) {
|
||||
ver := normalizedBuildVersion(version)
|
||||
if len(ver) != 0 {
|
||||
return ver, nil
|
||||
}
|
||||
|
||||
bucketURL, versionLabel, err := splitVersion(version)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// revalidate, if exact build from e.g. CI bucket requested.
|
||||
ver = normalizedBuildVersion(versionLabel)
|
||||
if len(ver) != 0 {
|
||||
return ver, nil
|
||||
}
|
||||
|
||||
if kubeReleaseLabelRegex.MatchString(versionLabel) {
|
||||
url := fmt.Sprintf("%s/%s.txt", bucketURL, versionLabel)
|
||||
body, err := fetchFromURL(url)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// Re-validate received version and return.
|
||||
return KubernetesReleaseVersion(body)
|
||||
}
|
||||
return "", fmt.Errorf("version %q doesn't match patterns for neither semantic version nor labels (stable, latest, ...)", version)
|
||||
}
|
||||
|
||||
// KubernetesVersionToImageTag is helper function that replaces all
|
||||
// non-allowed symbols in tag strings with underscores.
|
||||
// Image tag can only contain lowercase and uppercase letters, digits,
|
||||
// underscores, periods and dashes.
|
||||
// Current usage is for CI images where all of symbols except '+' are valid,
|
||||
// but function is for generic usage where input can't be always pre-validated.
|
||||
func KubernetesVersionToImageTag(version string) string {
|
||||
allowed := regexp.MustCompile(`[^-a-zA-Z0-9_\.]`)
|
||||
return allowed.ReplaceAllString(version, "_")
|
||||
}
|
||||
|
||||
// KubernetesIsCIVersion checks if user requested CI version
|
||||
func KubernetesIsCIVersion(version string) bool {
|
||||
subs := kubeBucketPrefixes.FindAllStringSubmatch(version, 1)
|
||||
if len(subs) == 1 && len(subs[0]) == 4 && strings.HasPrefix(subs[0][2], "ci") {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Internal helper: returns normalized build version (with "v" prefix if needed)
|
||||
// If input doesn't match known version pattern, returns empty string.
|
||||
func normalizedBuildVersion(version string) string {
|
||||
if kubeReleaseRegex.MatchString(version) {
|
||||
if strings.HasPrefix(version, "v") {
|
||||
return version
|
||||
}
|
||||
return "v" + version
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Internal helper: split version parts,
|
||||
// Return base URL and cleaned-up version
|
||||
func splitVersion(version string) (string, string, error) {
|
||||
var urlSuffix string
|
||||
subs := kubeBucketPrefixes.FindAllStringSubmatch(version, 1)
|
||||
if len(subs) != 1 || len(subs[0]) != 4 {
|
||||
return "", "", fmt.Errorf("invalid version %q", version)
|
||||
}
|
||||
|
||||
switch {
|
||||
case strings.HasPrefix(subs[0][2], "ci"):
|
||||
// Just use whichever the user specified
|
||||
urlSuffix = subs[0][2]
|
||||
default:
|
||||
urlSuffix = "release"
|
||||
}
|
||||
url := fmt.Sprintf("%s/%s", kubeReleaseBucketURL, urlSuffix)
|
||||
return url, subs[0][3], nil
|
||||
}
|
||||
|
||||
// Internal helper: return content of URL
|
||||
func fetchFromURL(url string) (string, error) {
|
||||
client := &http.Client{Transport: netutil.SetOldTransportDefaults(&http.Transport{})}
|
||||
resp, err := client.Get(url)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unable to get URL %q: %s", url, err.Error())
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return "", fmt.Errorf("unable to fetch file. URL: %q Status: %v", url, resp.Status)
|
||||
}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unable to read content of URL %q: %s", url, err.Error())
|
||||
}
|
||||
return strings.TrimSpace(string(body)), nil
|
||||
}
|
292
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/version_test.go
generated
vendored
292
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/version_test.go
generated
vendored
@ -1,292 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 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 util
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"path"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestEmptyVersion(t *testing.T) {
|
||||
|
||||
ver, err := KubernetesReleaseVersion("")
|
||||
if err == nil {
|
||||
t.Error("KubernetesReleaseVersion returned successfully, but error expected")
|
||||
}
|
||||
if ver != "" {
|
||||
t.Error("KubernetesReleaseVersion returned value, expected only error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidVersion(t *testing.T) {
|
||||
validVersions := []string{
|
||||
"v1.3.0",
|
||||
"v1.4.0-alpha.0",
|
||||
"v1.4.5",
|
||||
"v1.4.0-beta.0",
|
||||
"v2.0.0",
|
||||
"v1.6.0-alpha.0.536+d60d9f3269288f",
|
||||
"v1.5.0-alpha.0.1078+1044b6822497da-pull",
|
||||
"v1.5.0-alpha.1.822+49b9e32fad9f32-pull-gke-gci",
|
||||
"v1.6.1+coreos.0",
|
||||
}
|
||||
for _, s := range validVersions {
|
||||
ver, err := KubernetesReleaseVersion(s)
|
||||
t.Log("Valid: ", s, ver, err)
|
||||
if err != nil {
|
||||
t.Errorf("KubernetesReleaseVersion unexpected error for version %q: %v", s, err)
|
||||
}
|
||||
if ver != s {
|
||||
t.Errorf("KubernetesReleaseVersion should return same valid version string. %q != %q", s, ver)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalidVersion(t *testing.T) {
|
||||
invalidVersions := []string{
|
||||
"v1.3",
|
||||
"1.4",
|
||||
"b1.4.0",
|
||||
"c1.4.5+git",
|
||||
"something1.2",
|
||||
}
|
||||
for _, s := range invalidVersions {
|
||||
ver, err := KubernetesReleaseVersion(s)
|
||||
t.Log("Invalid: ", s, ver, err)
|
||||
if err == nil {
|
||||
t.Errorf("KubernetesReleaseVersion error expected for version %q, but returned successfully", s)
|
||||
}
|
||||
if ver != "" {
|
||||
t.Errorf("KubernetesReleaseVersion should return empty string in case of error. Returned %q for version %q", ver, s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidConvenientForUserVersion(t *testing.T) {
|
||||
validVersions := []string{
|
||||
"1.4.0",
|
||||
"1.4.5+git",
|
||||
"1.6.1_coreos.0",
|
||||
}
|
||||
for _, s := range validVersions {
|
||||
ver, err := KubernetesReleaseVersion(s)
|
||||
t.Log("Valid: ", s, ver, err)
|
||||
if err != nil {
|
||||
t.Errorf("KubernetesReleaseVersion unexpected error for version %q: %v", s, err)
|
||||
}
|
||||
if ver != "v"+s {
|
||||
t.Errorf("KubernetesReleaseVersion should return semantic version string. %q vs. %q", s, ver)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestVersionFromNetwork(t *testing.T) {
|
||||
type T struct {
|
||||
Content string
|
||||
Status int
|
||||
Expected string
|
||||
ErrorExpected bool
|
||||
}
|
||||
cases := map[string]T{
|
||||
"stable": {"stable-1", http.StatusOK, "v1.4.6", false}, // recursive pointer to stable-1
|
||||
"stable-1": {"v1.4.6", http.StatusOK, "v1.4.6", false},
|
||||
"stable-1.3": {"v1.3.10", http.StatusOK, "v1.3.10", false},
|
||||
"latest": {"v1.6.0-alpha.0", http.StatusOK, "v1.6.0-alpha.0", false},
|
||||
"latest-1.3": {"v1.3.11-beta.0", http.StatusOK, "v1.3.11-beta.0", false},
|
||||
"empty": {"", http.StatusOK, "", true},
|
||||
"garbage": {"<?xml version='1.0'?><Error><Code>NoSuchKey</Code><Message>The specified key does not exist.</Message></Error>", http.StatusOK, "", true},
|
||||
"unknown": {"The requested URL was not found on this server.", http.StatusNotFound, "", true},
|
||||
}
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
key := strings.TrimSuffix(path.Base(r.URL.Path), ".txt")
|
||||
res, found := cases[key]
|
||||
if found {
|
||||
http.Error(w, res.Content, res.Status)
|
||||
} else {
|
||||
http.Error(w, "Unknown test case key!", http.StatusNotFound)
|
||||
}
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
kubeReleaseBucketURL = server.URL
|
||||
|
||||
for k, v := range cases {
|
||||
ver, err := KubernetesReleaseVersion(k)
|
||||
t.Logf("Key: %q. Result: %q, Error: %v", k, ver, err)
|
||||
switch {
|
||||
case err != nil && !v.ErrorExpected:
|
||||
t.Errorf("KubernetesReleaseVersion: unexpected error for %q. Error: %v", k, err)
|
||||
case err == nil && v.ErrorExpected:
|
||||
t.Errorf("KubernetesReleaseVersion: error expected for key %q, but result is %q", k, ver)
|
||||
case ver != v.Expected:
|
||||
t.Errorf("KubernetesReleaseVersion: unexpected result for key %q. Expected: %q Actual: %q", k, v.Expected, ver)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestVersionToTag(t *testing.T) {
|
||||
type T struct {
|
||||
input string
|
||||
expected string
|
||||
}
|
||||
cases := []T{
|
||||
// NOP
|
||||
{"", ""},
|
||||
// Official releases
|
||||
{"v1.0.0", "v1.0.0"},
|
||||
// CI or custom builds
|
||||
{"v10.1.2-alpha.1.100+0123456789abcdef+SOMETHING", "v10.1.2-alpha.1.100_0123456789abcdef_SOMETHING"},
|
||||
// random and invalid input: should return safe value
|
||||
{"v1,0!0+üñµ", "v1_0_0____"},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
tag := KubernetesVersionToImageTag(tc.input)
|
||||
t.Logf("KubernetesVersionToImageTag: Input: %q. Result: %q. Expected: %q", tc.input, tag, tc.expected)
|
||||
if tag != tc.expected {
|
||||
t.Errorf("failed KubernetesVersionToImageTag: Input: %q. Result: %q. Expected: %q", tc.input, tag, tc.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSplitVersion(t *testing.T) {
|
||||
type T struct {
|
||||
input string
|
||||
bucket string
|
||||
label string
|
||||
valid bool
|
||||
}
|
||||
cases := []T{
|
||||
// Release area
|
||||
{"v1.7.0", "https://dl.k8s.io/release", "v1.7.0", true},
|
||||
{"v1.8.0-alpha.2.1231+afabd012389d53a", "https://dl.k8s.io/release", "v1.8.0-alpha.2.1231+afabd012389d53a", true},
|
||||
{"release/v1.7.0", "https://dl.k8s.io/release", "v1.7.0", true},
|
||||
{"release/latest-1.7", "https://dl.k8s.io/release", "latest-1.7", true},
|
||||
// CI builds area, lookup actual builds at ci-cross/*.txt
|
||||
{"ci/latest", "https://dl.k8s.io/ci", "latest", true},
|
||||
{"ci-cross/latest", "https://dl.k8s.io/ci-cross", "latest", true},
|
||||
{"ci/latest-1.7", "https://dl.k8s.io/ci", "latest-1.7", true},
|
||||
{"ci-cross/latest-1.7", "https://dl.k8s.io/ci-cross", "latest-1.7", true},
|
||||
// unknown label in default (release) area: splitVersion validate only areas.
|
||||
{"unknown-1", "https://dl.k8s.io/release", "unknown-1", true},
|
||||
// unknown area, not valid input.
|
||||
{"unknown/latest-1", "", "", false},
|
||||
}
|
||||
// kubeReleaseBucketURL can be overridden during network tests, thus ensure
|
||||
// it will contain value corresponding to expected outcome for this unit test
|
||||
kubeReleaseBucketURL = "https://dl.k8s.io"
|
||||
|
||||
for _, tc := range cases {
|
||||
bucket, label, err := splitVersion(tc.input)
|
||||
switch {
|
||||
case err != nil && tc.valid:
|
||||
t.Errorf("splitVersion: unexpected error for %q. Error: %v", tc.input, err)
|
||||
case err == nil && !tc.valid:
|
||||
t.Errorf("splitVersion: error expected for key %q, but result is %q, %q", tc.input, bucket, label)
|
||||
case bucket != tc.bucket:
|
||||
t.Errorf("splitVersion: unexpected bucket result for key %q. Expected: %q Actual: %q", tc.input, tc.bucket, bucket)
|
||||
case label != tc.label:
|
||||
t.Errorf("splitVersion: unexpected label result for key %q. Expected: %q Actual: %q", tc.input, tc.label, label)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestKubernetesIsCIVersion(t *testing.T) {
|
||||
type T struct {
|
||||
input string
|
||||
expected bool
|
||||
}
|
||||
cases := []T{
|
||||
{"", false},
|
||||
// Official releases
|
||||
{"v1.0.0", false},
|
||||
{"release/v1.0.0", false},
|
||||
// CI builds
|
||||
{"ci/latest-1", true},
|
||||
{"ci-cross/latest", true},
|
||||
{"ci/v1.9.0-alpha.1.123+acbcbfd53bfa0a", true},
|
||||
{"ci-cross/v1.9.0-alpha.1.123+acbcbfd53bfa0a", true},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
result := KubernetesIsCIVersion(tc.input)
|
||||
t.Logf("KubernetesIsCIVersion: Input: %q. Result: %v. Expected: %v", tc.input, result, tc.expected)
|
||||
if result != tc.expected {
|
||||
t.Errorf("failed KubernetesIsCIVersion: Input: %q. Result: %v. Expected: %v", tc.input, result, tc.expected)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Validate KubernetesReleaseVersion but with bucket prefixes
|
||||
func TestCIBuildVersion(t *testing.T) {
|
||||
type T struct {
|
||||
input string
|
||||
expected string
|
||||
valid bool
|
||||
}
|
||||
cases := []T{
|
||||
// Official releases
|
||||
{"v1.7.0", "v1.7.0", true},
|
||||
{"release/v1.8.0", "v1.8.0", true},
|
||||
{"1.4.0-beta.0", "v1.4.0-beta.0", true},
|
||||
{"release/0invalid", "", false},
|
||||
// CI or custom builds
|
||||
{"ci/v1.9.0-alpha.1.123+acbcbfd53bfa0a", "v1.9.0-alpha.1.123+acbcbfd53bfa0a", true},
|
||||
{"ci-cross/v1.9.0-alpha.1.123+acbcbfd53bfa0a", "v1.9.0-alpha.1.123+acbcbfd53bfa0a", true},
|
||||
{"ci/1.9.0-alpha.1.123+acbcbfd53bfa0a", "v1.9.0-alpha.1.123+acbcbfd53bfa0a", true},
|
||||
{"ci-cross/1.9.0-alpha.1.123+acbcbfd53bfa0a", "v1.9.0-alpha.1.123+acbcbfd53bfa0a", true},
|
||||
{"ci/0invalid", "", false},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
ver, err := KubernetesReleaseVersion(tc.input)
|
||||
t.Logf("Input: %q. Result: %q, Error: %v", tc.input, ver, err)
|
||||
switch {
|
||||
case err != nil && tc.valid:
|
||||
t.Errorf("KubernetesReleaseVersion: unexpected error for input %q. Error: %v", tc.input, err)
|
||||
case err == nil && !tc.valid:
|
||||
t.Errorf("KubernetesReleaseVersion: error expected for input %q, but result is %q", tc.input, ver)
|
||||
case ver != tc.expected:
|
||||
t.Errorf("KubernetesReleaseVersion: unexpected result for input %q. Expected: %q Actual: %q", tc.input, tc.expected, ver)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizedBuildVersionVersion(t *testing.T) {
|
||||
type T struct {
|
||||
input string
|
||||
expected string
|
||||
}
|
||||
cases := []T{
|
||||
{"v1.7.0", "v1.7.0"},
|
||||
{"v1.8.0-alpha.2.1231+afabd012389d53a", "v1.8.0-alpha.2.1231+afabd012389d53a"},
|
||||
{"1.7.0", "v1.7.0"},
|
||||
{"unknown-1", ""},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
output := normalizedBuildVersion(tc.input)
|
||||
if output != tc.expected {
|
||||
t.Errorf("normalizedBuildVersion: unexpected output %q for input %q. Expected: %q", output, tc.input, tc.expected)
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user