mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-14 18:53:35 +00:00
Fresh dep ensure
This commit is contained in:
41
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/BUILD
generated
vendored
41
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/BUILD
generated
vendored
@ -11,6 +11,8 @@ go_library(
|
||||
srcs = [
|
||||
"arguments.go",
|
||||
"cgroupdriver.go",
|
||||
"chroot_unix.go",
|
||||
"chroot_windows.go",
|
||||
"copy.go",
|
||||
"endpoint.go",
|
||||
"error.go",
|
||||
@ -21,16 +23,21 @@ go_library(
|
||||
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",
|
||||
"//cmd/kubeadm/app/constants:go_default_library",
|
||||
"//pkg/version:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/errors:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/net:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/validation:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/version:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/yaml:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/kubernetes/scheme:go_default_library",
|
||||
"//vendor/github.com/pkg/errors:go_default_library",
|
||||
"//vendor/k8s.io/klog:go_default_library",
|
||||
"//vendor/k8s.io/utils/exec:go_default_library",
|
||||
"//vendor/sigs.k8s.io/yaml:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
@ -39,6 +46,7 @@ go_test(
|
||||
srcs = [
|
||||
"arguments_test.go",
|
||||
"cgroupdriver_test.go",
|
||||
"chroot_test.go",
|
||||
"endpoint_test.go",
|
||||
"error_test.go",
|
||||
"marshal_test.go",
|
||||
@ -48,10 +56,14 @@ go_test(
|
||||
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",
|
||||
"//cmd/kubeadm/app/apis/kubeadm/v1beta1:go_default_library",
|
||||
"//cmd/kubeadm/app/constants:go_default_library",
|
||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
|
||||
"//vendor/github.com/pkg/errors:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
@ -72,8 +84,11 @@ filegroup(
|
||||
"//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/pkiutil:all-srcs",
|
||||
"//cmd/kubeadm/app/util/pubkeypin:all-srcs",
|
||||
"//cmd/kubeadm/app/util/runtime:all-srcs",
|
||||
"//cmd/kubeadm/app/util/staticpod:all-srcs",
|
||||
"//cmd/kubeadm/app/util/system:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
|
52
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient/BUILD
generated
vendored
52
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient/BUILD
generated
vendored
@ -22,25 +22,26 @@ go_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",
|
||||
"//staging/src/k8s.io/api/apps/v1:go_default_library",
|
||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||
"//staging/src/k8s.io/api/rbac/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/intstr:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/net:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/strategicpatch:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/dynamic:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/kubernetes:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/kubernetes/scheme:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/rest:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/testing:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/tools/clientcmd:go_default_library",
|
||||
"//vendor/github.com/pkg/errors:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
@ -61,14 +62,17 @@ go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"dryrunclient_test.go",
|
||||
"idempotency_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",
|
||||
"//pkg/kubelet/apis:go_default_library",
|
||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||
"//staging/src/k8s.io/api/rbac/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/testing:go_default_library",
|
||||
],
|
||||
)
|
||||
|
6
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient/clientbacked_dryrun.go
generated
vendored
6
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient/clientbacked_dryrun.go
generated
vendored
@ -29,6 +29,8 @@ import (
|
||||
"k8s.io/client-go/rest"
|
||||
core "k8s.io/client-go/testing"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// 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
|
||||
@ -61,11 +63,11 @@ func NewClientBackedDryRunGetter(config *rest.Config) (*ClientBackedDryRunGetter
|
||||
func NewClientBackedDryRunGetterFromKubeconfig(file string) (*ClientBackedDryRunGetter, error) {
|
||||
config, err := clientcmd.LoadFromFile(file)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to load kubeconfig: %v", err)
|
||||
return nil, errors.Wrap(err, "failed to load kubeconfig")
|
||||
}
|
||||
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 nil, errors.Wrap(err, "failed to create API client configuration from kubeconfig")
|
||||
}
|
||||
return NewClientBackedDryRunGetter(clientConfig)
|
||||
}
|
||||
|
5
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient/dryrunclient.go
generated
vendored
5
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient/dryrunclient.go
generated
vendored
@ -23,6 +23,7 @@ import (
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
@ -114,7 +115,7 @@ func NewDryRunClientWithOpts(opts DryRunClientOptions) clientset.Interface {
|
||||
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")
|
||||
return true, nil, errors.New("can't cast get reactor event action object to GetAction interface")
|
||||
}
|
||||
handled, obj, err := opts.Getter.HandleGetAction(getAction)
|
||||
|
||||
@ -139,7 +140,7 @@ func NewDryRunClientWithOpts(opts DryRunClientOptions) clientset.Interface {
|
||||
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")
|
||||
return true, nil, errors.New("can't cast list reactor event action object to ListAction interface")
|
||||
}
|
||||
handled, objs, err := opts.Getter.HandleListAction(listAction)
|
||||
|
||||
|
2
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient/dryrunclient_test.go
generated
vendored
2
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient/dryrunclient_test.go
generated
vendored
@ -72,7 +72,7 @@ func TestLogDryRunAction(t *testing.T) {
|
||||
`),
|
||||
},
|
||||
{
|
||||
action: core.NewPatchAction(schema.GroupVersionResource{Version: "v1", Resource: "nodes"}, "default", "my-node", []byte(`{"spec":{"taints":[{"key": "foo", "value": "bar"}]}}`)),
|
||||
action: core.NewPatchAction(schema.GroupVersionResource{Version: "v1", Resource: "nodes"}, "default", "my-node", "application/strategic-merge-patch+json", []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:
|
||||
|
68
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient/idempotency.go
generated
vendored
68
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient/idempotency.go
generated
vendored
@ -20,6 +20,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
apps "k8s.io/api/apps/v1"
|
||||
"k8s.io/api/core/v1"
|
||||
rbac "k8s.io/api/rbac/v1"
|
||||
@ -40,11 +41,11 @@ import (
|
||||
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)
|
||||
return errors.Wrap(err, "unable to create configmap")
|
||||
}
|
||||
|
||||
if _, err := client.CoreV1().ConfigMaps(cm.ObjectMeta.Namespace).Update(cm); err != nil {
|
||||
return fmt.Errorf("unable to update configmap: %v", err)
|
||||
return errors.Wrap(err, "unable to update configmap")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@ -58,7 +59,7 @@ func CreateOrRetainConfigMap(client clientset.Interface, cm *v1.ConfigMap, confi
|
||||
}
|
||||
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 errors.Wrap(err, "unable to create configmap")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -69,11 +70,11 @@ func CreateOrRetainConfigMap(client clientset.Interface, cm *v1.ConfigMap, confi
|
||||
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)
|
||||
return errors.Wrap(err, "unable to create secret")
|
||||
}
|
||||
|
||||
if _, err := client.CoreV1().Secrets(secret.ObjectMeta.Namespace).Update(secret); err != nil {
|
||||
return fmt.Errorf("unable to update secret: %v", err)
|
||||
return errors.Wrap(err, "unable to update secret")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@ -85,7 +86,7 @@ func CreateOrUpdateServiceAccount(client clientset.Interface, sa *v1.ServiceAcco
|
||||
// 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 errors.Wrap(err, "unable to create serviceaccount")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@ -95,11 +96,11 @@ func CreateOrUpdateServiceAccount(client clientset.Interface, sa *v1.ServiceAcco
|
||||
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)
|
||||
return errors.Wrap(err, "unable to create deployment")
|
||||
}
|
||||
|
||||
if _, err := client.AppsV1().Deployments(deploy.ObjectMeta.Namespace).Update(deploy); err != nil {
|
||||
return fmt.Errorf("unable to update deployment: %v", err)
|
||||
return errors.Wrap(err, "unable to update deployment")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@ -109,11 +110,11 @@ func CreateOrUpdateDeployment(client clientset.Interface, deploy *apps.Deploymen
|
||||
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)
|
||||
return errors.Wrap(err, "unable to create daemonset")
|
||||
}
|
||||
|
||||
if _, err := client.AppsV1().DaemonSets(ds.ObjectMeta.Namespace).Update(ds); err != nil {
|
||||
return fmt.Errorf("unable to update daemonset: %v", err)
|
||||
return errors.Wrap(err, "unable to update daemonset")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@ -141,11 +142,11 @@ func DeleteDeploymentForeground(client clientset.Interface, namespace, name stri
|
||||
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)
|
||||
return errors.Wrap(err, "unable to create RBAC role")
|
||||
}
|
||||
|
||||
if _, err := client.RbacV1().Roles(role.ObjectMeta.Namespace).Update(role); err != nil {
|
||||
return fmt.Errorf("unable to update RBAC role: %v", err)
|
||||
return errors.Wrap(err, "unable to update RBAC role")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@ -155,11 +156,11 @@ func CreateOrUpdateRole(client clientset.Interface, role *rbac.Role) error {
|
||||
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)
|
||||
return errors.Wrap(err, "unable to create RBAC rolebinding")
|
||||
}
|
||||
|
||||
if _, err := client.RbacV1().RoleBindings(roleBinding.ObjectMeta.Namespace).Update(roleBinding); err != nil {
|
||||
return fmt.Errorf("unable to update RBAC rolebinding: %v", err)
|
||||
return errors.Wrap(err, "unable to update RBAC rolebinding")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@ -169,11 +170,11 @@ func CreateOrUpdateRoleBinding(client clientset.Interface, roleBinding *rbac.Rol
|
||||
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)
|
||||
return errors.Wrap(err, "unable to create RBAC clusterrole")
|
||||
}
|
||||
|
||||
if _, err := client.RbacV1().ClusterRoles().Update(clusterRole); err != nil {
|
||||
return fmt.Errorf("unable to update RBAC clusterrole: %v", err)
|
||||
return errors.Wrap(err, "unable to update RBAC clusterrole")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@ -183,23 +184,26 @@ func CreateOrUpdateClusterRole(client clientset.Interface, clusterRole *rbac.Clu
|
||||
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)
|
||||
return errors.Wrap(err, "unable to create RBAC clusterrolebinding")
|
||||
}
|
||||
|
||||
if _, err := client.RbacV1().ClusterRoleBindings().Update(clusterRoleBinding); err != nil {
|
||||
return fmt.Errorf("unable to update RBAC clusterrolebinding: %v", err)
|
||||
return errors.Wrap(err, "unable to update RBAC clusterrolebinding")
|
||||
}
|
||||
}
|
||||
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) {
|
||||
// PatchNodeOnce executes patchFn on the node object found by the node name.
|
||||
// This is a condition function meant to be used with wait.Poll. false, nil
|
||||
// implies it is safe to try again, an error indicates no more tries should be
|
||||
// made and true indicates success.
|
||||
func PatchNodeOnce(client clientset.Interface, nodeName string, patchFn func(*v1.Node)) func() (bool, error) {
|
||||
return func() (bool, error) {
|
||||
// First get the node object
|
||||
n, err := client.CoreV1().Nodes().Get(nodeName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
// TODO this should only be for timeouts
|
||||
return false, nil
|
||||
}
|
||||
|
||||
@ -211,7 +215,7 @@ func PatchNode(client clientset.Interface, nodeName string, patchFn func(*v1.Nod
|
||||
|
||||
oldData, err := json.Marshal(n)
|
||||
if err != nil {
|
||||
return false, err
|
||||
return false, errors.Wrapf(err, "failed to marshal unmodified node %q into JSON", n.Name)
|
||||
}
|
||||
|
||||
// Execute the mutating function
|
||||
@ -219,22 +223,32 @@ func PatchNode(client clientset.Interface, nodeName string, patchFn func(*v1.Nod
|
||||
|
||||
newData, err := json.Marshal(n)
|
||||
if err != nil {
|
||||
return false, err
|
||||
return false, errors.Wrapf(err, "failed to marshal modified node %q into JSON", n.Name)
|
||||
}
|
||||
|
||||
patchBytes, err := strategicpatch.CreateTwoWayMergePatch(oldData, newData, v1.Node{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
return false, errors.Wrap(err, "failed to create two way merge patch")
|
||||
}
|
||||
|
||||
if _, err := client.CoreV1().Nodes().Patch(n.Name, types.StrategicMergePatchType, patchBytes); err != nil {
|
||||
// TODO also check for timeouts
|
||||
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 false, errors.Wrapf(err, "error patching node %q through apiserver", n.Name)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// PatchNode tries to patch a node using patchFn for the actual mutating logic.
|
||||
// Retries are provided by the wait package.
|
||||
func PatchNode(client clientset.Interface, nodeName string, patchFn func(*v1.Node)) error {
|
||||
// wait.Poll will rerun the condition function every interval function if
|
||||
// the function returns false. If the condition function returns an error
|
||||
// then the retries end and the error is returned.
|
||||
return wait.Poll(constants.APICallRetryInterval, constants.PatchNodeTimeout, PatchNodeOnce(client, nodeName, patchFn))
|
||||
}
|
||||
|
83
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient/idempotency_test.go
generated
vendored
Normal file
83
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient/idempotency_test.go
generated
vendored
Normal file
@ -0,0 +1,83 @@
|
||||
/*
|
||||
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 apiclient_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient"
|
||||
kubeletapis "k8s.io/kubernetes/pkg/kubelet/apis"
|
||||
)
|
||||
|
||||
func TestPatchNodeNonErrorCases(t *testing.T) {
|
||||
testcases := []struct {
|
||||
name string
|
||||
lookupName string
|
||||
node v1.Node
|
||||
success bool
|
||||
}{
|
||||
{
|
||||
name: "simple update",
|
||||
lookupName: "testnode",
|
||||
node: v1.Node{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "testnode",
|
||||
Labels: map[string]string{kubeletapis.LabelHostname: ""},
|
||||
},
|
||||
},
|
||||
success: true,
|
||||
},
|
||||
{
|
||||
name: "node does not exist",
|
||||
lookupName: "whale",
|
||||
success: false,
|
||||
},
|
||||
{
|
||||
name: "node not labelled yet",
|
||||
lookupName: "robin",
|
||||
node: v1.Node{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "robin",
|
||||
},
|
||||
},
|
||||
success: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
client := fake.NewSimpleClientset()
|
||||
_, err := client.Core().Nodes().Create(&tc.node)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create node to fake client: %v", err)
|
||||
}
|
||||
conditionFunction := apiclient.PatchNodeOnce(client, tc.lookupName, func(node *v1.Node) {
|
||||
node.Name = "testNewNode"
|
||||
})
|
||||
success, err := conditionFunction()
|
||||
if err != nil {
|
||||
t.Fatalf("did not expect error: %v", err)
|
||||
}
|
||||
if success != tc.success {
|
||||
t.Fatalf("expected %v got %v", tc.success, success)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
9
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient/init_dryrun.go
generated
vendored
9
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient/init_dryrun.go
generated
vendored
@ -17,10 +17,11 @@ limitations under the License.
|
||||
package apiclient
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@ -79,7 +80,7 @@ func (idr *InitDryRunGetter) HandleListAction(action core.ListAction) (bool, run
|
||||
}
|
||||
|
||||
// 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
|
||||
// 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
|
||||
@ -88,12 +89,12 @@ func (idr *InitDryRunGetter) handleKubernetesService(action core.GetAction) (boo
|
||||
|
||||
_, svcSubnet, err := net.ParseCIDR(idr.serviceSubnet)
|
||||
if err != nil {
|
||||
return true, nil, fmt.Errorf("error parsing CIDR %q: %v", idr.serviceSubnet, err)
|
||||
return true, nil, errors.Wrapf(err, "error parsing CIDR %q", idr.serviceSubnet)
|
||||
}
|
||||
|
||||
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)
|
||||
return true, nil, errors.Wrapf(err, "unable to get first IP address from the given CIDR (%s)", svcSubnet.String())
|
||||
}
|
||||
|
||||
// The only used field of this Service object is the ClusterIP, which kube-dns uses to calculate its own IP
|
||||
|
48
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient/wait.go
generated
vendored
48
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient/wait.go
generated
vendored
@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
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.
|
||||
@ -22,6 +22,7 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@ -29,6 +30,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||
kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
|
||||
)
|
||||
|
||||
@ -49,6 +51,8 @@ type Waiter interface {
|
||||
WaitForStaticPodControlPlaneHashes(nodeName string) (map[string]string, error)
|
||||
// WaitForHealthyKubelet blocks until the kubelet /healthz endpoint returns 'ok'
|
||||
WaitForHealthyKubelet(initalTimeout time.Duration, healthzEndpoint string) error
|
||||
// WaitForKubeletAndFunc is a wrapper for WaitForHealthyKubelet that also blocks for a function
|
||||
WaitForKubeletAndFunc(f func() error) error
|
||||
// SetTimeout adjusts the timeout to the specified duration
|
||||
SetTimeout(timeout time.Duration)
|
||||
}
|
||||
@ -131,24 +135,46 @@ func (w *KubeWaiter) WaitForPodToDisappear(podName string) error {
|
||||
// WaitForHealthyKubelet blocks until the kubelet /healthz endpoint returns 'ok'
|
||||
func (w *KubeWaiter) WaitForHealthyKubelet(initalTimeout time.Duration, healthzEndpoint string) error {
|
||||
time.Sleep(initalTimeout)
|
||||
fmt.Printf("[kubelet-check] Initial timeout of %v passed.\n", 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.Println("[kubelet-check] It seems like the kubelet isn't running or healthy.")
|
||||
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.Println("[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 errors.New("the kubelet healthz endpoint is unhealthy")
|
||||
}
|
||||
return nil
|
||||
}, 5) // a failureThreshold of five means waiting for a total of 155 seconds
|
||||
}
|
||||
|
||||
// WaitForKubeletAndFunc waits primarily for the function f to execute, even though it might take some time. If that takes a long time, and the kubelet
|
||||
// /healthz continuously are unhealthy, kubeadm will error out after a period of exponential backoff
|
||||
func (w *KubeWaiter) WaitForKubeletAndFunc(f func() error) error {
|
||||
errorChan := make(chan error)
|
||||
|
||||
go func(errC chan error, waiter Waiter) {
|
||||
if err := waiter.WaitForHealthyKubelet(40*time.Second, fmt.Sprintf("http://localhost:%d/healthz", kubeadmconstants.KubeletHealthzPort)); err != nil {
|
||||
errC <- err
|
||||
}
|
||||
}(errorChan, w)
|
||||
|
||||
go func(errC chan error, waiter Waiter) {
|
||||
// This main goroutine sends whatever the f function returns (error or not) to the channel
|
||||
// This in order to continue on success (nil error), or just fail if the function returns an error
|
||||
errC <- f()
|
||||
}(errorChan, w)
|
||||
|
||||
// This call is blocking until one of the goroutines sends to errorChan
|
||||
return <-errorChan
|
||||
}
|
||||
|
||||
// SetTimeout adjusts the timeout to the specified duration
|
||||
func (w *KubeWaiter) SetTimeout(timeout time.Duration) {
|
||||
w.timeout = timeout
|
||||
@ -211,20 +237,6 @@ func (w *KubeWaiter) WaitForStaticPodHashChange(nodeName, component, previousHas
|
||||
})
|
||||
}
|
||||
|
||||
// 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) {
|
||||
|
||||
|
10
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/arguments.go
generated
vendored
10
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/arguments.go
generated
vendored
@ -20,6 +20,8 @@ import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// BuildArgumentListFromMap takes two string-string maps, one with the base arguments and one
|
||||
@ -89,10 +91,10 @@ func ReplaceArgument(command []string, argMutateFunc func(map[string]string) map
|
||||
// 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 '--'")
|
||||
return "", "", errors.New("the argument should start with '--'")
|
||||
}
|
||||
if !strings.Contains(arg, "=") {
|
||||
return "", "", fmt.Errorf("the argument should have a '=' between the flag and the value")
|
||||
return "", "", errors.New("the argument should have a '=' between the flag and the value")
|
||||
}
|
||||
// Remove the starting --
|
||||
arg = strings.TrimPrefix(arg, "--")
|
||||
@ -101,10 +103,10 @@ func parseArgument(arg string) (string, string, error) {
|
||||
|
||||
// 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")
|
||||
return "", "", errors.New("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 "", "", errors.New("the argument must have a key")
|
||||
}
|
||||
|
||||
return keyvalSlice[0], keyvalSlice[1], nil
|
||||
|
19
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/audit/BUILD
generated
vendored
19
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/audit/BUILD
generated
vendored
@ -7,11 +7,12 @@ go_library(
|
||||
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",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/apis/audit/install:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/apis/audit/v1:go_default_library",
|
||||
"//vendor/github.com/pkg/errors:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
@ -20,10 +21,10 @@ go_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",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/apis/audit/install:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/apis/audit/v1:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
|
23
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/audit/utils.go
generated
vendored
23
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/audit/utils.go
generated
vendored
@ -17,39 +17,40 @@ limitations under the License.
|
||||
package audit
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
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"
|
||||
auditv1 "k8s.io/apiserver/pkg/apis/audit/v1"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/util"
|
||||
)
|
||||
|
||||
// CreateDefaultAuditLogPolicy writes the default audit log policy to disk.
|
||||
func CreateDefaultAuditLogPolicy(policyFile string) error {
|
||||
policy := auditv1beta1.Policy{
|
||||
policy := auditv1.Policy{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: auditv1beta1.SchemeGroupVersion.String(),
|
||||
APIVersion: auditv1.SchemeGroupVersion.String(),
|
||||
Kind: "Policy",
|
||||
},
|
||||
Rules: []auditv1beta1.PolicyRule{
|
||||
Rules: []auditv1.PolicyRule{
|
||||
{
|
||||
Level: auditv1beta1.LevelMetadata,
|
||||
Level: auditv1.LevelMetadata,
|
||||
},
|
||||
},
|
||||
}
|
||||
return writePolicyToDisk(policyFile, &policy)
|
||||
}
|
||||
|
||||
func writePolicyToDisk(policyFile string, policy *auditv1beta1.Policy) error {
|
||||
func writePolicyToDisk(policyFile string, policy *auditv1.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)
|
||||
return errors.Wrapf(err, "failed to create directory %q: ", filepath.Dir(policyFile))
|
||||
}
|
||||
|
||||
scheme := runtime.NewScheme()
|
||||
@ -59,14 +60,14 @@ func writePolicyToDisk(policyFile string, policy *auditv1beta1.Policy) error {
|
||||
codecs := serializer.NewCodecFactory(scheme)
|
||||
|
||||
// writes the policy to disk
|
||||
serialized, err := util.MarshalToYamlForCodecs(policy, auditv1beta1.SchemeGroupVersion, codecs)
|
||||
serialized, err := util.MarshalToYamlForCodecs(policy, auditv1.SchemeGroupVersion, codecs)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal audit policy to YAML: %v", err)
|
||||
return errors.Wrap(err, "failed to marshal audit policy to YAML")
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(policyFile, serialized, 0600); err != nil {
|
||||
return fmt.Errorf("failed to write audit policy to %v: %v", policyFile, err)
|
||||
return errors.Wrapf(err, "failed to write audit policy to %v: ", policyFile)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
4
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/audit/utils_test.go
generated
vendored
4
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/audit/utils_test.go
generated
vendored
@ -25,7 +25,7 @@ import (
|
||||
"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"
|
||||
auditv1 "k8s.io/apiserver/pkg/apis/audit/v1"
|
||||
)
|
||||
|
||||
func cleanup(t *testing.T, path string) {
|
||||
@ -54,7 +54,7 @@ func TestCreateDefaultAuditLogPolicy(t *testing.T) {
|
||||
scheme := runtime.NewScheme()
|
||||
install.Install(scheme)
|
||||
codecs := serializer.NewCodecFactory(scheme)
|
||||
policy := auditv1beta1.Policy{}
|
||||
policy := auditv1.Policy{}
|
||||
err = runtime.DecodeInto(codecs.UniversalDecoder(), policyBytes, &policy)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to decode written policy: %v", err)
|
||||
|
9
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/cgroupdriver.go
generated
vendored
9
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/cgroupdriver.go
generated
vendored
@ -17,9 +17,10 @@ limitations under the License.
|
||||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
utilsexec "k8s.io/utils/exec"
|
||||
)
|
||||
|
||||
@ -39,7 +40,7 @@ func GetCgroupDriverDocker(execer utilsexec.Interface) (string, error) {
|
||||
|
||||
func validateCgroupDriver(driver string) error {
|
||||
if driver != "cgroupfs" && driver != "systemd" {
|
||||
return fmt.Errorf("unknown cgroup driver %q", driver)
|
||||
return errors.Errorf("unknown cgroup driver %q", driver)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -51,7 +52,7 @@ func validateCgroupDriver(driver string) error {
|
||||
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 "", errors.Wrap(err, "cannot execute 'docker info'")
|
||||
}
|
||||
return string(out), nil
|
||||
}
|
||||
@ -71,5 +72,5 @@ func getCgroupDriverFromDockerInfo(info string) (string, error) {
|
||||
}
|
||||
return driver, nil
|
||||
}
|
||||
return "", fmt.Errorf("cgroup driver is not defined in 'docker info'")
|
||||
return "", errors.New("cgroup driver is not defined in 'docker info'")
|
||||
}
|
||||
|
66
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/chroot_test.go
generated
vendored
Normal file
66
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/chroot_test.go
generated
vendored
Normal file
@ -0,0 +1,66 @@
|
||||
// +build !windows
|
||||
|
||||
/*
|
||||
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 (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Can't just call Chroot() because it will affect other tests in the
|
||||
// same process. Golang makes it hard to just call fork(), so instead
|
||||
// we exec ourselves again, and run the test in the new subprocess.
|
||||
|
||||
func testChrootReal(t *testing.T) {
|
||||
testfile := filepath.FromSlash("/" + filepath.Base(os.Args[0]))
|
||||
dir := filepath.Dir(os.Args[0])
|
||||
if dir == "." {
|
||||
t.Skip("skipping: running test at root somehow")
|
||||
}
|
||||
if err := Chroot(dir); err != nil {
|
||||
if strings.Contains(err.Error(), "operation not permitted") {
|
||||
t.Skip("skipping: insufficient permissions to chroot")
|
||||
}
|
||||
t.Fatalf("chroot error: %v", err)
|
||||
}
|
||||
|
||||
// All file access should now be relative to `dir`
|
||||
if _, err := os.Stat(testfile); err != nil {
|
||||
t.Errorf("Expected file %q to exist, but got %v", testfile, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestChroot(t *testing.T) {
|
||||
if os.Getenv("GO_TEST_CHROOT_FOR_REALZ") == "1" {
|
||||
testChrootReal(t)
|
||||
return
|
||||
}
|
||||
|
||||
cmd := exec.Command(os.Args[0], "-test.v", "-test.run=TestChroot")
|
||||
cmd.Env = []string{"GO_TEST_CHROOT_FOR_REALZ=1"}
|
||||
|
||||
out, err := cmd.Output()
|
||||
t.Logf("subprocess output:\n%s", out)
|
||||
if err != nil {
|
||||
t.Errorf("subprocess error: %v", err)
|
||||
}
|
||||
}
|
40
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/chroot_unix.go
generated
vendored
Normal file
40
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/chroot_unix.go
generated
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
// +build !windows
|
||||
|
||||
/*
|
||||
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 (
|
||||
"github.com/pkg/errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// Chroot chroot()s to the new path.
|
||||
// NB: All file paths after this call are effectively relative to
|
||||
// `rootfs`
|
||||
func Chroot(rootfs string) error {
|
||||
if err := syscall.Chroot(rootfs); err != nil {
|
||||
return errors.Wrapf(err, "unable to chroot to %s", rootfs)
|
||||
}
|
||||
root := filepath.FromSlash("/")
|
||||
if err := os.Chdir(root); err != nil {
|
||||
return errors.Wrapf(err, "unable to chdir to %s", root)
|
||||
}
|
||||
return nil
|
||||
}
|
30
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/chroot_windows.go
generated
vendored
Normal file
30
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/chroot_windows.go
generated
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
// +build windows
|
||||
|
||||
/*
|
||||
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 (
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Chroot chroot()s to the new path.
|
||||
// NB: All file paths after this call are effectively relative to
|
||||
// `rootfs`
|
||||
func Chroot(rootfs string) error {
|
||||
return errors.New("chroot is not implemented on Windows")
|
||||
}
|
58
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/BUILD
generated
vendored
58
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/BUILD
generated
vendored
@ -10,49 +10,62 @@ go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"cluster.go",
|
||||
"masterconfig.go",
|
||||
"nodeconfig.go",
|
||||
"common.go",
|
||||
"initconfiguration.go",
|
||||
"joinconfiguration.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/v1beta1:go_default_library",
|
||||
"//cmd/kubeadm/app/apis/kubeadm/validation:go_default_library",
|
||||
"//cmd/kubeadm/app/componentconfigs:go_default_library",
|
||||
"//cmd/kubeadm/app/constants:go_default_library",
|
||||
"//cmd/kubeadm/app/util:go_default_library",
|
||||
"//cmd/kubeadm/app/util/config/strict: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",
|
||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/net:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/version:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/kubernetes:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/tools/clientcmd:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/util/cert:go_default_library",
|
||||
"//staging/src/k8s.io/cluster-bootstrap/token/util:go_default_library",
|
||||
"//vendor/github.com/pkg/errors:go_default_library",
|
||||
"//vendor/k8s.io/klog:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"masterconfig_test.go",
|
||||
"nodeconfig_test.go",
|
||||
"cluster_test.go",
|
||||
"common_test.go",
|
||||
"initconfiguration_test.go",
|
||||
"joinconfiguration_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/apis/kubeadm/v1beta1:go_default_library",
|
||||
"//cmd/kubeadm/app/constants:go_default_library",
|
||||
"//cmd/kubeadm/app/util:go_default_library",
|
||||
"//cmd/kubeadm/app/util/apiclient:go_default_library",
|
||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/version:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/kubernetes:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/kubernetes/fake: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",
|
||||
"//vendor/github.com/renstrom/dedent:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
@ -65,6 +78,9 @@ filegroup(
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
srcs = [
|
||||
":package-srcs",
|
||||
"//cmd/kubeadm/app/util/config/strict:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
|
231
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/cluster.go
generated
vendored
231
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/cluster.go
generated
vendored
@ -17,49 +17,246 @@ limitations under the License.
|
||||
package config
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
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/version"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
certutil "k8s.io/client-go/util/cert"
|
||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||
kubeadmscheme "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/scheme"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/componentconfigs"
|
||||
"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) {
|
||||
func FetchConfigFromFileOrCluster(client clientset.Interface, w io.Writer, logPrefix, cfgPath string, newControlPlane bool) (*kubeadmapi.InitConfiguration, error) {
|
||||
// Load the configuration from a file or the cluster
|
||||
configBytes, err := loadConfigurationBytes(client, w, logPrefix, cfgPath)
|
||||
initcfg, err := loadConfiguration(client, w, logPrefix, cfgPath, newControlPlane)
|
||||
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)
|
||||
// Apply dynamic defaults
|
||||
if err := SetInitDynamicDefaults(initcfg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return initcfg, err
|
||||
}
|
||||
|
||||
// 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) {
|
||||
// loadConfiguration loads the configuration byte slice from either a file or the cluster ConfigMap
|
||||
func loadConfiguration(client clientset.Interface, w io.Writer, logPrefix, cfgPath string, newControlPlane bool) (*kubeadmapi.InitConfiguration, 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)
|
||||
return loadInitConfigurationFromFile(cfgPath)
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, "[%s] Reading configuration from the cluster...\n", logPrefix)
|
||||
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.KubeadmConfigConfigMap)
|
||||
return getInitConfigurationFromCluster(constants.KubernetesDir, client, newControlPlane)
|
||||
}
|
||||
|
||||
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)
|
||||
func loadInitConfigurationFromFile(cfgPath string) (*kubeadmapi.InitConfiguration, error) {
|
||||
configBytes, err := ioutil.ReadFile(cfgPath)
|
||||
if err != nil {
|
||||
return nil, 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
|
||||
// Unmarshal the versioned configuration populated from the file,
|
||||
// convert it to the internal API types, then default and validate
|
||||
// NB the file contains multiple YAML, with a combination of
|
||||
// - a YAML with a InitConfiguration object
|
||||
// - a YAML with a ClusterConfiguration object (without embedded component configs)
|
||||
// - separated YAML for components configs
|
||||
initcfg, err := BytesToInternalConfig(configBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return initcfg, nil
|
||||
}
|
||||
|
||||
func getInitConfigurationFromCluster(kubeconfigDir string, client clientset.Interface, newControlPlane bool) (*kubeadmapi.InitConfiguration, error) {
|
||||
// TODO: This code should support reading the MasterConfiguration key as well for backwards-compat
|
||||
// Also, the config map really should be KubeadmConfigConfigMap...
|
||||
configMap, err := client.CoreV1().ConfigMaps(metav1.NamespaceSystem).Get(constants.KubeadmConfigConfigMap, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get config map")
|
||||
}
|
||||
|
||||
// InitConfiguration is composed with data from different places
|
||||
initcfg := &kubeadmapi.InitConfiguration{}
|
||||
|
||||
// gets ClusterConfiguration from kubeadm-config
|
||||
clusterConfigurationData, ok := configMap.Data[constants.ClusterConfigurationConfigMapKey]
|
||||
if !ok {
|
||||
return nil, errors.Errorf("unexpected error when reading kubeadm-config ConfigMap: %s key value pair missing", constants.ClusterConfigurationConfigMapKey)
|
||||
}
|
||||
if err := runtime.DecodeInto(kubeadmscheme.Codecs.UniversalDecoder(), []byte(clusterConfigurationData), &initcfg.ClusterConfiguration); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to decode cluster configuration data")
|
||||
}
|
||||
|
||||
// gets the component configs from the corresponding config maps
|
||||
if err := getComponentConfigs(client, &initcfg.ClusterConfiguration); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get component configs")
|
||||
}
|
||||
|
||||
// if this isn't a new controlplane instance (e.g. in case of kubeadm upgrades)
|
||||
// get nodes specific information as well
|
||||
if !newControlPlane {
|
||||
// gets the nodeRegistration for the current from the node object
|
||||
if err := getNodeRegistration(kubeconfigDir, client, &initcfg.NodeRegistration); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get node registration")
|
||||
}
|
||||
// gets the APIEndpoint for the current node from then ClusterStatus in the kubeadm-config ConfigMap
|
||||
if err := getAPIEndpoint(configMap.Data, initcfg.NodeRegistration.Name, &initcfg.LocalAPIEndpoint); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to getAPIEndpoint")
|
||||
}
|
||||
}
|
||||
|
||||
return initcfg, nil
|
||||
}
|
||||
|
||||
// getNodeRegistration returns the nodeRegistration for the current node
|
||||
func getNodeRegistration(kubeconfigDir string, client clientset.Interface, nodeRegistration *kubeadmapi.NodeRegistrationOptions) error {
|
||||
// gets the name of the current node
|
||||
nodeName, err := getNodeNameFromKubeletConfig(kubeconfigDir)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to get node name from kubelet config")
|
||||
}
|
||||
|
||||
// gets the corresponding node and retrives attributes stored there.
|
||||
node, err := client.CoreV1().Nodes().Get(nodeName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "faild to get corresponding node")
|
||||
}
|
||||
|
||||
criSocket, ok := node.ObjectMeta.Annotations[constants.AnnotationKubeadmCRISocket]
|
||||
if !ok {
|
||||
return errors.Errorf("node %s doesn't have %s annotation", nodeName, constants.AnnotationKubeadmCRISocket)
|
||||
}
|
||||
|
||||
// returns the nodeRegistration attributes
|
||||
nodeRegistration.Name = nodeName
|
||||
nodeRegistration.CRISocket = criSocket
|
||||
nodeRegistration.Taints = node.Spec.Taints
|
||||
// NB. currently nodeRegistration.KubeletExtraArgs isn't stored at node level but only in the kubeadm-flags.env
|
||||
// that isn't modified during upgrades
|
||||
// in future we might reconsider this thus enabling changes to the kubeadm-flags.env during upgrades as well
|
||||
return nil
|
||||
}
|
||||
|
||||
// getNodeNameFromConfig gets the node name from a kubelet config file
|
||||
// TODO: in future we want to switch to a more canonical way for doing this e.g. by having this
|
||||
// information in the local kubelet config.yaml
|
||||
func getNodeNameFromKubeletConfig(kubeconfigDir string) (string, error) {
|
||||
// loads the kubelet.conf file
|
||||
fileName := filepath.Join(kubeconfigDir, constants.KubeletKubeConfigFileName)
|
||||
config, err := clientcmd.LoadFromFile(fileName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// gets the info about the current user
|
||||
authInfo := config.AuthInfos[config.Contexts[config.CurrentContext].AuthInfo]
|
||||
|
||||
// gets the X509 certificate with current user credentials
|
||||
var certs []*x509.Certificate
|
||||
if len(authInfo.ClientCertificateData) > 0 {
|
||||
// if the config file uses an embedded x509 certificate (e.g. kubelet.conf created by kubeadm), parse it
|
||||
if certs, err = certutil.ParseCertsPEM(authInfo.ClientCertificateData); err != nil {
|
||||
return "", err
|
||||
}
|
||||
} else if len(authInfo.ClientCertificate) > 0 {
|
||||
// if the config file links an external x509 certificate (e.g. kubelet.conf created by TLS bootstrap), load it
|
||||
if certs, err = certutil.CertsFromFile(authInfo.ClientCertificate); err != nil {
|
||||
return "", err
|
||||
}
|
||||
} else {
|
||||
return "", errors.New("invalid kubelet.conf. X509 certificate expected")
|
||||
}
|
||||
|
||||
// We are only putting one certificate in the certificate pem file, so it's safe to just pick the first one
|
||||
// TODO: Support multiple certs here in order to be able to rotate certs
|
||||
cert := certs[0]
|
||||
|
||||
// gets the node name from the certificate common name
|
||||
return strings.TrimPrefix(cert.Subject.CommonName, constants.NodesUserPrefix), nil
|
||||
}
|
||||
|
||||
// getAPIEndpoint returns the APIEndpoint for the current node
|
||||
func getAPIEndpoint(data map[string]string, nodeName string, apiEndpoint *kubeadmapi.APIEndpoint) error {
|
||||
// gets the ClusterStatus from kubeadm-config
|
||||
clusterStatus, err := unmarshalClusterStatus(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// gets the APIEndpoint for the current machine from the ClusterStatus
|
||||
e, ok := clusterStatus.APIEndpoints[nodeName]
|
||||
if !ok {
|
||||
return errors.New("failed to get APIEndpoint information for this node")
|
||||
}
|
||||
|
||||
apiEndpoint.AdvertiseAddress = e.AdvertiseAddress
|
||||
apiEndpoint.BindPort = e.BindPort
|
||||
return nil
|
||||
}
|
||||
|
||||
// getComponentConfigs gets the component configs from the corresponding config maps
|
||||
func getComponentConfigs(client clientset.Interface, clusterConfiguration *kubeadmapi.ClusterConfiguration) error {
|
||||
// some config maps is versioned, so we need the KubernetesVersion for getting the right config map
|
||||
k8sVersion := version.MustParseGeneric(clusterConfiguration.KubernetesVersion)
|
||||
for kind, registration := range componentconfigs.Known {
|
||||
obj, err := registration.GetFromConfigMap(client, k8sVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if ok := registration.SetToInternalConfig(obj, clusterConfiguration); !ok {
|
||||
return errors.Errorf("couldn't save componentconfig value for kind %q", string(kind))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetClusterStatus returns the kubeadm cluster status read from the kubeadm-config ConfigMap
|
||||
func GetClusterStatus(client clientset.Interface) (*kubeadmapi.ClusterStatus, error) {
|
||||
configMap, err := client.CoreV1().ConfigMaps(metav1.NamespaceSystem).Get(constants.KubeadmConfigConfigMap, metav1.GetOptions{})
|
||||
if apierrors.IsNotFound(err) {
|
||||
return &kubeadmapi.ClusterStatus{}, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
clusterStatus, err := unmarshalClusterStatus(configMap.Data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return clusterStatus, nil
|
||||
}
|
||||
|
||||
func unmarshalClusterStatus(data map[string]string) (*kubeadmapi.ClusterStatus, error) {
|
||||
clusterStatusData, ok := data[constants.ClusterStatusConfigMapKey]
|
||||
if !ok {
|
||||
return nil, errors.Errorf("unexpected error when reading kubeadm-config ConfigMap: %s key value pair missing", constants.ClusterStatusConfigMapKey)
|
||||
}
|
||||
clusterStatus := &kubeadmapi.ClusterStatus{}
|
||||
if err := runtime.DecodeInto(kubeadmscheme.Codecs.UniversalDecoder(), []byte(clusterStatusData), clusterStatus); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return clusterStatus, nil
|
||||
}
|
||||
|
874
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/cluster_test.go
generated
vendored
Normal file
874
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/cluster_test.go
generated
vendored
Normal file
@ -0,0 +1,874 @@
|
||||
/*
|
||||
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"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/version"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
clientsetfake "k8s.io/client-go/kubernetes/fake"
|
||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient"
|
||||
)
|
||||
|
||||
var k8sVersionString = "v1.12.0"
|
||||
var k8sVersion = version.MustParseGeneric(k8sVersionString)
|
||||
var nodeName = "mynode"
|
||||
var cfgFiles = map[string][]byte{
|
||||
"InitConfiguration_v1beta1": []byte(`
|
||||
apiVersion: kubeadm.k8s.io/v1beta1
|
||||
kind: InitConfiguration
|
||||
`),
|
||||
"ClusterConfiguration_v1beta1": []byte(`
|
||||
apiVersion: kubeadm.k8s.io/v1beta1
|
||||
kind: ClusterConfiguration
|
||||
kubernetesVersion: ` + k8sVersionString + `
|
||||
`),
|
||||
"ClusterStatus_v1beta1": []byte(`
|
||||
apiVersion: kubeadm.k8s.io/v1beta1
|
||||
kind: ClusterStatus
|
||||
apiEndpoints:
|
||||
` + nodeName + `:
|
||||
advertiseAddress: 1.2.3.4
|
||||
bindPort: 1234
|
||||
`),
|
||||
"ClusterStatus_v1beta1_Without_APIEndpoints": []byte(`
|
||||
apiVersion: kubeadm.k8s.io/v1beta1
|
||||
kind: ClusterStatus
|
||||
`),
|
||||
"InitConfiguration_v1alpha3": []byte(`
|
||||
apiVersion: kubeadm.k8s.io/v1alpha3
|
||||
kind: InitConfiguration
|
||||
`),
|
||||
"ClusterConfiguration_v1alpha3": []byte(`
|
||||
apiVersion: kubeadm.k8s.io/v1alpha3
|
||||
kind: ClusterConfiguration
|
||||
kubernetesVersion: ` + k8sVersionString + `
|
||||
`),
|
||||
"ClusterStatus_v1alpha3": []byte(`
|
||||
apiVersion: kubeadm.k8s.io/v1alpha3
|
||||
kind: ClusterStatus
|
||||
apiEndpoints:
|
||||
` + nodeName + `:
|
||||
advertiseAddress: 1.2.3.4
|
||||
bindPort: 1234
|
||||
`),
|
||||
"ClusterStatus_v1alpha3_Without_APIEndpoints": []byte(`
|
||||
apiVersion: kubeadm.k8s.io/v1alpha3
|
||||
kind: ClusterStatus
|
||||
`),
|
||||
"Kube-proxy_componentconfig": []byte(`
|
||||
apiVersion: kubeproxy.config.k8s.io/v1alpha1
|
||||
kind: KubeProxyConfiguration
|
||||
`),
|
||||
"Kubelet_componentconfig": []byte(`
|
||||
apiVersion: kubelet.config.k8s.io/v1beta1
|
||||
kind: KubeletConfiguration
|
||||
`),
|
||||
}
|
||||
|
||||
var kubeletConfFiles = map[string][]byte{
|
||||
"withoutX509Cert": []byte(`
|
||||
apiVersion: v1
|
||||
clusters:
|
||||
- cluster:
|
||||
server: https://10.0.2.15:6443
|
||||
name: kubernetes
|
||||
contexts:
|
||||
- context:
|
||||
cluster: kubernetes
|
||||
user: system:node:mynode
|
||||
name: system:node:mynode@kubernetes
|
||||
current-context: system:node:mynode@kubernetes
|
||||
kind: Config
|
||||
preferences: {}
|
||||
users:
|
||||
- name: system:node:mynode
|
||||
user:
|
||||
`),
|
||||
"configWithEmbeddedCert": []byte(`
|
||||
apiVersion: v1
|
||||
clusters:
|
||||
- cluster:
|
||||
server: https://10.0.2.15:6443
|
||||
name: kubernetes
|
||||
contexts:
|
||||
- context:
|
||||
cluster: kubernetes
|
||||
user: system:node:mynode
|
||||
name: system:node:mynode@kubernetes
|
||||
current-context: system:node:mynode@kubernetes
|
||||
kind: Config
|
||||
preferences: {}
|
||||
users:
|
||||
- name: system:node:mynode
|
||||
user:
|
||||
client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUM4akNDQWRxZ0F3SUJBZ0lJQWl3VURhYk5vZ1F3RFFZSktvWklodmNOQVFFTEJRQXdGVEVUTUJFR0ExVUUKQXhNS2EzVmlaWEp1WlhSbGN6QWVGdzB4T0RBNU1ERXhOVE14TWpaYUZ3MHhPVEE1TURFeE5qQXhOVGxhTURReApGVEFUQmdOVkJBb1RESE41YzNSbGJUcHViMlJsY3pFYk1Ca0dBMVVFQXhNU2MzbHpkR1Z0T201dlpHVTZiWGx1CmIyUmxNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQWs2UXUzeStyNEZYUzZ4VkoKWU1vNE9kSkt3R1d1MHY4TEJIUnhhOUhvVHo1RXZLQnB1OVJoemt5dStUaFczb0xta2ZTRmNJcitHa0M5MW0zOApFelRmVE5JY2dsL0V5YkpmR1QvdGdUazZYd1kxY1UrUUdmSEFNNTBCVzFXTFVHc25CSllJZjA5eENnZTVoTkxLCnREeUJOWWNQZzg1bUJpOU9CNFJ2UlgyQVFRMjJwZ0xrQUpJWklOU0FEdUFrODN2b051SXM2YVY2bHBkR2Vva3YKdDlpTFdNR3p3a3ZTZUZQTlNGeWZ3Q055eENjb1FDQUNtSnJRS3NoeUE2bWNzdVhORWVXdlRQdVVFSWZPVFl4dwpxdkszRVBOK0xUYlA2anhUMWtTcFJUOSt4Z29uSlFhU0RsbUNBd20zRGJkSVppWUt3R2ppMkxKL0kvYWc0cTlzCjNLb0J2UUlEQVFBQm95Y3dKVEFPQmdOVkhROEJBZjhFQkFNQ0JhQXdFd1lEVlIwbEJBd3dDZ1lJS3dZQkJRVUgKQXdJd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFLcVVrU21jdW85OG5EK015b005VFdEV0pyTndySXpQTUNqRQpCSkdyREhVaHIwcEZlRjc0RHViODNzRXlaNjFxNUVQd2Y0enNLSzdzdDRUTzZhcE9pZWJYVmN3dnZoa09HQ2dFCmFVdGNOMjFhUGxtU0tOd0c4ai8yK3ZhbU80bGplK1NnZzRUUVB0eDZWejh5VXN2RFhxSUZycjNNd1gzSDA1RW4KWXAzN05JYkhKbGxHUW5LVHA5aTg5aXF4WXVhSERqZldiVHlEY3B5NldNVjdVaFYvY1plc3lGL0NBamNHd1V6YgowRlo5bW5tMnFONlBGWHZ4RmdMSGFWZzN2SVVCbkNmVVVyY1BDNE94VFNPK21aUmUxazh3eUFpVWovSk0rZllvCkcrMi9sbThUYVZqb1U3Rmk1S2E1RzVIWTJHTGFSN1ArSXhZY3JNSENsNjJZN1JxY3JuYz0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
|
||||
`),
|
||||
"configWithLinkedCert": []byte(`
|
||||
apiVersion: v1
|
||||
clusters:
|
||||
- cluster:
|
||||
server: https://10.0.2.15:6443
|
||||
name: kubernetes
|
||||
contexts:
|
||||
- context:
|
||||
cluster: kubernetes
|
||||
user: system:node:mynode
|
||||
name: system:node:mynode@kubernetes
|
||||
current-context: system:node:mynode@kubernetes
|
||||
kind: Config
|
||||
preferences: {}
|
||||
users:
|
||||
- name: system:node:mynode
|
||||
user:
|
||||
client-certificate: kubelet.pem
|
||||
`),
|
||||
}
|
||||
|
||||
var pemFiles = map[string][]byte{
|
||||
"mynode.pem": []byte(`
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIC8jCCAdqgAwIBAgIIAiwUDabNogQwDQYJKoZIhvcNAQELBQAwFTETMBEGA1UE
|
||||
AxMKa3ViZXJuZXRlczAeFw0xODA5MDExNTMxMjZaFw0xOTA5MDExNjAxNTlaMDQx
|
||||
FTATBgNVBAoTDHN5c3RlbTpub2RlczEbMBkGA1UEAxMSc3lzdGVtOm5vZGU6bXlu
|
||||
b2RlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAk6Qu3y+r4FXS6xVJ
|
||||
YMo4OdJKwGWu0v8LBHRxa9HoTz5EvKBpu9Rhzkyu+ThW3oLmkfSFcIr+GkC91m38
|
||||
EzTfTNIcgl/EybJfGT/tgTk6XwY1cU+QGfHAM50BW1WLUGsnBJYIf09xCge5hNLK
|
||||
tDyBNYcPg85mBi9OB4RvRX2AQQ22pgLkAJIZINSADuAk83voNuIs6aV6lpdGeokv
|
||||
t9iLWMGzwkvSeFPNSFyfwCNyxCcoQCACmJrQKshyA6mcsuXNEeWvTPuUEIfOTYxw
|
||||
qvK3EPN+LTbP6jxT1kSpRT9+xgonJQaSDlmCAwm3DbdIZiYKwGji2LJ/I/ag4q9s
|
||||
3KoBvQIDAQABoycwJTAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUH
|
||||
AwIwDQYJKoZIhvcNAQELBQADggEBAKqUkSmcuo98nD+MyoM9TWDWJrNwrIzPMCjE
|
||||
BJGrDHUhr0pFeF74Dub83sEyZ61q5EPwf4zsKK7st4TO6apOiebXVcwvvhkOGCgE
|
||||
aUtcN21aPlmSKNwG8j/2+vamO4lje+Sgg4TQPtx6Vz8yUsvDXqIFrr3MwX3H05En
|
||||
Yp37NIbHJllGQnKTp9i89iqxYuaHDjfWbTyDcpy6WMV7UhV/cZesyF/CAjcGwUzb
|
||||
0FZ9mnm2qN6PFXvxFgLHaVg3vIUBnCfUUrcPC4OxTSO+mZRe1k8wyAiUj/JM+fYo
|
||||
G+2/lm8TaVjoU7Fi5Ka5G5HY2GLaR7P+IxYcrMHCl62Y7Rqcrnc=
|
||||
-----END CERTIFICATE-----
|
||||
`),
|
||||
}
|
||||
|
||||
func TestLoadInitConfigurationFromFile(t *testing.T) {
|
||||
// Create temp folder for the test case
|
||||
tmpdir, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't create tmpdir")
|
||||
}
|
||||
defer os.RemoveAll(tmpdir)
|
||||
|
||||
var tests = []struct {
|
||||
name string
|
||||
fileContents []byte
|
||||
}{
|
||||
{
|
||||
name: "v1beta1.partial1",
|
||||
fileContents: cfgFiles["InitConfiguration_v1beta1"],
|
||||
},
|
||||
{
|
||||
name: "v1beta1.partial2",
|
||||
fileContents: cfgFiles["ClusterConfiguration_v1beta1"],
|
||||
},
|
||||
{
|
||||
name: "v1beta1.full",
|
||||
fileContents: bytes.Join([][]byte{
|
||||
cfgFiles["InitConfiguration_v1beta1"],
|
||||
cfgFiles["ClusterConfiguration_v1beta1"],
|
||||
cfgFiles["Kube-proxy_componentconfig"],
|
||||
cfgFiles["Kubelet_componentconfig"],
|
||||
}, []byte(kubeadmconstants.YAMLDocumentSeparator)),
|
||||
},
|
||||
{
|
||||
name: "v1alpha3.partial1",
|
||||
fileContents: cfgFiles["InitConfiguration_v1alpha3"],
|
||||
},
|
||||
{
|
||||
name: "v1alpha3.partial2",
|
||||
fileContents: cfgFiles["ClusterConfiguration_v1alpha3"],
|
||||
},
|
||||
{
|
||||
name: "v1alpha3.full",
|
||||
fileContents: bytes.Join([][]byte{
|
||||
cfgFiles["InitConfiguration_v1alpha3"],
|
||||
cfgFiles["ClusterConfiguration_v1alpha3"],
|
||||
cfgFiles["Kube-proxy_componentconfig"],
|
||||
cfgFiles["Kubelet_componentconfig"],
|
||||
}, []byte(kubeadmconstants.YAMLDocumentSeparator)),
|
||||
},
|
||||
}
|
||||
|
||||
for _, rt := range tests {
|
||||
t.Run(rt.name, func(t2 *testing.T) {
|
||||
cfgPath := filepath.Join(tmpdir, rt.name)
|
||||
err := ioutil.WriteFile(cfgPath, rt.fileContents, 0644)
|
||||
if err != nil {
|
||||
t.Errorf("Couldn't create file")
|
||||
return
|
||||
}
|
||||
|
||||
obj, err := loadInitConfigurationFromFile(cfgPath)
|
||||
if err != nil {
|
||||
t.Errorf("Error reading file: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if obj == nil {
|
||||
t.Errorf("Unexpected nil return value")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetNodeNameFromKubeletConfig(t *testing.T) {
|
||||
tmpdir, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't create tmpdir")
|
||||
}
|
||||
defer os.RemoveAll(tmpdir)
|
||||
|
||||
var tests = []struct {
|
||||
name string
|
||||
kubeconfigContent []byte
|
||||
pemContent []byte
|
||||
expectedError bool
|
||||
}{
|
||||
{
|
||||
name: "valid - with embedded cert",
|
||||
kubeconfigContent: kubeletConfFiles["configWithEmbeddedCert"],
|
||||
},
|
||||
{
|
||||
name: "invalid - linked cert missing",
|
||||
kubeconfigContent: kubeletConfFiles["configWithLinkedCert"],
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
name: "valid - with linked cert",
|
||||
kubeconfigContent: kubeletConfFiles["configWithLinkedCert"],
|
||||
pemContent: pemFiles["mynode.pem"],
|
||||
},
|
||||
{
|
||||
name: "invalid - without embedded or linked X509Cert",
|
||||
kubeconfigContent: kubeletConfFiles["withoutX509Cert"],
|
||||
expectedError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, rt := range tests {
|
||||
t.Run(rt.name, func(t2 *testing.T) {
|
||||
if len(rt.pemContent) > 0 {
|
||||
pemPath := filepath.Join(tmpdir, "kubelet.pem")
|
||||
err := ioutil.WriteFile(pemPath, rt.pemContent, 0644)
|
||||
if err != nil {
|
||||
t.Errorf("Couldn't create pem file: %v", err)
|
||||
return
|
||||
}
|
||||
rt.kubeconfigContent = []byte(strings.Replace(string(rt.kubeconfigContent), "kubelet.pem", pemPath, -1))
|
||||
}
|
||||
|
||||
kubeconfigPath := filepath.Join(tmpdir, kubeadmconstants.KubeletKubeConfigFileName)
|
||||
err := ioutil.WriteFile(kubeconfigPath, rt.kubeconfigContent, 0644)
|
||||
if err != nil {
|
||||
t.Errorf("Couldn't create kubeconfig: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
name, err := getNodeNameFromKubeletConfig(tmpdir)
|
||||
if rt.expectedError != (err != nil) {
|
||||
t.Errorf("unexpected return err from getNodeRegistration: %v", err)
|
||||
return
|
||||
}
|
||||
if rt.expectedError {
|
||||
return
|
||||
}
|
||||
|
||||
if name != nodeName {
|
||||
t.Errorf("invalid name")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetNodeRegistration(t *testing.T) {
|
||||
tmpdir, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't create tmpdir")
|
||||
}
|
||||
defer os.RemoveAll(tmpdir)
|
||||
|
||||
var tests = []struct {
|
||||
name string
|
||||
fileContents []byte
|
||||
node *v1.Node
|
||||
expectedError bool
|
||||
}{
|
||||
{
|
||||
name: "invalid - no kubelet.conf",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
name: "valid",
|
||||
fileContents: kubeletConfFiles["configWithEmbeddedCert"],
|
||||
node: &v1.Node{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: nodeName,
|
||||
Annotations: map[string]string{
|
||||
kubeadmconstants.AnnotationKubeadmCRISocket: "myCRIsocket",
|
||||
},
|
||||
},
|
||||
Spec: v1.NodeSpec{
|
||||
Taints: []v1.Taint{kubeadmconstants.MasterTaint},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid - no node",
|
||||
fileContents: kubeletConfFiles["configWithEmbeddedCert"],
|
||||
expectedError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, rt := range tests {
|
||||
t.Run(rt.name, func(t2 *testing.T) {
|
||||
cfgPath := filepath.Join(tmpdir, kubeadmconstants.KubeletKubeConfigFileName)
|
||||
if len(rt.fileContents) > 0 {
|
||||
err := ioutil.WriteFile(cfgPath, rt.fileContents, 0644)
|
||||
if err != nil {
|
||||
t.Errorf("Couldn't create file")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
client := clientsetfake.NewSimpleClientset()
|
||||
|
||||
if rt.node != nil {
|
||||
_, err := client.CoreV1().Nodes().Create(rt.node)
|
||||
if err != nil {
|
||||
t.Errorf("couldn't create Node")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
cfg := &kubeadmapi.InitConfiguration{}
|
||||
err = getNodeRegistration(tmpdir, client, &cfg.NodeRegistration)
|
||||
if rt.expectedError != (err != nil) {
|
||||
t.Errorf("unexpected return err from getNodeRegistration: %v", err)
|
||||
return
|
||||
}
|
||||
if rt.expectedError {
|
||||
return
|
||||
}
|
||||
|
||||
if cfg.NodeRegistration.Name != nodeName {
|
||||
t.Errorf("invalid cfg.NodeRegistration.Name")
|
||||
}
|
||||
if cfg.NodeRegistration.CRISocket != "myCRIsocket" {
|
||||
t.Errorf("invalid cfg.NodeRegistration.CRISocket")
|
||||
}
|
||||
if len(cfg.NodeRegistration.Taints) != 1 {
|
||||
t.Errorf("invalid cfg.NodeRegistration.Taints")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAPIEndpoint(t *testing.T) {
|
||||
var tests = []struct {
|
||||
name string
|
||||
configMap fakeConfigMap
|
||||
expectedError bool
|
||||
}{
|
||||
{
|
||||
name: "valid v1beta1",
|
||||
configMap: fakeConfigMap{
|
||||
name: kubeadmconstants.KubeadmConfigConfigMap, // ClusterConfiguration from kubeadm-config.
|
||||
data: map[string]string{
|
||||
kubeadmconstants.ClusterStatusConfigMapKey: string(cfgFiles["ClusterStatus_v1beta1"]),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid v1beta1 - No ClusterStatus in kubeadm-config ConfigMap",
|
||||
configMap: fakeConfigMap{
|
||||
name: kubeadmconstants.KubeadmConfigConfigMap, // ClusterConfiguration from kubeadm-config.
|
||||
data: map[string]string{},
|
||||
},
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
name: "invalid v1beta1 - ClusterStatus without APIEndopoints",
|
||||
configMap: fakeConfigMap{
|
||||
name: kubeadmconstants.KubeadmConfigConfigMap, // ClusterConfiguration from kubeadm-config.
|
||||
data: map[string]string{
|
||||
kubeadmconstants.ClusterStatusConfigMapKey: string(cfgFiles["ClusterStatus_v1beta1_Without_APIEndpoints"]),
|
||||
},
|
||||
},
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
name: "valid v1alpha3",
|
||||
configMap: fakeConfigMap{
|
||||
name: kubeadmconstants.KubeadmConfigConfigMap, // ClusterConfiguration from kubeadm-config.
|
||||
data: map[string]string{
|
||||
kubeadmconstants.ClusterStatusConfigMapKey: string(cfgFiles["ClusterStatus_v1alpha3"]),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid v1alpha3 - No ClusterStatus in kubeadm-config ConfigMap",
|
||||
configMap: fakeConfigMap{
|
||||
name: kubeadmconstants.KubeadmConfigConfigMap, // ClusterConfiguration from kubeadm-config.
|
||||
data: map[string]string{},
|
||||
},
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
name: "invalid v1alpha3 - ClusterStatus without APIEndopoints",
|
||||
configMap: fakeConfigMap{
|
||||
name: kubeadmconstants.KubeadmConfigConfigMap, // ClusterConfiguration from kubeadm-config.
|
||||
data: map[string]string{
|
||||
kubeadmconstants.ClusterStatusConfigMapKey: string(cfgFiles["ClusterStatus_v1alpha3_Without_APIEndpoints"]),
|
||||
},
|
||||
},
|
||||
expectedError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, rt := range tests {
|
||||
t.Run(rt.name, func(t *testing.T) {
|
||||
cfg := &kubeadmapi.InitConfiguration{}
|
||||
err := getAPIEndpoint(rt.configMap.data, nodeName, &cfg.LocalAPIEndpoint)
|
||||
if rt.expectedError != (err != nil) {
|
||||
t.Errorf("unexpected return err from getInitConfigurationFromCluster: %v", err)
|
||||
return
|
||||
}
|
||||
if rt.expectedError {
|
||||
return
|
||||
}
|
||||
|
||||
if cfg.LocalAPIEndpoint.AdvertiseAddress != "1.2.3.4" || cfg.LocalAPIEndpoint.BindPort != 1234 {
|
||||
t.Errorf("invalid cfg.APIEndpoint")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetComponentConfigs(t *testing.T) {
|
||||
var tests = []struct {
|
||||
name string
|
||||
configMaps []fakeConfigMap
|
||||
expectedError bool
|
||||
}{
|
||||
{
|
||||
name: "valid",
|
||||
configMaps: []fakeConfigMap{
|
||||
{
|
||||
name: kubeadmconstants.KubeProxyConfigMap, // Kube-proxy component config from corresponding ConfigMap.
|
||||
data: map[string]string{
|
||||
kubeadmconstants.KubeProxyConfigMapKey: string(cfgFiles["Kube-proxy_componentconfig"]),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: kubeadmconstants.GetKubeletConfigMapName(k8sVersion), // Kubelet component config from corresponding ConfigMap.
|
||||
data: map[string]string{
|
||||
kubeadmconstants.KubeletBaseConfigurationConfigMapKey: string(cfgFiles["Kubelet_componentconfig"]),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid - No kubelet component config ConfigMap",
|
||||
configMaps: []fakeConfigMap{
|
||||
{
|
||||
name: kubeadmconstants.KubeProxyConfigMap,
|
||||
data: map[string]string{
|
||||
kubeadmconstants.KubeProxyConfigMapKey: string(cfgFiles["Kube-proxy_componentconfig"]),
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
name: "invalid - No kube-proxy component config ConfigMap",
|
||||
configMaps: []fakeConfigMap{
|
||||
{
|
||||
name: kubeadmconstants.GetKubeletConfigMapName(k8sVersion),
|
||||
data: map[string]string{
|
||||
kubeadmconstants.KubeletBaseConfigurationConfigMapKey: string(cfgFiles["Kubelet_componentconfig"]),
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, rt := range tests {
|
||||
t.Run(rt.name, func(t *testing.T) {
|
||||
client := clientsetfake.NewSimpleClientset()
|
||||
|
||||
for _, c := range rt.configMaps {
|
||||
err := c.create(client)
|
||||
if err != nil {
|
||||
t.Errorf("couldn't create ConfigMap %s", c.name)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
cfg := &kubeadmapi.InitConfiguration{
|
||||
ClusterConfiguration: kubeadmapi.ClusterConfiguration{
|
||||
KubernetesVersion: k8sVersionString,
|
||||
},
|
||||
}
|
||||
err := getComponentConfigs(client, &cfg.ClusterConfiguration)
|
||||
if rt.expectedError != (err != nil) {
|
||||
t.Errorf("unexpected return err from getInitConfigurationFromCluster: %v", err)
|
||||
return
|
||||
}
|
||||
if rt.expectedError {
|
||||
return
|
||||
}
|
||||
|
||||
// Test expected values in InitConfiguration
|
||||
if cfg.ComponentConfigs.Kubelet == nil {
|
||||
t.Errorf("invalid cfg.ComponentConfigs.Kubelet")
|
||||
}
|
||||
if cfg.ComponentConfigs.KubeProxy == nil {
|
||||
t.Errorf("invalid cfg.ComponentConfigs.KubeProxy")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetInitConfigurationFromCluster(t *testing.T) {
|
||||
tmpdir, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't create tmpdir")
|
||||
}
|
||||
defer os.RemoveAll(tmpdir)
|
||||
|
||||
var tests = []struct {
|
||||
name string
|
||||
fileContents []byte
|
||||
node *v1.Node
|
||||
configMaps []fakeConfigMap
|
||||
newControlPlane bool
|
||||
expectedError bool
|
||||
}{
|
||||
{
|
||||
name: "invalid - No kubeadm-config ConfigMap",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
name: "invalid - No ClusterConfiguration in kubeadm-config ConfigMap",
|
||||
configMaps: []fakeConfigMap{
|
||||
{
|
||||
name: kubeadmconstants.KubeadmConfigConfigMap, // ClusterConfiguration from kubeadm-config.
|
||||
data: map[string]string{},
|
||||
},
|
||||
},
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
name: "valid v1beta1 - new control plane == false", // InitConfiguration composed with data from different places, with also node specific information from ClusterStatus and node
|
||||
configMaps: []fakeConfigMap{
|
||||
{
|
||||
name: kubeadmconstants.KubeadmConfigConfigMap, // ClusterConfiguration from kubeadm-config.
|
||||
data: map[string]string{
|
||||
kubeadmconstants.ClusterConfigurationConfigMapKey: string(cfgFiles["ClusterConfiguration_v1beta1"]),
|
||||
kubeadmconstants.ClusterStatusConfigMapKey: string(cfgFiles["ClusterStatus_v1beta1"]),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: kubeadmconstants.KubeProxyConfigMap, // Kube-proxy component config from corresponding ConfigMap.
|
||||
data: map[string]string{
|
||||
kubeadmconstants.KubeProxyConfigMapKey: string(cfgFiles["Kube-proxy_componentconfig"]),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: kubeadmconstants.GetKubeletConfigMapName(k8sVersion), // Kubelet component config from corresponding ConfigMap.
|
||||
data: map[string]string{
|
||||
kubeadmconstants.KubeletBaseConfigurationConfigMapKey: string(cfgFiles["Kubelet_componentconfig"]),
|
||||
},
|
||||
},
|
||||
},
|
||||
fileContents: kubeletConfFiles["configWithEmbeddedCert"],
|
||||
node: &v1.Node{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: nodeName,
|
||||
Annotations: map[string]string{
|
||||
kubeadmconstants.AnnotationKubeadmCRISocket: "myCRIsocket",
|
||||
},
|
||||
},
|
||||
Spec: v1.NodeSpec{
|
||||
Taints: []v1.Taint{kubeadmconstants.MasterTaint},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid v1beta1 - new control plane == true", // InitConfiguration composed with data from different places, without node specific information
|
||||
configMaps: []fakeConfigMap{
|
||||
{
|
||||
name: kubeadmconstants.KubeadmConfigConfigMap, // ClusterConfiguration from kubeadm-config.
|
||||
data: map[string]string{
|
||||
kubeadmconstants.ClusterConfigurationConfigMapKey: string(cfgFiles["ClusterConfiguration_v1beta1"]),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: kubeadmconstants.KubeProxyConfigMap, // Kube-proxy component config from corresponding ConfigMap.
|
||||
data: map[string]string{
|
||||
kubeadmconstants.KubeProxyConfigMapKey: string(cfgFiles["Kube-proxy_componentconfig"]),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: kubeadmconstants.GetKubeletConfigMapName(k8sVersion), // Kubelet component config from corresponding ConfigMap.
|
||||
data: map[string]string{
|
||||
kubeadmconstants.KubeletBaseConfigurationConfigMapKey: string(cfgFiles["Kubelet_componentconfig"]),
|
||||
},
|
||||
},
|
||||
},
|
||||
newControlPlane: true,
|
||||
},
|
||||
{
|
||||
name: "valid v1alpha3 - new control plane == false", // InitConfiguration composed with data from different places, with also node specific information from ClusterStatus and node
|
||||
configMaps: []fakeConfigMap{
|
||||
{
|
||||
name: kubeadmconstants.KubeadmConfigConfigMap, // ClusterConfiguration from kubeadm-config.
|
||||
data: map[string]string{
|
||||
kubeadmconstants.ClusterConfigurationConfigMapKey: string(cfgFiles["ClusterConfiguration_v1alpha3"]),
|
||||
kubeadmconstants.ClusterStatusConfigMapKey: string(cfgFiles["ClusterStatus_v1alpha3"]),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: kubeadmconstants.KubeProxyConfigMap, // Kube-proxy component config from corresponding ConfigMap.
|
||||
data: map[string]string{
|
||||
kubeadmconstants.KubeProxyConfigMapKey: string(cfgFiles["Kube-proxy_componentconfig"]),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: kubeadmconstants.GetKubeletConfigMapName(k8sVersion), // Kubelet component config from corresponding ConfigMap.
|
||||
data: map[string]string{
|
||||
kubeadmconstants.KubeletBaseConfigurationConfigMapKey: string(cfgFiles["Kubelet_componentconfig"]),
|
||||
},
|
||||
},
|
||||
},
|
||||
fileContents: kubeletConfFiles["configWithEmbeddedCert"],
|
||||
node: &v1.Node{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: nodeName,
|
||||
Annotations: map[string]string{
|
||||
kubeadmconstants.AnnotationKubeadmCRISocket: "myCRIsocket",
|
||||
},
|
||||
},
|
||||
Spec: v1.NodeSpec{
|
||||
Taints: []v1.Taint{kubeadmconstants.MasterTaint},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid v1alpha3 - new control plane == true", // InitConfiguration composed with data from different places, without node specific information
|
||||
configMaps: []fakeConfigMap{
|
||||
{
|
||||
name: kubeadmconstants.KubeadmConfigConfigMap, // ClusterConfiguration from kubeadm-config.
|
||||
data: map[string]string{
|
||||
kubeadmconstants.ClusterConfigurationConfigMapKey: string(cfgFiles["ClusterConfiguration_v1alpha3"]),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: kubeadmconstants.KubeProxyConfigMap, // Kube-proxy component config from corresponding ConfigMap.
|
||||
data: map[string]string{
|
||||
kubeadmconstants.KubeProxyConfigMapKey: string(cfgFiles["Kube-proxy_componentconfig"]),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: kubeadmconstants.GetKubeletConfigMapName(k8sVersion), // Kubelet component config from corresponding ConfigMap.
|
||||
data: map[string]string{
|
||||
kubeadmconstants.KubeletBaseConfigurationConfigMapKey: string(cfgFiles["Kubelet_componentconfig"]),
|
||||
},
|
||||
},
|
||||
},
|
||||
newControlPlane: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, rt := range tests {
|
||||
t.Run(rt.name, func(t *testing.T) {
|
||||
cfgPath := filepath.Join(tmpdir, kubeadmconstants.KubeletKubeConfigFileName)
|
||||
if len(rt.fileContents) > 0 {
|
||||
err := ioutil.WriteFile(cfgPath, rt.fileContents, 0644)
|
||||
if err != nil {
|
||||
t.Errorf("Couldn't create file")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
client := clientsetfake.NewSimpleClientset()
|
||||
|
||||
if rt.node != nil {
|
||||
_, err := client.CoreV1().Nodes().Create(rt.node)
|
||||
if err != nil {
|
||||
t.Errorf("couldn't create Node")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for _, c := range rt.configMaps {
|
||||
err := c.create(client)
|
||||
if err != nil {
|
||||
t.Errorf("couldn't create ConfigMap %s", c.name)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
cfg, err := getInitConfigurationFromCluster(tmpdir, client, rt.newControlPlane)
|
||||
if rt.expectedError != (err != nil) {
|
||||
t.Errorf("unexpected return err from getInitConfigurationFromCluster: %v", err)
|
||||
return
|
||||
}
|
||||
if rt.expectedError {
|
||||
return
|
||||
}
|
||||
|
||||
// Test expected values in InitConfiguration
|
||||
if cfg == nil {
|
||||
t.Errorf("unexpected nil return value")
|
||||
}
|
||||
if cfg.ClusterConfiguration.KubernetesVersion != k8sVersionString {
|
||||
t.Errorf("invalid ClusterConfiguration.KubernetesVersion")
|
||||
}
|
||||
if !rt.newControlPlane && (cfg.LocalAPIEndpoint.AdvertiseAddress != "1.2.3.4" || cfg.LocalAPIEndpoint.BindPort != 1234) {
|
||||
t.Errorf("invalid cfg.LocalAPIEndpoint")
|
||||
}
|
||||
if cfg.ComponentConfigs.Kubelet == nil {
|
||||
t.Errorf("invalid cfg.ComponentConfigs.Kubelet")
|
||||
}
|
||||
if cfg.ComponentConfigs.KubeProxy == nil {
|
||||
t.Errorf("invalid cfg.ComponentConfigs.KubeProxy")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetGetClusterStatus(t *testing.T) {
|
||||
var tests = []struct {
|
||||
name string
|
||||
configMaps []fakeConfigMap
|
||||
expectedEndpoints int
|
||||
expectedError bool
|
||||
}{
|
||||
{
|
||||
name: "invalid missing config map",
|
||||
expectedEndpoints: 0,
|
||||
},
|
||||
{
|
||||
name: "valid v1beta1",
|
||||
configMaps: []fakeConfigMap{
|
||||
{
|
||||
name: kubeadmconstants.KubeadmConfigConfigMap,
|
||||
data: map[string]string{
|
||||
kubeadmconstants.ClusterStatusConfigMapKey: string(cfgFiles["ClusterStatus_v1beta1"]),
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedEndpoints: 1,
|
||||
},
|
||||
{
|
||||
name: "valid v1alpha3",
|
||||
configMaps: []fakeConfigMap{
|
||||
{
|
||||
name: kubeadmconstants.KubeadmConfigConfigMap,
|
||||
data: map[string]string{
|
||||
kubeadmconstants.ClusterStatusConfigMapKey: string(cfgFiles["ClusterStatus_v1alpha3"]),
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedEndpoints: 1,
|
||||
},
|
||||
{
|
||||
name: "invalid missing ClusterStatusConfigMapKey in the config map",
|
||||
configMaps: []fakeConfigMap{
|
||||
{
|
||||
name: kubeadmconstants.KubeadmConfigConfigMap,
|
||||
data: map[string]string{},
|
||||
},
|
||||
},
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
name: "invalid wrong value in the config map",
|
||||
configMaps: []fakeConfigMap{
|
||||
{
|
||||
name: kubeadmconstants.KubeadmConfigConfigMap,
|
||||
data: map[string]string{
|
||||
kubeadmconstants.ClusterStatusConfigMapKey: "not a kubeadm type",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, rt := range tests {
|
||||
t.Run(rt.name, func(t *testing.T) {
|
||||
client := clientsetfake.NewSimpleClientset()
|
||||
|
||||
for _, c := range rt.configMaps {
|
||||
err := c.create(client)
|
||||
if err != nil {
|
||||
t.Errorf("couldn't create ConfigMap %s", c.name)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
clusterStatus, err := GetClusterStatus(client)
|
||||
if rt.expectedError != (err != nil) {
|
||||
t.Errorf("unexpected return err from GetClusterStatus: %v", err)
|
||||
return
|
||||
}
|
||||
if rt.expectedError {
|
||||
return
|
||||
}
|
||||
|
||||
// Test expected values in clusterStatus
|
||||
if len(clusterStatus.APIEndpoints) != rt.expectedEndpoints {
|
||||
t.Errorf("unexpected ClusterStatus return value")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type fakeConfigMap struct {
|
||||
name string
|
||||
data map[string]string
|
||||
}
|
||||
|
||||
func (c *fakeConfigMap) create(client clientset.Interface) error {
|
||||
return apiclient.CreateOrUpdateConfigMap(client, &v1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: c.name,
|
||||
Namespace: metav1.NamespaceSystem,
|
||||
},
|
||||
Data: c.data,
|
||||
})
|
||||
}
|
205
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/common.go
generated
vendored
Normal file
205
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/common.go
generated
vendored
Normal file
@ -0,0 +1,205 @@
|
||||
/*
|
||||
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"
|
||||
"net"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"k8s.io/klog"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
netutil "k8s.io/apimachinery/pkg/util/net"
|
||||
"k8s.io/apimachinery/pkg/util/version"
|
||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||
kubeadmscheme "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/scheme"
|
||||
kubeadmapiv1beta1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta1"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
||||
)
|
||||
|
||||
// MarshalKubeadmConfigObject marshals an Object registered in the kubeadm scheme. If the object is a InitConfiguration or ClusterConfiguration, some extra logic is run
|
||||
func MarshalKubeadmConfigObject(obj runtime.Object) ([]byte, error) {
|
||||
switch internalcfg := obj.(type) {
|
||||
case *kubeadmapi.InitConfiguration:
|
||||
return MarshalInitConfigurationToBytes(internalcfg, kubeadmapiv1beta1.SchemeGroupVersion)
|
||||
case *kubeadmapi.ClusterConfiguration:
|
||||
return MarshalClusterConfigurationToBytes(internalcfg, kubeadmapiv1beta1.SchemeGroupVersion)
|
||||
default:
|
||||
return kubeadmutil.MarshalToYamlForCodecs(obj, kubeadmapiv1beta1.SchemeGroupVersion, kubeadmscheme.Codecs)
|
||||
}
|
||||
}
|
||||
|
||||
// DetectUnsupportedVersion reads YAML bytes, extracts the TypeMeta information and errors out with an user-friendly message if the API spec is too old for this kubeadm version
|
||||
func DetectUnsupportedVersion(b []byte) error {
|
||||
gvks, err := kubeadmutil.GroupVersionKindsFromBytes(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: On our way to making the kubeadm API beta and higher, give good user output in case they use an old config file with a new kubeadm version, and
|
||||
// tell them how to upgrade. The support matrix will look something like this now and in the future:
|
||||
// v1.10 and earlier: v1alpha1
|
||||
// v1.11: v1alpha1 read-only, writes only v1alpha2 config
|
||||
// v1.12: v1alpha2 read-only, writes only v1alpha3 config. Warns if the user tries to use v1alpha1
|
||||
// v1.13: v1alpha3 read-only, writes only v1beta1 config. Warns if the user tries to use v1alpha1 or v1alpha2
|
||||
oldKnownAPIVersions := map[string]string{
|
||||
"kubeadm.k8s.io/v1alpha1": "v1.11",
|
||||
"kubeadm.k8s.io/v1alpha2": "v1.12",
|
||||
}
|
||||
// If we find an old API version in this gvk list, error out and tell the user why this doesn't work
|
||||
knownKinds := map[string]bool{}
|
||||
for _, gvk := range gvks {
|
||||
if useKubeadmVersion := oldKnownAPIVersions[gvk.GroupVersion().String()]; len(useKubeadmVersion) != 0 {
|
||||
return errors.Errorf("your configuration file uses an old API spec: %q. Please use kubeadm %s instead and run 'kubeadm config migrate --old-config old.yaml --new-config new.yaml', which will write the new, similar spec using a newer API version.", gvk.GroupVersion().String(), useKubeadmVersion)
|
||||
}
|
||||
knownKinds[gvk.Kind] = true
|
||||
}
|
||||
|
||||
// InitConfiguration and JoinConfiguration may not apply together, warn if more than one is specified
|
||||
mutuallyExclusive := []string{constants.InitConfigurationKind, constants.JoinConfigurationKind}
|
||||
mutuallyExclusiveCount := 0
|
||||
for _, kind := range mutuallyExclusive {
|
||||
if knownKinds[kind] {
|
||||
mutuallyExclusiveCount++
|
||||
}
|
||||
}
|
||||
if mutuallyExclusiveCount > 1 {
|
||||
klog.Warningf("WARNING: Detected resource kinds that may not apply: %v", mutuallyExclusive)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NormalizeKubernetesVersion resolves version labels, sets alternative
|
||||
// image registry if requested for CI builds, and validates minimal
|
||||
// version that kubeadm SetInitDynamicDefaultssupports.
|
||||
func NormalizeKubernetesVersion(cfg *kubeadmapi.ClusterConfiguration) error {
|
||||
// Requested version is automatic CI build, thus use KubernetesCI Image Repository for core images
|
||||
if kubeadmutil.KubernetesIsCIVersion(cfg.KubernetesVersion) {
|
||||
cfg.CIImageRepository = constants.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 errors.Wrapf(err, "couldn't parse Kubernetes version %q", cfg.KubernetesVersion)
|
||||
}
|
||||
if k8sVersion.LessThan(constants.MinimumControlPlaneVersion) {
|
||||
return errors.Errorf("this version of kubeadm only supports deploying clusters with the control plane version >= %s. Current version: %s", constants.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 {
|
||||
klog.V(1).Infof("lowercasing SAN %q to %q", san, lowercase)
|
||||
sans[i] = lowercase
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// VerifyAPIServerBindAddress can be used to verify if a bind address for the API Server is 0.0.0.0,
|
||||
// in which case this address is not valid and should not be used.
|
||||
func VerifyAPIServerBindAddress(address string) error {
|
||||
ip := net.ParseIP(address)
|
||||
if ip == nil {
|
||||
return errors.Errorf("cannot parse IP address: %s", address)
|
||||
}
|
||||
if !ip.IsGlobalUnicast() {
|
||||
return errors.Errorf("cannot use %q as the bind address for the API Server", address)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ChooseAPIServerBindAddress is a wrapper for netutil.ChooseBindAddress that also handles
|
||||
// the case where no default routes were found and an IP for the API server could not be obatained.
|
||||
func ChooseAPIServerBindAddress(bindAddress net.IP) (net.IP, error) {
|
||||
ip, err := netutil.ChooseBindAddress(bindAddress)
|
||||
if err != nil {
|
||||
if netutil.IsNoRoutesError(err) {
|
||||
klog.Warningf("WARNING: could not obtain a bind address for the API Server: %v; using: %s", err, constants.DefaultAPIServerBindAddress)
|
||||
defaultIP := net.ParseIP(constants.DefaultAPIServerBindAddress)
|
||||
if defaultIP == nil {
|
||||
return nil, errors.Errorf("cannot parse default IP address: %s", constants.DefaultAPIServerBindAddress)
|
||||
}
|
||||
return defaultIP, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
if bindAddress != nil && !bindAddress.IsUnspecified() && !reflect.DeepEqual(ip, bindAddress) {
|
||||
klog.Warningf("WARNING: overriding requested API server bind address: requested %q, actual %q", bindAddress, ip)
|
||||
}
|
||||
return ip, nil
|
||||
}
|
||||
|
||||
// MigrateOldConfigFromFile migrates an old configuration from a file into a new one (returned as a byte slice). Only kubeadm kinds are migrated. Others are silently ignored.
|
||||
func MigrateOldConfigFromFile(cfgPath string) ([]byte, error) {
|
||||
newConfig := [][]byte{}
|
||||
|
||||
cfgBytes, err := ioutil.ReadFile(cfgPath)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
|
||||
gvks, err := kubeadmutil.GroupVersionKindsFromBytes(cfgBytes)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
|
||||
// Migrate InitConfiguration and ClusterConfiguration if there are any in the config
|
||||
if kubeadmutil.GroupVersionKindsHasInitConfiguration(gvks...) || kubeadmutil.GroupVersionKindsHasClusterConfiguration(gvks...) {
|
||||
o, err := ConfigFileAndDefaultsToInternalConfig(cfgPath, &kubeadmapiv1beta1.InitConfiguration{})
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
b, err := MarshalKubeadmConfigObject(o)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
newConfig = append(newConfig, b)
|
||||
}
|
||||
|
||||
// Migrate JoinConfiguration if there is any
|
||||
if kubeadmutil.GroupVersionKindsHasJoinConfiguration(gvks...) {
|
||||
o, err := JoinConfigFileAndDefaultsToInternalConfig(cfgPath, &kubeadmapiv1beta1.JoinConfiguration{})
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
b, err := MarshalKubeadmConfigObject(o)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
newConfig = append(newConfig, b)
|
||||
}
|
||||
|
||||
return bytes.Join(newConfig, []byte(constants.YAMLDocumentSeparator)), nil
|
||||
}
|
514
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/common_test.go
generated
vendored
Normal file
514
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/common_test.go
generated
vendored
Normal file
@ -0,0 +1,514 @@
|
||||
/*
|
||||
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"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/renstrom/dedent"
|
||||
|
||||
kubeadmapiv1beta1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta1"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
||||
)
|
||||
|
||||
var files = map[string][]byte{
|
||||
"Master_v1alpha1": []byte(`
|
||||
apiVersion: kubeadm.k8s.io/v1alpha1
|
||||
kind: InitConfiguration
|
||||
`),
|
||||
"Node_v1alpha1": []byte(`
|
||||
apiVersion: kubeadm.k8s.io/v1alpha1
|
||||
kind: NodeConfiguration
|
||||
`),
|
||||
"Master_v1alpha2": []byte(`
|
||||
apiVersion: kubeadm.k8s.io/v1alpha2
|
||||
kind: MasterConfiguration
|
||||
`),
|
||||
"Node_v1alpha2": []byte(`
|
||||
apiVersion: kubeadm.k8s.io/v1alpha2
|
||||
kind: NodeConfiguration
|
||||
`),
|
||||
"Init_v1alpha3": []byte(`
|
||||
apiVersion: kubeadm.k8s.io/v1alpha3
|
||||
kind: InitConfiguration
|
||||
`),
|
||||
"Join_v1alpha3": []byte(`
|
||||
apiVersion: kubeadm.k8s.io/v1alpha3
|
||||
kind: JoinConfiguration
|
||||
`),
|
||||
"Init_v1beta1": []byte(`
|
||||
apiVersion: kubeadm.k8s.io/v1beta1
|
||||
kind: InitConfiguration
|
||||
`),
|
||||
"Join_v1beta1": []byte(`
|
||||
apiVersion: kubeadm.k8s.io/v1beta1
|
||||
kind: JoinConfiguration
|
||||
`),
|
||||
"NoKind": []byte(`
|
||||
apiVersion: baz.k8s.io/v1
|
||||
foo: foo
|
||||
bar: bar
|
||||
`),
|
||||
"NoAPIVersion": []byte(`
|
||||
kind: Bar
|
||||
foo: foo
|
||||
bar: bar
|
||||
`),
|
||||
"Foo": []byte(`
|
||||
apiVersion: foo.k8s.io/v1
|
||||
kind: Foo
|
||||
`),
|
||||
}
|
||||
|
||||
func TestDetectUnsupportedVersion(t *testing.T) {
|
||||
var tests = []struct {
|
||||
name string
|
||||
fileContents []byte
|
||||
expectedErr bool
|
||||
}{
|
||||
{
|
||||
name: "Master_v1alpha1",
|
||||
fileContents: files["Master_v1alpha1"],
|
||||
expectedErr: true,
|
||||
},
|
||||
{
|
||||
name: "Node_v1alpha1",
|
||||
fileContents: files["Node_v1alpha1"],
|
||||
expectedErr: true,
|
||||
},
|
||||
{
|
||||
name: "Master_v1alpha2",
|
||||
fileContents: files["Master_v1alpha2"],
|
||||
expectedErr: true,
|
||||
},
|
||||
{
|
||||
name: "Node_v1alpha2",
|
||||
fileContents: files["Node_v1alpha2"],
|
||||
expectedErr: true,
|
||||
},
|
||||
{
|
||||
name: "Init_v1alpha3",
|
||||
fileContents: files["Init_v1alpha3"],
|
||||
},
|
||||
{
|
||||
name: "Join_v1alpha3",
|
||||
fileContents: files["Join_v1alpha3"],
|
||||
},
|
||||
{
|
||||
name: "Init_v1beta1",
|
||||
fileContents: files["Init_v1beta1"],
|
||||
},
|
||||
{
|
||||
name: "Join_v1beta1",
|
||||
fileContents: files["Join_v1beta1"],
|
||||
},
|
||||
{
|
||||
name: "DuplicateInit v1alpha3",
|
||||
fileContents: bytes.Join([][]byte{files["Init_v1alpha3"], files["Init_v1alpha3"]}, []byte(constants.YAMLDocumentSeparator)),
|
||||
expectedErr: true,
|
||||
},
|
||||
{
|
||||
name: "DuplicateInit v1beta1",
|
||||
fileContents: bytes.Join([][]byte{files["Init_v1beta1"], files["Init_v1beta1"]}, []byte(constants.YAMLDocumentSeparator)),
|
||||
expectedErr: true,
|
||||
},
|
||||
{
|
||||
name: "DuplicateInit v1beta1 and v1alpha3",
|
||||
fileContents: bytes.Join([][]byte{files["Init_v1beta1"], files["Init_v1alpha3"]}, []byte(constants.YAMLDocumentSeparator)),
|
||||
expectedErr: true,
|
||||
},
|
||||
{
|
||||
name: "DuplicateJoin v1alpha3",
|
||||
fileContents: bytes.Join([][]byte{files["Join_v1alpha3"], files["Join_v1alpha3"]}, []byte(constants.YAMLDocumentSeparator)),
|
||||
expectedErr: true,
|
||||
},
|
||||
{
|
||||
name: "DuplicateJoin v1beta1",
|
||||
fileContents: bytes.Join([][]byte{files["Join_v1beta1"], files["Join_v1beta1"]}, []byte(constants.YAMLDocumentSeparator)),
|
||||
expectedErr: true,
|
||||
},
|
||||
{
|
||||
name: "DuplicateJoin v1beta1 and v1alpha3",
|
||||
fileContents: bytes.Join([][]byte{files["Join_v1beta1"], files["Join_v1alpha3"]}, []byte(constants.YAMLDocumentSeparator)),
|
||||
expectedErr: true,
|
||||
},
|
||||
{
|
||||
name: "NoKind",
|
||||
fileContents: files["NoKind"],
|
||||
expectedErr: true,
|
||||
},
|
||||
{
|
||||
name: "NoAPIVersion",
|
||||
fileContents: files["NoAPIVersion"],
|
||||
expectedErr: true,
|
||||
},
|
||||
{
|
||||
name: "Ignore other Kind",
|
||||
fileContents: bytes.Join([][]byte{files["Foo"], files["Master_v1alpha3"]}, []byte(constants.YAMLDocumentSeparator)),
|
||||
},
|
||||
{
|
||||
name: "Ignore other Kind",
|
||||
fileContents: bytes.Join([][]byte{files["Foo"], files["Master_v1beta1"]}, []byte(constants.YAMLDocumentSeparator)),
|
||||
},
|
||||
// CanMixInitJoin cases used to be MustNotMixInitJoin, however due to UX issues DetectUnsupportedVersion had to tolerate that.
|
||||
// So the following tests actually verify, that Init and Join can be mixed together with no error.
|
||||
{
|
||||
name: "CanMixInitJoin v1alpha3",
|
||||
fileContents: bytes.Join([][]byte{files["Init_v1alpha3"], files["Join_v1alpha3"]}, []byte(constants.YAMLDocumentSeparator)),
|
||||
expectedErr: false,
|
||||
},
|
||||
{
|
||||
name: "CanMixInitJoin v1alpha3 - v1beta1",
|
||||
fileContents: bytes.Join([][]byte{files["Init_v1alpha3"], files["Join_v1beta1"]}, []byte(constants.YAMLDocumentSeparator)),
|
||||
expectedErr: false,
|
||||
},
|
||||
{
|
||||
name: "CanMixInitJoin v1beta1 - v1alpha3",
|
||||
fileContents: bytes.Join([][]byte{files["Init_v1beta1"], files["Join_v1alpha3"]}, []byte(constants.YAMLDocumentSeparator)),
|
||||
expectedErr: false,
|
||||
},
|
||||
{
|
||||
name: "CanMixInitJoin v1beta1",
|
||||
fileContents: bytes.Join([][]byte{files["Init_v1beta1"], files["Join_v1beta1"]}, []byte(constants.YAMLDocumentSeparator)),
|
||||
expectedErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, rt := range tests {
|
||||
t.Run(rt.name, func(t2 *testing.T) {
|
||||
|
||||
err := DetectUnsupportedVersion(rt.fileContents)
|
||||
if (err != nil) != rt.expectedErr {
|
||||
t2.Errorf("expected error: %t, actual: %t", rt.expectedErr, err != nil)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
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 := &kubeadmapiv1beta1.InitConfiguration{
|
||||
ClusterConfiguration: kubeadmapiv1beta1.ClusterConfiguration{
|
||||
APIServer: kubeadmapiv1beta1.APIServer{
|
||||
CertSANs: test.in,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
LowercaseSANs(cfg.APIServer.CertSANs)
|
||||
|
||||
if len(cfg.APIServer.CertSANs) != len(test.out) {
|
||||
t.Fatalf("expected %d elements, got %d", len(test.out), len(cfg.APIServer.CertSANs))
|
||||
}
|
||||
|
||||
for i, expected := range test.out {
|
||||
if cfg.APIServer.CertSANs[i] != expected {
|
||||
t.Errorf("expected element %d to be %q, got %q", i, expected, cfg.APIServer.CertSANs[i])
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifyAPIServerBindAddress(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
address string
|
||||
expectedError bool
|
||||
}{
|
||||
{
|
||||
name: "valid address: IPV4",
|
||||
address: "192.168.0.1",
|
||||
},
|
||||
{
|
||||
name: "valid address: IPV6",
|
||||
address: "2001:db8:85a3::8a2e:370:7334",
|
||||
},
|
||||
{
|
||||
name: "invalid address: not a global unicast 0.0.0.0",
|
||||
address: "0.0.0.0",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
name: "invalid address: not a global unicast 127.0.0.1",
|
||||
address: "127.0.0.1",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
name: "invalid address: not a global unicast ::",
|
||||
address: "::",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
name: "invalid address: cannot parse IPV4",
|
||||
address: "255.255.255.255.255",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
name: "invalid address: cannot parse IPV6",
|
||||
address: "2a00:800::2a00:800:10102a00",
|
||||
expectedError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
if err := VerifyAPIServerBindAddress(test.address); (err != nil) != test.expectedError {
|
||||
t.Errorf("expected error: %v, got %v, error: %v", test.expectedError, (err != nil), err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMigrateOldConfigFromFile(t *testing.T) {
|
||||
tests := []struct {
|
||||
desc string
|
||||
oldCfg string
|
||||
expectedKinds []string
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
desc: "empty file produces empty result",
|
||||
oldCfg: "",
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
desc: "bad config produces error",
|
||||
oldCfg: dedent.Dedent(`
|
||||
apiVersion: kubeadm.k8s.io/v1alpha3
|
||||
`),
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
desc: "InitConfiguration only gets migrated",
|
||||
oldCfg: dedent.Dedent(`
|
||||
apiVersion: kubeadm.k8s.io/v1alpha3
|
||||
kind: InitConfiguration
|
||||
`),
|
||||
expectedKinds: []string{
|
||||
constants.InitConfigurationKind,
|
||||
constants.ClusterConfigurationKind,
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
desc: "ClusterConfiguration only gets migrated",
|
||||
oldCfg: dedent.Dedent(`
|
||||
apiVersion: kubeadm.k8s.io/v1alpha3
|
||||
kind: ClusterConfiguration
|
||||
`),
|
||||
expectedKinds: []string{
|
||||
constants.InitConfigurationKind,
|
||||
constants.ClusterConfigurationKind,
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
desc: "JoinConfiguration only gets migrated",
|
||||
oldCfg: dedent.Dedent(`
|
||||
apiVersion: kubeadm.k8s.io/v1alpha3
|
||||
kind: JoinConfiguration
|
||||
token: abcdef.0123456789abcdef
|
||||
discoveryTokenAPIServers:
|
||||
- kube-apiserver:6443
|
||||
discoveryTokenUnsafeSkipCAVerification: true
|
||||
`),
|
||||
expectedKinds: []string{
|
||||
constants.JoinConfigurationKind,
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
desc: "Init + Cluster Configurations are migrated",
|
||||
oldCfg: dedent.Dedent(`
|
||||
apiVersion: kubeadm.k8s.io/v1alpha3
|
||||
kind: InitConfiguration
|
||||
---
|
||||
apiVersion: kubeadm.k8s.io/v1alpha3
|
||||
kind: ClusterConfiguration
|
||||
`),
|
||||
expectedKinds: []string{
|
||||
constants.InitConfigurationKind,
|
||||
constants.ClusterConfigurationKind,
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
desc: "Init + Join Configurations are migrated",
|
||||
oldCfg: dedent.Dedent(`
|
||||
apiVersion: kubeadm.k8s.io/v1alpha3
|
||||
kind: InitConfiguration
|
||||
---
|
||||
apiVersion: kubeadm.k8s.io/v1alpha3
|
||||
kind: JoinConfiguration
|
||||
token: abcdef.0123456789abcdef
|
||||
discoveryTokenAPIServers:
|
||||
- kube-apiserver:6443
|
||||
discoveryTokenUnsafeSkipCAVerification: true
|
||||
`),
|
||||
expectedKinds: []string{
|
||||
constants.InitConfigurationKind,
|
||||
constants.ClusterConfigurationKind,
|
||||
constants.JoinConfigurationKind,
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
desc: "Cluster + Join Configurations are migrated",
|
||||
oldCfg: dedent.Dedent(`
|
||||
apiVersion: kubeadm.k8s.io/v1alpha3
|
||||
kind: ClusterConfiguration
|
||||
---
|
||||
apiVersion: kubeadm.k8s.io/v1alpha3
|
||||
kind: JoinConfiguration
|
||||
token: abcdef.0123456789abcdef
|
||||
discoveryTokenAPIServers:
|
||||
- kube-apiserver:6443
|
||||
discoveryTokenUnsafeSkipCAVerification: true
|
||||
`),
|
||||
expectedKinds: []string{
|
||||
constants.InitConfigurationKind,
|
||||
constants.ClusterConfigurationKind,
|
||||
constants.JoinConfigurationKind,
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
desc: "Init + Cluster + Join Configurations are migrated",
|
||||
oldCfg: dedent.Dedent(`
|
||||
apiVersion: kubeadm.k8s.io/v1alpha3
|
||||
kind: InitConfiguration
|
||||
---
|
||||
apiVersion: kubeadm.k8s.io/v1alpha3
|
||||
kind: ClusterConfiguration
|
||||
---
|
||||
apiVersion: kubeadm.k8s.io/v1alpha3
|
||||
kind: JoinConfiguration
|
||||
token: abcdef.0123456789abcdef
|
||||
discoveryTokenAPIServers:
|
||||
- kube-apiserver:6443
|
||||
discoveryTokenUnsafeSkipCAVerification: true
|
||||
`),
|
||||
expectedKinds: []string{
|
||||
constants.InitConfigurationKind,
|
||||
constants.ClusterConfigurationKind,
|
||||
constants.JoinConfigurationKind,
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
desc: "component configs are not migrated",
|
||||
oldCfg: dedent.Dedent(`
|
||||
apiVersion: kubeadm.k8s.io/v1alpha3
|
||||
kind: InitConfiguration
|
||||
---
|
||||
apiVersion: kubeadm.k8s.io/v1alpha3
|
||||
kind: ClusterConfiguration
|
||||
---
|
||||
apiVersion: kubeadm.k8s.io/v1alpha3
|
||||
kind: JoinConfiguration
|
||||
token: abcdef.0123456789abcdef
|
||||
discoveryTokenAPIServers:
|
||||
- kube-apiserver:6443
|
||||
discoveryTokenUnsafeSkipCAVerification: true
|
||||
---
|
||||
apiVersion: kubeproxy.config.k8s.io/v1alpha1
|
||||
kind: KubeProxyConfiguration
|
||||
---
|
||||
apiVersion: kubelet.config.k8s.io/v1beta1
|
||||
kind: KubeletConfiguration
|
||||
`),
|
||||
expectedKinds: []string{
|
||||
constants.InitConfigurationKind,
|
||||
constants.ClusterConfigurationKind,
|
||||
constants.JoinConfigurationKind,
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
file, err := ioutil.TempFile("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("could not create temporary test file: %v", err)
|
||||
}
|
||||
fileName := file.Name()
|
||||
defer os.Remove(fileName)
|
||||
|
||||
_, err = file.WriteString(test.oldCfg)
|
||||
file.Close()
|
||||
if err != nil {
|
||||
t.Fatalf("could not write contents of old config: %v", err)
|
||||
}
|
||||
|
||||
b, err := MigrateOldConfigFromFile(fileName)
|
||||
if test.expectErr {
|
||||
if err == nil {
|
||||
t.Fatalf("unexpected success:\n%s", b)
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected failure: %v", err)
|
||||
}
|
||||
gvks, err := kubeadmutil.GroupVersionKindsFromBytes(b)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error returned by GroupVersionKindsFromBytes: %v", err)
|
||||
}
|
||||
if len(gvks) != len(test.expectedKinds) {
|
||||
t.Fatalf("length mismatch between resulting gvks and expected kinds:\n\tlen(gvks)=%d\n\tlen(expectedKinds)=%d",
|
||||
len(gvks), len(test.expectedKinds))
|
||||
}
|
||||
for _, expectedKind := range test.expectedKinds {
|
||||
if !kubeadmutil.GroupVersionKindsHasKind(gvks, expectedKind) {
|
||||
t.Fatalf("migration failed to produce config kind: %s", expectedKind)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
376
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/initconfiguration.go
generated
vendored
Normal file
376
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/initconfiguration.go
generated
vendored
Normal file
@ -0,0 +1,376 @@
|
||||
/*
|
||||
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 (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"k8s.io/klog"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
bootstraputil "k8s.io/cluster-bootstrap/token/util"
|
||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||
kubeadmscheme "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/scheme"
|
||||
kubeadmapiv1beta1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta1"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/componentconfigs"
|
||||
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/util/config/strict"
|
||||
nodeutil "k8s.io/kubernetes/pkg/util/node"
|
||||
)
|
||||
|
||||
// SetInitDynamicDefaults checks and sets configuration values for the InitConfiguration object
|
||||
func SetInitDynamicDefaults(cfg *kubeadmapi.InitConfiguration) error {
|
||||
if err := SetBootstrapTokensDynamicDefaults(&cfg.BootstrapTokens); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := SetNodeRegistrationDynamicDefaults(&cfg.NodeRegistration, true); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := SetAPIEndpointDynamicDefaults(&cfg.LocalAPIEndpoint); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := SetClusterDynamicDefaults(&cfg.ClusterConfiguration, cfg.LocalAPIEndpoint.AdvertiseAddress, cfg.LocalAPIEndpoint.BindPort); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetBootstrapTokensDynamicDefaults checks and sets configuration values for the BootstrapTokens object
|
||||
func SetBootstrapTokensDynamicDefaults(cfg *[]kubeadmapi.BootstrapToken) error {
|
||||
// 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 {
|
||||
if bt.Token != nil && len(bt.Token.String()) > 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
tokenStr, err := bootstraputil.GenerateBootstrapToken()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "couldn't generate random token")
|
||||
}
|
||||
token, err := kubeadmapi.NewBootstrapTokenString(tokenStr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
(*cfg)[i].Token = token
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetNodeRegistrationDynamicDefaults checks and sets configuration values for the NodeRegistration object
|
||||
func SetNodeRegistrationDynamicDefaults(cfg *kubeadmapi.NodeRegistrationOptions, masterTaint bool) error {
|
||||
var err error
|
||||
cfg.Name, err = nodeutil.GetHostname(cfg.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 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 masterTaint && cfg.Taints == nil {
|
||||
cfg.Taints = []v1.Taint{kubeadmconstants.MasterTaint}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetAPIEndpointDynamicDefaults checks and sets configuration values for the APIEndpoint object
|
||||
func SetAPIEndpointDynamicDefaults(cfg *kubeadmapi.APIEndpoint) error {
|
||||
// validate cfg.API.AdvertiseAddress.
|
||||
addressIP := net.ParseIP(cfg.AdvertiseAddress)
|
||||
if addressIP == nil && cfg.AdvertiseAddress != "" {
|
||||
return errors.Errorf("couldn't use \"%s\" as \"apiserver-advertise-address\", must be ipv4 or ipv6 address", cfg.AdvertiseAddress)
|
||||
}
|
||||
// This is the same logic as the API Server uses, except that if no interface is found the address is set to 0.0.0.0, which is invalid and cannot be used
|
||||
// for bootstrapping a cluster.
|
||||
ip, err := ChooseAPIServerBindAddress(addressIP)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cfg.AdvertiseAddress = ip.String()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetClusterDynamicDefaults checks and sets values for the ClusterConfiguration object
|
||||
func SetClusterDynamicDefaults(cfg *kubeadmapi.ClusterConfiguration, advertiseAddress string, bindPort int32) error {
|
||||
// Default all the embedded ComponentConfig structs
|
||||
componentconfigs.Known.Default(cfg)
|
||||
|
||||
ip := net.ParseIP(advertiseAddress)
|
||||
if ip.To4() != nil {
|
||||
cfg.ComponentConfigs.KubeProxy.BindAddress = kubeadmapiv1beta1.DefaultProxyBindAddressv4
|
||||
} else {
|
||||
cfg.ComponentConfigs.KubeProxy.BindAddress = kubeadmapiv1beta1.DefaultProxyBindAddressv6
|
||||
}
|
||||
|
||||
// Resolve possible version labels and validate version string
|
||||
if err := NormalizeKubernetesVersion(cfg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If ControlPlaneEndpoint is specified without a port number defaults it to
|
||||
// the bindPort number of the APIEndpoint.
|
||||
// This will allow join of additional control plane instances with different bindPort number
|
||||
if cfg.ControlPlaneEndpoint != "" {
|
||||
host, port, err := kubeadmutil.ParseHostPort(cfg.ControlPlaneEndpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if port == "" {
|
||||
cfg.ControlPlaneEndpoint = net.JoinHostPort(host, strconv.FormatInt(int64(bindPort), 10))
|
||||
}
|
||||
}
|
||||
|
||||
// Downcase SANs. Some domain names (like ELBs) have capitals in them.
|
||||
LowercaseSANs(cfg.APIServer.CertSANs)
|
||||
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 *kubeadmapiv1beta1.InitConfiguration) (*kubeadmapi.InitConfiguration, error) {
|
||||
internalcfg := &kubeadmapi.InitConfiguration{}
|
||||
|
||||
if cfgPath != "" {
|
||||
// Loads configuration from config file, if provided
|
||||
// Nb. --config overrides command line flags
|
||||
klog.V(1).Infoln("loading configuration from the given file")
|
||||
|
||||
b, err := ioutil.ReadFile(cfgPath)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "unable to read config from %q ", cfgPath)
|
||||
}
|
||||
internalcfg, err = BytesToInternalConfig(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
// Takes passed flags into account; the defaulting is executed once again enforcing assignment 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 := SetInitDynamicDefaults(internalcfg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Validates cfg (flags/configs + defaults + dynamic defaults)
|
||||
if err := validation.ValidateInitConfiguration(internalcfg).ToAggregate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return internalcfg, nil
|
||||
}
|
||||
|
||||
// BytesToInternalConfig converts a byte slice to an internal, defaulted and validated configuration object.
|
||||
// The byte slice may contain one or many different YAML documents. These YAML documents are parsed one-by-one
|
||||
// and well-known ComponentConfig GroupVersionKinds are stored inside of the internal InitConfiguration struct
|
||||
func BytesToInternalConfig(b []byte) (*kubeadmapi.InitConfiguration, error) {
|
||||
var initcfg *kubeadmapi.InitConfiguration
|
||||
var clustercfg *kubeadmapi.ClusterConfiguration
|
||||
decodedComponentConfigObjects := map[componentconfigs.RegistrationKind]runtime.Object{}
|
||||
|
||||
if err := DetectUnsupportedVersion(b); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gvkmap, err := kubeadmutil.SplitYAMLDocuments(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for gvk, fileContent := range gvkmap {
|
||||
// verify the validity of the YAML
|
||||
strict.VerifyUnmarshalStrict(fileContent, gvk)
|
||||
|
||||
// Try to get the registration for the ComponentConfig based on the kind
|
||||
regKind := componentconfigs.RegistrationKind(gvk.Kind)
|
||||
registration, found := componentconfigs.Known[regKind]
|
||||
if found {
|
||||
// Unmarshal the bytes from the YAML document into a runtime.Object containing the ComponentConfiguration struct
|
||||
obj, err := registration.Unmarshal(fileContent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
decodedComponentConfigObjects[regKind] = obj
|
||||
continue
|
||||
}
|
||||
|
||||
if kubeadmutil.GroupVersionKindsHasInitConfiguration(gvk) {
|
||||
// Set initcfg to an empty struct value the deserializer will populate
|
||||
initcfg = &kubeadmapi.InitConfiguration{}
|
||||
// Decode the bytes into the internal struct. Under the hood, the bytes will be unmarshalled into the
|
||||
// right external version, defaulted, and converted into the internal version.
|
||||
if err := runtime.DecodeInto(kubeadmscheme.Codecs.UniversalDecoder(), fileContent, initcfg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
if kubeadmutil.GroupVersionKindsHasClusterConfiguration(gvk) {
|
||||
// Set clustercfg to an empty struct value the deserializer will populate
|
||||
clustercfg = &kubeadmapi.ClusterConfiguration{}
|
||||
// Decode the bytes into the internal struct. Under the hood, the bytes will be unmarshalled into the
|
||||
// right external version, defaulted, and converted into the internal version.
|
||||
if err := runtime.DecodeInto(kubeadmscheme.Codecs.UniversalDecoder(), fileContent, clustercfg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.Printf("[config] WARNING: Ignored YAML document with GroupVersionKind %v\n", gvk)
|
||||
}
|
||||
|
||||
// Enforce that InitConfiguration and/or ClusterConfiguration has to exist among the YAML documents
|
||||
if initcfg == nil && clustercfg == nil {
|
||||
return nil, errors.New("no InitConfiguration or ClusterConfiguration kind was found in the YAML file")
|
||||
}
|
||||
|
||||
// If InitConfiguration wasn't given, default it by creating an external struct instance, default it and convert into the internal type
|
||||
if initcfg == nil {
|
||||
extinitcfg := &kubeadmapiv1beta1.InitConfiguration{}
|
||||
kubeadmscheme.Scheme.Default(extinitcfg)
|
||||
// Set initcfg to an empty struct value the deserializer will populate
|
||||
initcfg = &kubeadmapi.InitConfiguration{}
|
||||
kubeadmscheme.Scheme.Convert(extinitcfg, initcfg, nil)
|
||||
}
|
||||
// If ClusterConfiguration was given, populate it in the InitConfiguration struct
|
||||
if clustercfg != nil {
|
||||
initcfg.ClusterConfiguration = *clustercfg
|
||||
}
|
||||
|
||||
// Save the loaded ComponentConfig objects in the initcfg object
|
||||
for kind, obj := range decodedComponentConfigObjects {
|
||||
registration, found := componentconfigs.Known[kind]
|
||||
if found {
|
||||
if ok := registration.SetToInternalConfig(obj, &initcfg.ClusterConfiguration); !ok {
|
||||
return nil, errors.Errorf("couldn't save componentconfig value for kind %q", string(kind))
|
||||
}
|
||||
} else {
|
||||
// This should never happen in practice
|
||||
fmt.Printf("[config] WARNING: Decoded a kind that couldn't be saved to the internal configuration: %q\n", string(kind))
|
||||
}
|
||||
}
|
||||
return initcfg, nil
|
||||
}
|
||||
|
||||
func defaultedInternalConfig() *kubeadmapi.ClusterConfiguration {
|
||||
externalcfg := &kubeadmapiv1beta1.ClusterConfiguration{}
|
||||
internalcfg := &kubeadmapi.ClusterConfiguration{}
|
||||
|
||||
kubeadmscheme.Scheme.Default(externalcfg)
|
||||
kubeadmscheme.Scheme.Convert(externalcfg, internalcfg, nil)
|
||||
|
||||
// Default the embedded ComponentConfig structs
|
||||
componentconfigs.Known.Default(internalcfg)
|
||||
return internalcfg
|
||||
}
|
||||
|
||||
// MarshalInitConfigurationToBytes marshals the internal InitConfiguration object to bytes. It writes the embedded
|
||||
// ClusterConfiguration object with ComponentConfigs out as separate YAML documents
|
||||
func MarshalInitConfigurationToBytes(cfg *kubeadmapi.InitConfiguration, gv schema.GroupVersion) ([]byte, error) {
|
||||
initbytes, err := kubeadmutil.MarshalToYamlForCodecs(cfg, gv, kubeadmscheme.Codecs)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
allFiles := [][]byte{initbytes}
|
||||
|
||||
// Exception: If the specified groupversion is targeting the internal type, don't print embedded ClusterConfiguration contents
|
||||
// This is mostly used for unit testing. In a real scenario the internal version of the API is never marshalled as-is.
|
||||
if gv.Version != runtime.APIVersionInternal {
|
||||
clusterbytes, err := MarshalClusterConfigurationToBytes(&cfg.ClusterConfiguration, gv)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
allFiles = append(allFiles, clusterbytes)
|
||||
}
|
||||
return bytes.Join(allFiles, []byte(kubeadmconstants.YAMLDocumentSeparator)), nil
|
||||
}
|
||||
|
||||
// MarshalClusterConfigurationToBytes marshals the internal ClusterConfiguration object to bytes. It writes the embedded
|
||||
// ComponentConfiguration objects out as separate YAML documents
|
||||
func MarshalClusterConfigurationToBytes(clustercfg *kubeadmapi.ClusterConfiguration, gv schema.GroupVersion) ([]byte, error) {
|
||||
clusterbytes, err := kubeadmutil.MarshalToYamlForCodecs(clustercfg, gv, kubeadmscheme.Codecs)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
allFiles := [][]byte{clusterbytes}
|
||||
componentConfigContent := map[string][]byte{}
|
||||
defaultedcfg := defaultedInternalConfig()
|
||||
|
||||
for kind, registration := range componentconfigs.Known {
|
||||
// If the ComponentConfig struct for the current registration is nil, skip it when marshalling
|
||||
realobj, ok := registration.GetFromInternalConfig(clustercfg)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
defaultedobj, ok := registration.GetFromInternalConfig(defaultedcfg)
|
||||
// Invalid: The caller asked to not print the componentconfigs if defaulted, but defaultComponentConfigs() wasn't able to create default objects to use for reference
|
||||
if !ok {
|
||||
return []byte{}, errors.New("couldn't create a default componentconfig object")
|
||||
}
|
||||
|
||||
// If the real ComponentConfig object differs from the default, print it out. If not, there's no need to print it out, so skip it
|
||||
if !reflect.DeepEqual(realobj, defaultedobj) {
|
||||
contentBytes, err := registration.Marshal(realobj)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
componentConfigContent[string(kind)] = contentBytes
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the ComponentConfig files by kind when marshalling
|
||||
sortedComponentConfigFiles := consistentOrderByteSlice(componentConfigContent)
|
||||
allFiles = append(allFiles, sortedComponentConfigFiles...)
|
||||
return bytes.Join(allFiles, []byte(kubeadmconstants.YAMLDocumentSeparator)), nil
|
||||
}
|
||||
|
||||
// consistentOrderByteSlice takes a map of a string key and a byte slice, and returns a byte slice of byte slices
|
||||
// with consistent ordering, where the keys in the map determine the ordering of the return value. This has to be
|
||||
// done as the order of a for...range loop over a map in go is undeterministic, and could otherwise lead to flakes
|
||||
// in e.g. unit tests when marshalling content with a random order
|
||||
func consistentOrderByteSlice(content map[string][]byte) [][]byte {
|
||||
keys := []string{}
|
||||
sortedContent := [][]byte{}
|
||||
for key := range content {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
for _, key := range keys {
|
||||
sortedContent = append(sortedContent, content[key])
|
||||
}
|
||||
return sortedContent
|
||||
}
|
217
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/initconfiguration_test.go
generated
vendored
Normal file
217
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/initconfiguration_test.go
generated
vendored
Normal file
@ -0,0 +1,217 @@
|
||||
/*
|
||||
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"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/pmezard/go-difflib/difflib"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||
kubeadmapiv1beta1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta1"
|
||||
)
|
||||
|
||||
const (
|
||||
master_v1alpha3YAML = "testdata/conversion/master/v1alpha3.yaml"
|
||||
master_v1beta1YAML = "testdata/conversion/master/v1beta1.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"
|
||||
)
|
||||
|
||||
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
|
||||
{ // v1alpha3 -> internal
|
||||
name: "v1alpha3ToInternal",
|
||||
in: master_v1alpha3YAML,
|
||||
out: master_internalYAML,
|
||||
groupVersion: kubeadm.SchemeGroupVersion,
|
||||
},
|
||||
{ // v1beta1 -> internal
|
||||
name: "v1beta1ToInternal",
|
||||
in: master_v1beta1YAML,
|
||||
out: master_internalYAML,
|
||||
groupVersion: kubeadm.SchemeGroupVersion,
|
||||
},
|
||||
{ // v1alpha3 -> internal -> v1beta1
|
||||
name: "v1alpha3Tov1beta1",
|
||||
in: master_v1alpha3YAML,
|
||||
out: master_v1beta1YAML,
|
||||
groupVersion: kubeadmapiv1beta1.SchemeGroupVersion,
|
||||
},
|
||||
{ // v1beta1 -> internal -> v1beta1
|
||||
name: "v1beta1Tov1beta1",
|
||||
in: master_v1beta1YAML,
|
||||
out: master_v1beta1YAML,
|
||||
groupVersion: kubeadmapiv1beta1.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
|
||||
{ // v1beta1 -> default -> validate -> internal -> v1beta1
|
||||
name: "incompleteYAMLToDefaultedv1beta1",
|
||||
in: master_incompleteYAML,
|
||||
out: master_defaultedYAML,
|
||||
groupVersion: kubeadmapiv1beta1.SchemeGroupVersion,
|
||||
},
|
||||
{ // v1alpha3 -> 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, &kubeadmapiv1beta1.InitConfiguration{})
|
||||
if err != nil {
|
||||
if rt.expectedErr {
|
||||
return
|
||||
}
|
||||
t2.Fatalf("couldn't unmarshal test data: %v", err)
|
||||
}
|
||||
|
||||
actual, err := MarshalInitConfigurationToBytes(internalcfg, rt.groupVersion)
|
||||
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))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConsistentOrderByteSlice(t *testing.T) {
|
||||
var (
|
||||
aKind = "Akind"
|
||||
aFile = []byte(`
|
||||
kind: Akind
|
||||
apiVersion: foo.k8s.io/v1
|
||||
`)
|
||||
aaKind = "Aakind"
|
||||
aaFile = []byte(`
|
||||
kind: Aakind
|
||||
apiVersion: foo.k8s.io/v1
|
||||
`)
|
||||
abKind = "Abkind"
|
||||
abFile = []byte(`
|
||||
kind: Abkind
|
||||
apiVersion: foo.k8s.io/v1
|
||||
`)
|
||||
)
|
||||
var tests = []struct {
|
||||
name string
|
||||
in map[string][]byte
|
||||
expected [][]byte
|
||||
}{
|
||||
{
|
||||
name: "a_aa_ab",
|
||||
in: map[string][]byte{
|
||||
aKind: aFile,
|
||||
aaKind: aaFile,
|
||||
abKind: abFile,
|
||||
},
|
||||
expected: [][]byte{aaFile, abFile, aFile},
|
||||
},
|
||||
{
|
||||
name: "a_ab_aa",
|
||||
in: map[string][]byte{
|
||||
aKind: aFile,
|
||||
abKind: abFile,
|
||||
aaKind: aaFile,
|
||||
},
|
||||
expected: [][]byte{aaFile, abFile, aFile},
|
||||
},
|
||||
{
|
||||
name: "aa_a_ab",
|
||||
in: map[string][]byte{
|
||||
aaKind: aaFile,
|
||||
aKind: aFile,
|
||||
abKind: abFile,
|
||||
},
|
||||
expected: [][]byte{aaFile, abFile, aFile},
|
||||
},
|
||||
{
|
||||
name: "aa_ab_a",
|
||||
in: map[string][]byte{
|
||||
aaKind: aaFile,
|
||||
abKind: abFile,
|
||||
aKind: aFile,
|
||||
},
|
||||
expected: [][]byte{aaFile, abFile, aFile},
|
||||
},
|
||||
{
|
||||
name: "ab_a_aa",
|
||||
in: map[string][]byte{
|
||||
abKind: abFile,
|
||||
aKind: aFile,
|
||||
aaKind: aaFile,
|
||||
},
|
||||
expected: [][]byte{aaFile, abFile, aFile},
|
||||
},
|
||||
{
|
||||
name: "ab_aa_a",
|
||||
in: map[string][]byte{
|
||||
abKind: abFile,
|
||||
aaKind: aaFile,
|
||||
aKind: aFile,
|
||||
},
|
||||
expected: [][]byte{aaFile, abFile, aFile},
|
||||
},
|
||||
}
|
||||
|
||||
for _, rt := range tests {
|
||||
t.Run(rt.name, func(t2 *testing.T) {
|
||||
actual := consistentOrderByteSlice(rt.in)
|
||||
if !reflect.DeepEqual(rt.expected, actual) {
|
||||
t2.Errorf("the expected and actual output differs.\n\texpected: %s\n\tout: %s\n", rt.expected, actual)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
121
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/joinconfiguration.go
generated
vendored
Normal file
121
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/joinconfiguration.go
generated
vendored
Normal file
@ -0,0 +1,121 @@
|
||||
/*
|
||||
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 (
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/klog"
|
||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||
kubeadmscheme "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/scheme"
|
||||
kubeadmapiv1beta1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta1"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/util/config/strict"
|
||||
)
|
||||
|
||||
// SetJoinDynamicDefaults checks and sets configuration values for the JoinConfiguration object
|
||||
func SetJoinDynamicDefaults(cfg *kubeadmapi.JoinConfiguration) error {
|
||||
addMasterTaint := false
|
||||
if cfg.ControlPlane != nil {
|
||||
addMasterTaint = true
|
||||
}
|
||||
if err := SetNodeRegistrationDynamicDefaults(&cfg.NodeRegistration, addMasterTaint); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := SetJoinControlPlaneDefaults(cfg.ControlPlane); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetJoinControlPlaneDefaults checks and sets configuration values for the JoinControlPlane object
|
||||
func SetJoinControlPlaneDefaults(cfg *kubeadmapi.JoinControlPlane) error {
|
||||
if cfg != nil {
|
||||
if err := SetAPIEndpointDynamicDefaults(&cfg.LocalAPIEndpoint); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// JoinConfigFileAndDefaultsToInternalConfig 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 JoinConfigFileAndDefaultsToInternalConfig(cfgPath string, defaultversionedcfg *kubeadmapiv1beta1.JoinConfiguration) (*kubeadmapi.JoinConfiguration, error) {
|
||||
internalcfg := &kubeadmapi.JoinConfiguration{}
|
||||
|
||||
if cfgPath != "" {
|
||||
// Loads configuration from config file, if provided
|
||||
// Nb. --config overrides command line flags, TODO: fix this
|
||||
klog.V(1).Infoln("loading configuration from the given file")
|
||||
|
||||
b, err := ioutil.ReadFile(cfgPath)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "unable to read config from %q ", cfgPath)
|
||||
}
|
||||
|
||||
if err := DetectUnsupportedVersion(b); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gvkmap, err := kubeadmutil.SplitYAMLDocuments(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
joinBytes := []byte{}
|
||||
for gvk, bytes := range gvkmap {
|
||||
if gvk.Kind == constants.JoinConfigurationKind {
|
||||
joinBytes = bytes
|
||||
// verify the validity of the YAML
|
||||
strict.VerifyUnmarshalStrict(bytes, gvk)
|
||||
}
|
||||
}
|
||||
|
||||
if len(joinBytes) == 0 {
|
||||
return nil, errors.Errorf("no %s found in config file %q", constants.JoinConfigurationKind, cfgPath)
|
||||
}
|
||||
|
||||
if err := runtime.DecodeInto(kubeadmscheme.Codecs.UniversalDecoder(), joinBytes, internalcfg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} 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.ValidateJoinConfiguration(internalcfg).ToAggregate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return internalcfg, nil
|
||||
}
|
@ -24,54 +24,60 @@ import (
|
||||
"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"
|
||||
kubeadmapiv1beta1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta1"
|
||||
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
||||
)
|
||||
|
||||
const (
|
||||
node_v1alpha1YAML = "testdata/conversion/node/v1alpha1.yaml"
|
||||
node_v1alpha2YAML = "testdata/conversion/node/v1alpha2.yaml"
|
||||
node_v1alpha3YAML = "testdata/conversion/node/v1alpha3.yaml"
|
||||
node_v1beta1YAML = "testdata/conversion/node/v1beta1.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) {
|
||||
func TestJoinConfigFileAndDefaultsToInternalConfig(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,
|
||||
// These tests are reading one file, loading it using JoinConfigFileAndDefaultsToInternalConfig 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,
|
||||
{ // v1alpha3 -> internal
|
||||
name: "v1alpha3ToInternal",
|
||||
in: node_v1alpha3YAML,
|
||||
out: node_internalYAML,
|
||||
groupVersion: kubeadm.SchemeGroupVersion,
|
||||
},
|
||||
{ // v1alpha2 -> internal
|
||||
name: "v1alpha2ToInternal",
|
||||
in: node_v1alpha2YAML,
|
||||
{ // v1beta1 -> internal
|
||||
name: "v1beta1ToInternal",
|
||||
in: node_v1beta1YAML,
|
||||
out: node_internalYAML,
|
||||
groupVersion: kubeadm.SchemeGroupVersion,
|
||||
},
|
||||
{ // v1alpha1 -> internal -> v1alpha2
|
||||
name: "v1alpha1WithoutTypeMetaTov1alpha2",
|
||||
in: node_v1alpha1YAML,
|
||||
out: node_v1alpha2YAML,
|
||||
groupVersion: v1alpha2.SchemeGroupVersion,
|
||||
{ // v1alpha3 -> internal -> v1beta1
|
||||
name: "v1alpha3Tov1beta1",
|
||||
in: node_v1alpha3YAML,
|
||||
out: node_v1beta1YAML,
|
||||
groupVersion: kubeadmapiv1beta1.SchemeGroupVersion,
|
||||
},
|
||||
// These tests are reading one file that has only a subset of the fields populated, loading it using NodeConfigFileAndDefaultsToInternalConfig,
|
||||
{ // v1beta1 -> internal -> v1beta1
|
||||
name: "v1beta1Tov1beta1",
|
||||
in: node_v1beta1YAML,
|
||||
out: node_v1beta1YAML,
|
||||
groupVersion: kubeadmapiv1beta1.SchemeGroupVersion,
|
||||
},
|
||||
// These tests are reading one file that has only a subset of the fields populated, loading it using JoinConfigFileAndDefaultsToInternalConfig,
|
||||
// and then marshals the internal object to the expected groupVersion
|
||||
{ // v1alpha1 -> default -> validate -> internal -> v1alpha2
|
||||
name: "incompleteYAMLToDefaulted",
|
||||
{ // v1beta1 -> default -> validate -> internal -> v1beta1
|
||||
name: "incompleteYAMLToDefaultedv1beta1",
|
||||
in: node_incompleteYAML,
|
||||
out: node_defaultedYAML,
|
||||
groupVersion: v1alpha2.SchemeGroupVersion,
|
||||
groupVersion: kubeadmapiv1beta1.SchemeGroupVersion,
|
||||
},
|
||||
{ // v1alpha1 (faulty) -> validation should fail
|
||||
{ // v1alpha3 -> validation should fail
|
||||
name: "invalidYAMLShouldFail",
|
||||
in: node_invalidYAML,
|
||||
expectedErr: true,
|
||||
@ -81,7 +87,7 @@ func TestNodeConfigFileAndDefaultsToInternalConfig(t *testing.T) {
|
||||
for _, rt := range tests {
|
||||
t.Run(rt.name, func(t2 *testing.T) {
|
||||
|
||||
internalcfg, err := NodeConfigFileAndDefaultsToInternalConfig(rt.in, &v1alpha2.NodeConfiguration{})
|
||||
internalcfg, err := JoinConfigFileAndDefaultsToInternalConfig(rt.in, &kubeadmapiv1beta1.JoinConfiguration{})
|
||||
if err != nil {
|
||||
if rt.expectedErr {
|
||||
return
|
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
|
||||
}
|
46
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/strict/BUILD
generated
vendored
Normal file
46
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/strict/BUILD
generated
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["strict.go"],
|
||||
importpath = "k8s.io/kubernetes/cmd/kubeadm/app/util/config/strict",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//cmd/kubeadm/app/apis/kubeadm/scheme:go_default_library",
|
||||
"//cmd/kubeadm/app/componentconfigs:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//vendor/github.com/pkg/errors:go_default_library",
|
||||
"//vendor/k8s.io/klog:go_default_library",
|
||||
"//vendor/sigs.k8s.io/yaml:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["strict_test.go"],
|
||||
data = glob(["testdata/**"]),
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//cmd/kubeadm/app/apis/kubeadm/v1alpha3:go_default_library",
|
||||
"//cmd/kubeadm/app/apis/kubeadm/v1beta1:go_default_library",
|
||||
"//cmd/kubeadm/app/componentconfigs:go_default_library",
|
||||
"//cmd/kubeadm/app/constants:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//staging/src/k8s.io/kube-proxy/config/v1alpha1:go_default_library",
|
||||
"//staging/src/k8s.io/kubelet/config/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"],
|
||||
)
|
58
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/strict/strict.go
generated
vendored
Normal file
58
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/strict/strict.go
generated
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
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 strict
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/klog"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/scheme"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/componentconfigs"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
// VerifyUnmarshalStrict takes a YAML byte slice and a GroupVersionKind and verifies if the YAML
|
||||
// schema is known and if it unmarshals with strict mode.
|
||||
//
|
||||
// TODO(neolit123): The returned error here is currently ignored everywhere and a klog warning is thrown instead.
|
||||
// We don't want to turn this into an actual error yet. Eventually this can be controlled with an optional CLI flag.
|
||||
func VerifyUnmarshalStrict(bytes []byte, gvk schema.GroupVersionKind) error {
|
||||
|
||||
var (
|
||||
iface interface{}
|
||||
err error
|
||||
)
|
||||
|
||||
iface, err = scheme.Scheme.New(gvk)
|
||||
if err != nil {
|
||||
iface, err = componentconfigs.Scheme.New(gvk)
|
||||
if err != nil {
|
||||
err := errors.Errorf("unknown configuration %#v for scheme definitions in %q and %q",
|
||||
gvk, scheme.Scheme.Name(), componentconfigs.Scheme.Name())
|
||||
klog.Warning(err.Error())
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := yaml.UnmarshalStrict(bytes, iface); err != nil {
|
||||
err := errors.Wrapf(err, "error unmarshaling configuration %#v", gvk)
|
||||
klog.Warning(err.Error())
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
168
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/strict/strict_test.go
generated
vendored
Normal file
168
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/strict/strict_test.go
generated
vendored
Normal file
@ -0,0 +1,168 @@
|
||||
/*
|
||||
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 strict
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
kubeproxyconfigv1alpha1 "k8s.io/kube-proxy/config/v1alpha1"
|
||||
kubeletconfigv1beta1 "k8s.io/kubelet/config/v1beta1"
|
||||
kubeadmapiv1alpha3 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha3"
|
||||
kubeadmapiv1beta1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta1"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/componentconfigs"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||
)
|
||||
|
||||
func TestVerifyUnmarshalStrict(t *testing.T) {
|
||||
const (
|
||||
pathTestData = "testdata/"
|
||||
)
|
||||
|
||||
var testFiles = []struct {
|
||||
fileName string
|
||||
kind string
|
||||
groupVersion schema.GroupVersion
|
||||
expectedError bool
|
||||
}{
|
||||
// tests with file errors
|
||||
{
|
||||
fileName: "invalid_duplicate_field_clustercfg.yaml",
|
||||
kind: constants.InitConfigurationKind,
|
||||
groupVersion: kubeadmapiv1beta1.SchemeGroupVersion,
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
fileName: "invalid_duplicate_field_initcfg.yaml",
|
||||
kind: constants.InitConfigurationKind,
|
||||
groupVersion: kubeadmapiv1alpha3.SchemeGroupVersion,
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
fileName: "invalid_duplicate_field_joincfg.yaml",
|
||||
kind: constants.JoinConfigurationKind,
|
||||
groupVersion: kubeadmapiv1beta1.SchemeGroupVersion,
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
fileName: "invalid_duplicate_field_kubeletcfg.yaml",
|
||||
kind: string(componentconfigs.KubeletConfigurationKind),
|
||||
groupVersion: kubeletconfigv1beta1.SchemeGroupVersion,
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
fileName: "invalid_duplicate_field_kubeproxycfg.yaml",
|
||||
kind: string(componentconfigs.KubeProxyConfigurationKind),
|
||||
groupVersion: kubeproxyconfigv1alpha1.SchemeGroupVersion,
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
fileName: "invalid_unknown_field_clustercfg.yaml",
|
||||
kind: constants.ClusterConfigurationKind,
|
||||
groupVersion: kubeadmapiv1beta1.SchemeGroupVersion,
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
fileName: "invalid_unknown_field_initcfg.yaml",
|
||||
kind: constants.InitConfigurationKind,
|
||||
groupVersion: kubeadmapiv1beta1.SchemeGroupVersion,
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
fileName: "invalid_unknown_field_joincfg.yaml",
|
||||
kind: constants.JoinConfigurationKind,
|
||||
groupVersion: kubeadmapiv1beta1.SchemeGroupVersion,
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
fileName: "invalid_unknown_field_kubeletcfg.yaml",
|
||||
kind: string(componentconfigs.KubeletConfigurationKind),
|
||||
groupVersion: kubeletconfigv1beta1.SchemeGroupVersion,
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
fileName: "invalid_unknown_field_kubeproxycfg.yaml",
|
||||
kind: string(componentconfigs.KubeProxyConfigurationKind),
|
||||
groupVersion: kubeproxyconfigv1alpha1.SchemeGroupVersion,
|
||||
expectedError: true,
|
||||
},
|
||||
// test unknown groupVersion and kind
|
||||
{
|
||||
fileName: "valid_clustercfg.yaml",
|
||||
kind: constants.ClusterConfigurationKind,
|
||||
groupVersion: schema.GroupVersion{Group: "someGroup", Version: "v1"},
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
fileName: "valid_clustercfg.yaml",
|
||||
kind: "SomeUnknownKind",
|
||||
groupVersion: kubeadmapiv1beta1.SchemeGroupVersion,
|
||||
expectedError: true,
|
||||
},
|
||||
// valid tests
|
||||
{
|
||||
fileName: "valid_clustercfg.yaml",
|
||||
kind: constants.ClusterConfigurationKind,
|
||||
groupVersion: kubeadmapiv1beta1.SchemeGroupVersion,
|
||||
expectedError: false,
|
||||
},
|
||||
{
|
||||
fileName: "valid_initcfg.yaml",
|
||||
kind: constants.InitConfigurationKind,
|
||||
groupVersion: kubeadmapiv1beta1.SchemeGroupVersion,
|
||||
expectedError: false,
|
||||
},
|
||||
{
|
||||
fileName: "valid_joincfg.yaml",
|
||||
kind: constants.JoinConfigurationKind,
|
||||
groupVersion: kubeadmapiv1beta1.SchemeGroupVersion,
|
||||
expectedError: false,
|
||||
},
|
||||
{
|
||||
fileName: "valid_kubeletcfg.yaml",
|
||||
kind: string(componentconfigs.KubeletConfigurationKind),
|
||||
groupVersion: kubeletconfigv1beta1.SchemeGroupVersion,
|
||||
expectedError: false,
|
||||
},
|
||||
{
|
||||
fileName: "valid_kubeproxycfg.yaml",
|
||||
kind: string(componentconfigs.KubeProxyConfigurationKind),
|
||||
groupVersion: kubeproxyconfigv1alpha1.SchemeGroupVersion,
|
||||
expectedError: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testFiles {
|
||||
t.Run(test.fileName, func(t *testing.T) {
|
||||
bytes, err := ioutil.ReadFile(filepath.Join(pathTestData, test.fileName))
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't read test data: %v", err)
|
||||
}
|
||||
gvk := schema.GroupVersionKind{
|
||||
Group: test.groupVersion.Group,
|
||||
Version: test.groupVersion.Version,
|
||||
Kind: test.kind,
|
||||
}
|
||||
err = VerifyUnmarshalStrict(bytes, gvk)
|
||||
if (err != nil) != test.expectedError {
|
||||
t.Errorf("expected error %v, got %v, error: %v", err != nil, test.expectedError, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
4
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/strict/testdata/invalid_duplicate_field_clustercfg.yaml
generated
vendored
Normal file
4
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/strict/testdata/invalid_duplicate_field_clustercfg.yaml
generated
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
apiVersion: kubeadm.k8s.io/v1beta1
|
||||
kind: ClusterConfiguration
|
||||
controlPlaneEndpoint: test1
|
||||
controlPlaneEndpoint: test2
|
6
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/strict/testdata/invalid_duplicate_field_initcfg.yaml
generated
vendored
Normal file
6
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/strict/testdata/invalid_duplicate_field_initcfg.yaml
generated
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
apiVersion: kubeadm.k8s.io/v1beta1
|
||||
kind: InitConfiguration
|
||||
bootstrapTokens:
|
||||
- token: "9a08jv.c0izixklcxtmnze7"
|
||||
bootstrapTokens:
|
||||
- token: "9a08jv.c0izixklcxtmnze7"
|
4
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/strict/testdata/invalid_duplicate_field_joincfg.yaml
generated
vendored
Normal file
4
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/strict/testdata/invalid_duplicate_field_joincfg.yaml
generated
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
apiVersion: kubeadm.k8s.io/v1beta1
|
||||
kind: JoinConfiguration
|
||||
caCertPath: relativepath
|
||||
caCertPath: relativepath
|
4
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/strict/testdata/invalid_duplicate_field_kubeletcfg.yaml
generated
vendored
Normal file
4
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/strict/testdata/invalid_duplicate_field_kubeletcfg.yaml
generated
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
apiVersion: kubelet.config.k8s.io/v1beta1
|
||||
kind: KubeletConfiguration
|
||||
address: 1.2.3.4
|
||||
address: 1.2.3.4
|
4
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/strict/testdata/invalid_duplicate_field_kubeproxycfg.yaml
generated
vendored
Normal file
4
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/strict/testdata/invalid_duplicate_field_kubeproxycfg.yaml
generated
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
apiVersion: kubeproxy.config.k8s.io/v1alpha1
|
||||
kind: KubeProxyConfiguration
|
||||
portRange: ""
|
||||
portRange: ""
|
3
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/strict/testdata/invalid_unknown_field_clustercfg.yaml
generated
vendored
Normal file
3
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/strict/testdata/invalid_unknown_field_clustercfg.yaml
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
apiVersion: kubeadm.k8s.io/v1beta1
|
||||
kind: ClusterConfiguration
|
||||
unknownField: test
|
3
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/strict/testdata/invalid_unknown_field_initcfg.yaml
generated
vendored
Normal file
3
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/strict/testdata/invalid_unknown_field_initcfg.yaml
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
apiVersion: kubeadm.k8s.io/v1beta1
|
||||
kind: InitConfiguration
|
||||
unknownField: test
|
3
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/strict/testdata/invalid_unknown_field_joincfg.yaml
generated
vendored
Normal file
3
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/strict/testdata/invalid_unknown_field_joincfg.yaml
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
apiVersion: kubeadm.k8s.io/v1beta1
|
||||
kind: JoinConfiguration
|
||||
unknownField: test
|
3
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/strict/testdata/invalid_unknown_field_kubeletcfg.yaml
generated
vendored
Normal file
3
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/strict/testdata/invalid_unknown_field_kubeletcfg.yaml
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
apiVersion: kubelet.config.k8s.io/v1beta1
|
||||
kind: KubeletConfiguration
|
||||
unknownField: unknown
|
3
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/strict/testdata/invalid_unknown_field_kubeproxycfg.yaml
generated
vendored
Normal file
3
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/strict/testdata/invalid_unknown_field_kubeproxycfg.yaml
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
apiVersion: kubeproxy.config.k8s.io/v1alpha1
|
||||
kind: KubeProxyConfiguration
|
||||
unknownField: unknown
|
3
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/strict/testdata/valid_clustercfg.yaml
generated
vendored
Normal file
3
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/strict/testdata/valid_clustercfg.yaml
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
apiVersion: kubeadm.k8s.io/v1beta1
|
||||
kind: ClusterConfiguration
|
||||
controlPlaneEndpoint: 202.0.100.1
|
4
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/strict/testdata/valid_initcfg.yaml
generated
vendored
Normal file
4
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/strict/testdata/valid_initcfg.yaml
generated
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
apiVersion: kubeadm.k8s.io/v1beta1
|
||||
kind: InitConfiguration
|
||||
bootstrapTokens:
|
||||
- token: "9a08jv.c0izixklcxtmnze7"
|
3
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/strict/testdata/valid_joincfg.yaml
generated
vendored
Normal file
3
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/strict/testdata/valid_joincfg.yaml
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
apiVersion: kubeadm.k8s.io/v1beta1
|
||||
kind: JoinConfiguration
|
||||
caCertPath: relativepath
|
3
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/strict/testdata/valid_kubeletcfg.yaml
generated
vendored
Normal file
3
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/strict/testdata/valid_kubeletcfg.yaml
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
apiVersion: kubelet.config.k8s.io/v1beta1
|
||||
kind: KubeletConfiguration
|
||||
address: 1.2.3.4
|
3
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/strict/testdata/valid_kubeproxycfg.yaml
generated
vendored
Normal file
3
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/strict/testdata/valid_kubeproxycfg.yaml
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
apiVersion: kubeproxy.config.k8s.io/v1alpha1
|
||||
kind: KubeProxyConfiguration
|
||||
portRange: ""
|
@ -1,15 +1,19 @@
|
||||
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: ""
|
||||
APIServer:
|
||||
CertSANs: null
|
||||
ExtraArgs:
|
||||
authorization-mode: Node,RBAC,Webhook
|
||||
ExtraVolumes:
|
||||
- HostPath: /host/read-only
|
||||
MountPath: /mount/read-only
|
||||
Name: ReadOnlyVolume
|
||||
PathType: ""
|
||||
ReadOnly: true
|
||||
- HostPath: /host/writable
|
||||
MountPath: /mount/writable
|
||||
Name: WritableVolume
|
||||
PathType: ""
|
||||
ReadOnly: false
|
||||
TimeoutForControlPlane: 4m0s
|
||||
BootstrapTokens:
|
||||
- Description: ""
|
||||
Expires: null
|
||||
@ -23,128 +27,166 @@ BootstrapTokens:
|
||||
CIImageRepository: ""
|
||||
CertificatesDir: /etc/kubernetes/pki
|
||||
ClusterName: kubernetes
|
||||
ControllerManagerExtraArgs: null
|
||||
ControllerManagerExtraVolumes: null
|
||||
ComponentConfigs:
|
||||
KubeProxy:
|
||||
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: iptables
|
||||
NodePortAddresses: null
|
||||
OOMScoreAdj: -999
|
||||
PortRange: ""
|
||||
ResourceContainer: /kube-proxy
|
||||
UDPIdleTimeout: 250ms
|
||||
Kubelet:
|
||||
Address: 1.2.3.4
|
||||
Authentication:
|
||||
Anonymous:
|
||||
Enabled: false
|
||||
Webhook:
|
||||
CacheTTL: 2m0s
|
||||
Enabled: true
|
||||
X509:
|
||||
ClientCAFile: /etc/kubernetes/pki/ca.crt
|
||||
Authorization:
|
||||
Mode: Webhook
|
||||
Webhook:
|
||||
CacheAuthorizedTTL: 5m0s
|
||||
CacheUnauthorizedTTL: 30s
|
||||
CPUCFSQuota: true
|
||||
CPUCFSQuotaPeriod: 0s
|
||||
CPUManagerPolicy: none
|
||||
CPUManagerReconcilePeriod: 10s
|
||||
CgroupDriver: cgroupfs
|
||||
CgroupRoot: ""
|
||||
CgroupsPerQOS: true
|
||||
ClusterDNS:
|
||||
- 10.96.0.10
|
||||
ClusterDomain: cluster.local
|
||||
ConfigMapAndSecretChangeDetectionStrategy: Watch
|
||||
ContainerLogMaxFiles: 5
|
||||
ContainerLogMaxSize: 10Mi
|
||||
ContentType: application/vnd.kubernetes.protobuf
|
||||
EnableContentionProfiling: false
|
||||
EnableControllerAttachDetach: true
|
||||
EnableDebuggingHandlers: true
|
||||
EnforceNodeAllocatable:
|
||||
- pods
|
||||
EventBurst: 10
|
||||
EventRecordQPS: 5
|
||||
EvictionHard:
|
||||
imagefs.available: 15%
|
||||
memory.available: 100Mi
|
||||
nodefs.available: 10%
|
||||
nodefs.inodesFree: 5%
|
||||
EvictionMaxPodGracePeriod: 0
|
||||
EvictionMinimumReclaim: null
|
||||
EvictionPressureTransitionPeriod: 5m0s
|
||||
EvictionSoft: null
|
||||
EvictionSoftGracePeriod: null
|
||||
FailSwapOn: true
|
||||
FeatureGates: null
|
||||
FileCheckFrequency: 20s
|
||||
HTTPCheckFrequency: 20s
|
||||
HairpinMode: promiscuous-bridge
|
||||
HealthzBindAddress: 127.0.0.1
|
||||
HealthzPort: 10248
|
||||
IPTablesDropBit: 15
|
||||
IPTablesMasqueradeBit: 14
|
||||
ImageGCHighThresholdPercent: 85
|
||||
ImageGCLowThresholdPercent: 80
|
||||
ImageMinimumGCAge: 2m0s
|
||||
KubeAPIBurst: 10
|
||||
KubeAPIQPS: 5
|
||||
KubeReserved: null
|
||||
KubeReservedCgroup: ""
|
||||
KubeletCgroups: ""
|
||||
MakeIPTablesUtilChains: true
|
||||
MaxOpenFiles: 1000000
|
||||
MaxPods: 110
|
||||
NodeLeaseDurationSeconds: 40
|
||||
NodeStatusReportFrequency: 1m0s
|
||||
NodeStatusUpdateFrequency: 10s
|
||||
OOMScoreAdj: -999
|
||||
PodCIDR: ""
|
||||
PodPidsLimit: -1
|
||||
PodsPerCore: 0
|
||||
Port: 10250
|
||||
ProtectKernelDefaults: false
|
||||
QOSReserved: null
|
||||
ReadOnlyPort: 0
|
||||
RegistryBurst: 10
|
||||
RegistryPullQPS: 5
|
||||
ResolverConfig: /etc/resolv.conf
|
||||
RotateCertificates: true
|
||||
RuntimeRequestTimeout: 2m0s
|
||||
SerializeImagePulls: true
|
||||
ServerTLSBootstrap: false
|
||||
StaticPodPath: /etc/kubernetes/manifests
|
||||
StaticPodURL: ""
|
||||
StaticPodURLHeader: null
|
||||
StreamingConnectionIdleTimeout: 4h0m0s
|
||||
SyncFrequency: 1m0s
|
||||
SystemCgroups: ""
|
||||
SystemReserved: null
|
||||
SystemReservedCgroup: ""
|
||||
TLSCertFile: ""
|
||||
TLSCipherSuites: null
|
||||
TLSMinVersion: ""
|
||||
TLSPrivateKeyFile: ""
|
||||
VolumeStatsAggPeriod: 1m0s
|
||||
ControlPlaneEndpoint: ""
|
||||
ControllerManager:
|
||||
ExtraArgs: null
|
||||
ExtraVolumes: null
|
||||
DNS:
|
||||
ImageRepository: ""
|
||||
ImageTag: ""
|
||||
Type: CoreDNS
|
||||
Etcd:
|
||||
External: null
|
||||
Local:
|
||||
DataDir: /var/lib/etcd
|
||||
ExtraArgs: null
|
||||
Image: ""
|
||||
ImageRepository: ""
|
||||
ImageTag: ""
|
||||
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
|
||||
KubernetesVersion: v1.12.2
|
||||
LocalAPIEndpoint:
|
||||
AdvertiseAddress: 192.168.2.2
|
||||
BindPort: 6443
|
||||
Networking:
|
||||
DNSDomain: cluster.local
|
||||
PodSubnet: ""
|
||||
@ -156,6 +198,7 @@ NodeRegistration:
|
||||
Taints:
|
||||
- effect: NoSchedule
|
||||
key: node-role.kubernetes.io/master
|
||||
SchedulerExtraArgs: null
|
||||
SchedulerExtraVolumes: null
|
||||
UnifiedControlPlaneImage: ""
|
||||
Scheduler:
|
||||
ExtraArgs: null
|
||||
ExtraVolumes: null
|
||||
UseHyperKubeImage: true
|
||||
|
@ -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: ""
|
165
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/testdata/conversion/master/v1alpha3.yaml
generated
vendored
Normal file
165
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/testdata/conversion/master/v1alpha3.yaml
generated
vendored
Normal file
@ -0,0 +1,165 @@
|
||||
apiEndpoint:
|
||||
advertiseAddress: 192.168.2.2
|
||||
bindPort: 6443
|
||||
apiVersion: kubeadm.k8s.io/v1alpha3
|
||||
bootstrapTokens:
|
||||
- groups:
|
||||
- system:bootstrappers:kubeadm:default-node-token
|
||||
token: s73ybu.6tw6wnqgp5z0wb77
|
||||
ttl: 24h0m0s
|
||||
usages:
|
||||
- signing
|
||||
- authentication
|
||||
kind: InitConfiguration
|
||||
nodeRegistration:
|
||||
criSocket: /var/run/dockershim.sock
|
||||
name: master-1
|
||||
taints:
|
||||
- effect: NoSchedule
|
||||
key: node-role.kubernetes.io/master
|
||||
---
|
||||
apiServerExtraArgs:
|
||||
authorization-mode: Node,RBAC,Webhook
|
||||
apiServerExtraVolumes:
|
||||
- hostPath: /host/read-only
|
||||
mountPath: /mount/read-only
|
||||
name: ReadOnlyVolume
|
||||
- hostPath: /host/writable
|
||||
mountPath: /mount/writable
|
||||
name: WritableVolume
|
||||
writable: true
|
||||
apiVersion: kubeadm.k8s.io/v1alpha3
|
||||
auditPolicy:
|
||||
logDir: /var/log/kubernetes/audit
|
||||
logMaxAge: 2
|
||||
path: ""
|
||||
certificatesDir: /etc/kubernetes/pki
|
||||
clusterName: kubernetes
|
||||
controlPlaneEndpoint: ""
|
||||
etcd:
|
||||
local:
|
||||
dataDir: /var/lib/etcd
|
||||
image: ""
|
||||
imageRepository: k8s.gcr.io
|
||||
kind: ClusterConfiguration
|
||||
kubernetesVersion: v1.12.2
|
||||
networking:
|
||||
dnsDomain: cluster.local
|
||||
podSubnet: ""
|
||||
serviceSubnet: 10.96.0.0/12
|
||||
unifiedControlPlaneImage: "k8s.gcr.io/hyperkube:v1.12.2"
|
||||
---
|
||||
apiVersion: kubeproxy.config.k8s.io/v1alpha1
|
||||
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
|
||||
kind: KubeProxyConfiguration
|
||||
metricsBindAddress: 127.0.0.1:10249
|
||||
mode: iptables
|
||||
nodePortAddresses: null
|
||||
oomScoreAdj: -999
|
||||
portRange: ""
|
||||
resourceContainer: /kube-proxy
|
||||
udpIdleTimeout: 250ms
|
||||
---
|
||||
address: 1.2.3.4
|
||||
apiVersion: kubelet.config.k8s.io/v1beta1
|
||||
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
|
||||
configMapAndSecretChangeDetectionStrategy: Watch
|
||||
containerLogMaxFiles: 5
|
||||
containerLogMaxSize: 10Mi
|
||||
contentType: application/vnd.kubernetes.protobuf
|
||||
cpuCFSQuota: true
|
||||
cpuCFSQuotaPeriod: 0s
|
||||
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
|
||||
kind: KubeletConfiguration
|
||||
kubeAPIBurst: 10
|
||||
kubeAPIQPS: 5
|
||||
makeIPTablesUtilChains: true
|
||||
maxOpenFiles: 1000000
|
||||
maxPods: 110
|
||||
nodeLeaseDurationSeconds: 40
|
||||
nodeStatusReportFrequency: 1m0s
|
||||
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
|
166
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/testdata/conversion/master/v1beta1.yaml
generated
vendored
Normal file
166
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/testdata/conversion/master/v1beta1.yaml
generated
vendored
Normal file
@ -0,0 +1,166 @@
|
||||
apiVersion: kubeadm.k8s.io/v1beta1
|
||||
bootstrapTokens:
|
||||
- groups:
|
||||
- system:bootstrappers:kubeadm:default-node-token
|
||||
token: s73ybu.6tw6wnqgp5z0wb77
|
||||
ttl: 24h0m0s
|
||||
usages:
|
||||
- signing
|
||||
- authentication
|
||||
kind: InitConfiguration
|
||||
localAPIEndpoint:
|
||||
advertiseAddress: 192.168.2.2
|
||||
bindPort: 6443
|
||||
nodeRegistration:
|
||||
criSocket: /var/run/dockershim.sock
|
||||
name: master-1
|
||||
taints:
|
||||
- effect: NoSchedule
|
||||
key: node-role.kubernetes.io/master
|
||||
---
|
||||
apiServer:
|
||||
extraArgs:
|
||||
authorization-mode: Node,RBAC,Webhook
|
||||
extraVolumes:
|
||||
- hostPath: /host/read-only
|
||||
mountPath: /mount/read-only
|
||||
name: ReadOnlyVolume
|
||||
readOnly: true
|
||||
- hostPath: /host/writable
|
||||
mountPath: /mount/writable
|
||||
name: WritableVolume
|
||||
timeoutForControlPlane: 4m0s
|
||||
apiVersion: kubeadm.k8s.io/v1beta1
|
||||
certificatesDir: /etc/kubernetes/pki
|
||||
clusterName: kubernetes
|
||||
controlPlaneEndpoint: ""
|
||||
controllerManager: {}
|
||||
dns:
|
||||
type: CoreDNS
|
||||
etcd:
|
||||
local:
|
||||
dataDir: /var/lib/etcd
|
||||
imageRepository: k8s.gcr.io
|
||||
kind: ClusterConfiguration
|
||||
kubernetesVersion: v1.12.2
|
||||
networking:
|
||||
dnsDomain: cluster.local
|
||||
podSubnet: ""
|
||||
serviceSubnet: 10.96.0.0/12
|
||||
scheduler: {}
|
||||
useHyperKubeImage: true
|
||||
---
|
||||
apiVersion: kubeproxy.config.k8s.io/v1alpha1
|
||||
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
|
||||
kind: KubeProxyConfiguration
|
||||
metricsBindAddress: 127.0.0.1:10249
|
||||
mode: iptables
|
||||
nodePortAddresses: null
|
||||
oomScoreAdj: -999
|
||||
portRange: ""
|
||||
resourceContainer: /kube-proxy
|
||||
udpIdleTimeout: 250ms
|
||||
---
|
||||
address: 1.2.3.4
|
||||
apiVersion: kubelet.config.k8s.io/v1beta1
|
||||
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
|
||||
configMapAndSecretChangeDetectionStrategy: Watch
|
||||
containerLogMaxFiles: 5
|
||||
containerLogMaxSize: 10Mi
|
||||
contentType: application/vnd.kubernetes.protobuf
|
||||
cpuCFSQuota: true
|
||||
cpuCFSQuotaPeriod: 0s
|
||||
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
|
||||
kind: KubeletConfiguration
|
||||
kubeAPIBurst: 10
|
||||
kubeAPIQPS: 5
|
||||
makeIPTablesUtilChains: true
|
||||
maxOpenFiles: 1000000
|
||||
maxPods: 110
|
||||
nodeLeaseDurationSeconds: 40
|
||||
nodeStatusReportFrequency: 1m0s
|
||||
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
|
@ -1,17 +1,21 @@
|
||||
CACertPath: /etc/kubernetes/pki/ca.crt
|
||||
ClusterName: kubernetes
|
||||
DiscoveryFile: ""
|
||||
DiscoveryTimeout: 5m0s
|
||||
DiscoveryToken: abcdef.0123456789abcdef
|
||||
DiscoveryTokenAPIServers:
|
||||
- kube-apiserver:6443
|
||||
DiscoveryTokenCACertHashes: null
|
||||
DiscoveryTokenUnsafeSkipCAVerification: true
|
||||
FeatureGates: null
|
||||
ControlPlane:
|
||||
LocalAPIEndpoint:
|
||||
AdvertiseAddress: 192.168.2.2
|
||||
BindPort: 6443
|
||||
Discovery:
|
||||
BootstrapToken:
|
||||
APIServerEndpoint: kube-apiserver:6443
|
||||
CACertHashes: null
|
||||
Token: abcdef.0123456789abcdef
|
||||
UnsafeSkipCAVerification: true
|
||||
File: null
|
||||
TLSBootstrapToken: abcdef.0123456789abcdef
|
||||
Timeout: 5m0s
|
||||
NodeRegistration:
|
||||
CRISocket: /var/run/dockershim.sock
|
||||
KubeletExtraArgs: null
|
||||
Name: master-1
|
||||
Taints: null
|
||||
TLSBootstrapToken: abcdef.0123456789abcdef
|
||||
Token: abcdef.0123456789abcdef
|
||||
Taints:
|
||||
- effect: NoSchedule
|
||||
key: node-role.kubernetes.io/master
|
||||
|
@ -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 +1,21 @@
|
||||
apiVersion: kubeadm.k8s.io/v1alpha2
|
||||
apiEndpoint:
|
||||
advertiseAddress: 192.168.2.2
|
||||
bindPort: 6443
|
||||
apiVersion: kubeadm.k8s.io/v1alpha3
|
||||
caCertPath: /etc/kubernetes/pki/ca.crt
|
||||
clusterName: kubernetes
|
||||
controlPlane: true
|
||||
discoveryFile: ""
|
||||
discoveryTimeout: 5m0s
|
||||
discoveryToken: abcdef.0123456789abcdef
|
||||
discoveryTokenAPIServers:
|
||||
- kube-apiserver:6443
|
||||
discoveryTokenUnsafeSkipCAVerification: true
|
||||
kind: NodeConfiguration
|
||||
kind: JoinConfiguration
|
||||
nodeRegistration:
|
||||
criSocket: /var/run/dockershim.sock
|
||||
name: master-1
|
||||
taints:
|
||||
- effect: NoSchedule
|
||||
key: node-role.kubernetes.io/master
|
||||
tlsBootstrapToken: abcdef.0123456789abcdef
|
||||
token: abcdef.0123456789abcdef
|
20
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/testdata/conversion/node/v1beta1.yaml
generated
vendored
Normal file
20
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/config/testdata/conversion/node/v1beta1.yaml
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
apiVersion: kubeadm.k8s.io/v1beta1
|
||||
caCertPath: /etc/kubernetes/pki/ca.crt
|
||||
controlPlane:
|
||||
localAPIEndpoint:
|
||||
advertiseAddress: 192.168.2.2
|
||||
bindPort: 6443
|
||||
discovery:
|
||||
bootstrapToken:
|
||||
apiServerEndpoint: kube-apiserver:6443
|
||||
token: abcdef.0123456789abcdef
|
||||
unsafeSkipCAVerification: true
|
||||
timeout: 5m0s
|
||||
tlsBootstrapToken: abcdef.0123456789abcdef
|
||||
kind: JoinConfiguration
|
||||
nodeRegistration:
|
||||
criSocket: /var/run/dockershim.sock
|
||||
name: master-1
|
||||
taints:
|
||||
- effect: NoSchedule
|
||||
key: node-role.kubernetes.io/master
|
@ -1,12 +1,4 @@
|
||||
api:
|
||||
advertiseAddress: 192.168.2.2
|
||||
bindPort: 6443
|
||||
controlPlaneEndpoint: ""
|
||||
apiVersion: kubeadm.k8s.io/v1alpha2
|
||||
auditPolicy:
|
||||
logDir: /var/log/kubernetes/audit
|
||||
logMaxAge: 2
|
||||
path: ""
|
||||
apiVersion: kubeadm.k8s.io/v1beta1
|
||||
bootstrapTokens:
|
||||
- groups:
|
||||
- system:bootstrappers:kubeadm:default-node-token
|
||||
@ -15,129 +7,146 @@ bootstrapTokens:
|
||||
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
|
||||
kind: InitConfiguration
|
||||
localAPIEndpoint:
|
||||
advertiseAddress: 192.168.2.2
|
||||
bindPort: 6443
|
||||
nodeRegistration:
|
||||
criSocket: /var/run/criruntime.sock
|
||||
name: master-1
|
||||
taints:
|
||||
- effect: NoSchedule
|
||||
key: node-role.kubernetes.io/master
|
||||
unifiedControlPlaneImage: ""
|
||||
---
|
||||
apiServer:
|
||||
timeoutForControlPlane: 4m0s
|
||||
apiVersion: kubeadm.k8s.io/v1beta1
|
||||
certificatesDir: /var/lib/kubernetes/pki
|
||||
clusterName: kubernetes
|
||||
controlPlaneEndpoint: ""
|
||||
controllerManager: {}
|
||||
dns:
|
||||
type: CoreDNS
|
||||
etcd:
|
||||
local:
|
||||
dataDir: /var/lib/etcd
|
||||
imageRepository: my-company.com
|
||||
kind: ClusterConfiguration
|
||||
kubernetesVersion: v1.13.0
|
||||
networking:
|
||||
dnsDomain: cluster.global
|
||||
podSubnet: 10.148.0.0/16
|
||||
serviceSubnet: 10.196.0.0/12
|
||||
scheduler: {}
|
||||
---
|
||||
apiVersion: kubeproxy.config.k8s.io/v1alpha1
|
||||
bindAddress: 0.0.0.0
|
||||
clientConnection:
|
||||
acceptContentTypes: ""
|
||||
burst: 10
|
||||
contentType: application/vnd.kubernetes.protobuf
|
||||
kubeconfig: /var/lib/kube-proxy/kubeconfig.conf
|
||||
qps: 5
|
||||
clusterCIDR: 10.148.0.0/16
|
||||
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
|
||||
kind: KubeProxyConfiguration
|
||||
metricsBindAddress: 127.0.0.1:10249
|
||||
mode: ""
|
||||
nodePortAddresses: null
|
||||
oomScoreAdj: -999
|
||||
portRange: ""
|
||||
resourceContainer: /kube-proxy
|
||||
udpIdleTimeout: 250ms
|
||||
---
|
||||
address: 0.0.0.0
|
||||
apiVersion: kubelet.config.k8s.io/v1beta1
|
||||
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
|
||||
configMapAndSecretChangeDetectionStrategy: Watch
|
||||
containerLogMaxFiles: 5
|
||||
containerLogMaxSize: 10Mi
|
||||
contentType: application/vnd.kubernetes.protobuf
|
||||
cpuCFSQuota: true
|
||||
cpuCFSQuotaPeriod: 100ms
|
||||
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
|
||||
kind: KubeletConfiguration
|
||||
kubeAPIBurst: 10
|
||||
kubeAPIQPS: 5
|
||||
makeIPTablesUtilChains: true
|
||||
maxOpenFiles: 1000000
|
||||
maxPods: 110
|
||||
nodeLeaseDurationSeconds: 40
|
||||
nodeStatusReportFrequency: 1m0s
|
||||
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
|
||||
|
@ -1,15 +1,19 @@
|
||||
# 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:
|
||||
apiVersion: kubeadm.k8s.io/v1beta1
|
||||
kind: InitConfiguration
|
||||
bootstrapTokens:
|
||||
- token: s73ybu.6tw6wnqgp5z0wb77
|
||||
localAPIEndpoint:
|
||||
advertiseAddress: 192.168.2.2
|
||||
bindPort: 6443
|
||||
nodeRegistration:
|
||||
criSocket: /var/run/criruntime.sock
|
||||
name: master-1
|
||||
---
|
||||
apiVersion: kubeadm.k8s.io/v1beta1
|
||||
certificatesDir: /var/lib/kubernetes/pki
|
||||
clusterName: kubernetes
|
||||
criSocket: /var/run/criruntime.sock
|
||||
imageRepository: my-company.com
|
||||
kubernetesVersion: v1.10.2
|
||||
kind: ClusterConfiguration
|
||||
kubernetesVersion: v1.13.0
|
||||
networking:
|
||||
dnsDomain: cluster.global
|
||||
serviceSubnet: 10.196.0.0/12
|
||||
nodeName: master-1
|
||||
token: s73ybu.6tw6wnqgp5z0wb77
|
||||
podSubnet: 10.148.0.0/16
|
||||
serviceSubnet: 10.196.0.0/12
|
@ -1,15 +1,13 @@
|
||||
apiVersion: kubeadm.k8s.io/v1alpha2
|
||||
apiVersion: kubeadm.k8s.io/v1beta1
|
||||
caCertPath: /etc/kubernetes/pki/ca.crt
|
||||
clusterName: kubernetes
|
||||
discoveryFile: ""
|
||||
discoveryTimeout: 5m0s
|
||||
discoveryToken: abcdef.0123456789abcdef
|
||||
discoveryTokenAPIServers:
|
||||
- kube-apiserver:6443
|
||||
discoveryTokenUnsafeSkipCAVerification: true
|
||||
kind: NodeConfiguration
|
||||
discovery:
|
||||
bootstrapToken:
|
||||
apiServerEndpoint: kube-apiserver:6443
|
||||
token: abcdef.0123456789abcdef
|
||||
unsafeSkipCAVerification: true
|
||||
timeout: 5m0s
|
||||
tlsBootstrapToken: abcdef.0123456789abcdef
|
||||
kind: JoinConfiguration
|
||||
nodeRegistration:
|
||||
criSocket: /var/run/dockershim.sock
|
||||
name: thegopher
|
||||
tlsBootstrapToken: abcdef.0123456789abcdef
|
||||
token: abcdef.0123456789abcdef
|
||||
|
@ -1,7 +1,11 @@
|
||||
apiVersion: kubeadm.k8s.io/v1alpha1
|
||||
kind: NodeConfiguration
|
||||
apiVersion: kubeadm.k8s.io/v1alpha3
|
||||
discoveryToken: abcdef.0123456789abcdef
|
||||
discoveryTokenAPIServers:
|
||||
- kube-apiserver:6443
|
||||
discoveryTokenUnsafeSkipCAVerification: true
|
||||
nodeName: thegopher
|
||||
kind: JoinConfiguration
|
||||
nodeRegistration:
|
||||
criSocket: /var/run/dockershim.sock
|
||||
name: thegopher
|
||||
tlsBootstrapToken: abcdef.0123456789abcdef
|
||||
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 +1,7 @@
|
||||
api:
|
||||
bindPort: 0
|
||||
certificatesDir: relativepath
|
||||
clusterName: kubernetes
|
||||
criSocket: relativepath
|
||||
imageRepository: my-company.com
|
||||
kubernetesVersion: v1.10.2
|
||||
apiVersion: kubeadm.k8s.io/v1beta1
|
||||
kind: ClusterConfiguration
|
||||
networking:
|
||||
dnsDomain: cluster.GLOBAL
|
||||
serviceSubnet: 10.196.1000.0/100
|
||||
nodeName: MASTER
|
||||
token: s7bu.6tw6wn
|
||||
dnsDomain: INVALID-DOMAIN-!!!!
|
||||
podSubnet: ""
|
||||
serviceSubnet: 10.96.0.0/12
|
||||
useHyperKubeImage: false
|
@ -1,11 +1,15 @@
|
||||
apiVersion: kubeadm.k8s.io/v1alpha1
|
||||
apiVersion: kubeadm.k8s.io/v1beta1
|
||||
kind: NodeConfiguration
|
||||
caCertPath: relativepath
|
||||
criSocket: relativepath
|
||||
discoveryFile: relativepath
|
||||
discoveryTimeout: not-a-time
|
||||
discoveryTokenAPIServers:
|
||||
- INVALID_URL
|
||||
discoveryTokenUnsafeSkipCAVerification: false
|
||||
nodeName: NODE-1
|
||||
token: invalidtoken
|
||||
discovery:
|
||||
timeout: not-a-time
|
||||
bootstrapToken:
|
||||
token: invalidtoken
|
||||
apiServerEndpoints:
|
||||
- INVALID_URL
|
||||
unsafeSkipCAVerification: false
|
||||
file:
|
||||
kubeConfigPath: relativepath
|
||||
nodeRegistration:
|
||||
criSocket: relativepath
|
||||
name: NODE-1
|
||||
|
4
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/dryrun/BUILD
generated
vendored
4
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/dryrun/BUILD
generated
vendored
@ -8,8 +8,8 @@ go_library(
|
||||
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",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/errors:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
|
5
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/dryrun/dryrun.go
generated
vendored
5
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/dryrun/dryrun.go
generated
vendored
@ -111,6 +111,11 @@ func (w *Waiter) WaitForHealthyKubelet(_ time.Duration, healthzEndpoint string)
|
||||
return nil
|
||||
}
|
||||
|
||||
// WaitForKubeletAndFunc is a wrapper for WaitForHealthyKubelet that also blocks for a function
|
||||
func (w *Waiter) WaitForKubeletAndFunc(f func() error) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetTimeout is a no-op; we don't wait in this implementation
|
||||
func (w *Waiter) SetTimeout(_ time.Duration) {}
|
||||
|
||||
|
47
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/endpoint.go
generated
vendored
47
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/endpoint.go
generated
vendored
@ -22,47 +22,51 @@ import (
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"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) {
|
||||
// - If the ControlPlaneEndpoint is defined, use it.
|
||||
// - if the ControlPlaneEndpoint is defined but without a port number, use the ControlPlaneEndpoint + api.BindPort is used.
|
||||
// - Otherwise, in case the ControlPlaneEndpoint is not defined, use the api.AdvertiseAddress + the api.BindPort.
|
||||
func GetMasterEndpoint(cfg *kubeadmapi.InitConfiguration) (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)
|
||||
bindPortString := strconv.Itoa(int(cfg.LocalAPIEndpoint.BindPort))
|
||||
if _, err := ParsePort(bindPortString); err != nil {
|
||||
return "", errors.Wrapf(err, "invalid value %q given for api.bindPort", cfg.LocalAPIEndpoint.BindPort)
|
||||
}
|
||||
|
||||
// parse the AdvertiseAddress
|
||||
var ip = net.ParseIP(api.AdvertiseAddress)
|
||||
var ip = net.ParseIP(cfg.LocalAPIEndpoint.AdvertiseAddress)
|
||||
if ip == nil {
|
||||
return "", fmt.Errorf("invalid value `%s` given for api.advertiseAddress", api.AdvertiseAddress)
|
||||
return "", errors.Errorf("invalid value `%s` given for api.advertiseAddress", cfg.LocalAPIEndpoint.AdvertiseAddress)
|
||||
}
|
||||
|
||||
// set the master url using cfg.API.AdvertiseAddress + the cfg.API.BindPort
|
||||
masterURL := &url.URL{
|
||||
Scheme: "https",
|
||||
Host: net.JoinHostPort(ip.String(), bindPort),
|
||||
Host: net.JoinHostPort(ip.String(), bindPortString),
|
||||
}
|
||||
|
||||
// if the controlplane endpoint is defined
|
||||
if len(api.ControlPlaneEndpoint) > 0 {
|
||||
if len(cfg.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 host, port, err = ParseHostPort(cfg.ControlPlaneEndpoint); err != nil {
|
||||
return "", errors.Wrapf(err, "invalid value %q given for controlPlaneEndpoint", cfg.ControlPlaneEndpoint)
|
||||
}
|
||||
|
||||
// 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")
|
||||
if port != bindPortString {
|
||||
fmt.Println("[endpoint] WARNING: port specified in controlPlaneEndpoint overrides bindPort in the controlplane address")
|
||||
}
|
||||
} else {
|
||||
port = bindPort
|
||||
port = bindPortString
|
||||
}
|
||||
|
||||
// overrides the master url using the controlPlaneAddress (and eventually the bindport)
|
||||
@ -90,8 +94,8 @@ func ParseHostPort(hostport string) (string, string, error) {
|
||||
|
||||
// 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 _, err := ParsePort(port); err != nil {
|
||||
return "", "", errors.New("port must be a valid number between 1 and 65535, inclusive")
|
||||
}
|
||||
}
|
||||
|
||||
@ -105,15 +109,16 @@ func ParseHostPort(hostport string) (string, string, error) {
|
||||
return host, port, nil
|
||||
}
|
||||
|
||||
return "", "", fmt.Errorf("host must be a valid IP address or a valid RFC-1123 DNS subdomain")
|
||||
return "", "", errors.New("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) {
|
||||
func ParsePort(port string) (int, error) {
|
||||
portInt, err := strconv.Atoi(port)
|
||||
if err == nil && (1 <= portInt && portInt <= 65535) {
|
||||
return portInt, nil
|
||||
}
|
||||
|
||||
return 0, fmt.Errorf("port must be a valid number between 1 and 65535, inclusive")
|
||||
return 0, errors.New("port must be a valid number between 1 and 65535, inclusive")
|
||||
}
|
||||
|
146
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/endpoint_test.go
generated
vendored
146
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/endpoint_test.go
generated
vendored
@ -25,139 +25,179 @@ import (
|
||||
func TestGetMasterEndpoint(t *testing.T) {
|
||||
var tests = []struct {
|
||||
name string
|
||||
api *kubeadmapi.API
|
||||
cfg *kubeadmapi.InitConfiguration
|
||||
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",
|
||||
cfg: &kubeadmapi.InitConfiguration{
|
||||
LocalAPIEndpoint: kubeadmapi.APIEndpoint{
|
||||
BindPort: 4567,
|
||||
AdvertiseAddress: "4.5.6.7",
|
||||
},
|
||||
ClusterConfiguration: kubeadmapi.ClusterConfiguration{
|
||||
ControlPlaneEndpoint: "cp.k8s.io:1234",
|
||||
},
|
||||
},
|
||||
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",
|
||||
cfg: &kubeadmapi.InitConfiguration{
|
||||
LocalAPIEndpoint: kubeadmapi.APIEndpoint{
|
||||
BindPort: 4567,
|
||||
AdvertiseAddress: "4.5.6.7",
|
||||
},
|
||||
ClusterConfiguration: kubeadmapi.ClusterConfiguration{
|
||||
ControlPlaneEndpoint: "1.2.3.4:1234",
|
||||
},
|
||||
},
|
||||
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",
|
||||
cfg: &kubeadmapi.InitConfiguration{
|
||||
LocalAPIEndpoint: kubeadmapi.APIEndpoint{
|
||||
BindPort: 4567,
|
||||
AdvertiseAddress: "4.5.6.7",
|
||||
},
|
||||
ClusterConfiguration: kubeadmapi.ClusterConfiguration{
|
||||
ControlPlaneEndpoint: "[2001:db8::1]:1234",
|
||||
},
|
||||
},
|
||||
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",
|
||||
cfg: &kubeadmapi.InitConfiguration{
|
||||
LocalAPIEndpoint: kubeadmapi.APIEndpoint{
|
||||
BindPort: 4567,
|
||||
AdvertiseAddress: "4.5.6.7",
|
||||
},
|
||||
ClusterConfiguration: kubeadmapi.ClusterConfiguration{
|
||||
|
||||
ControlPlaneEndpoint: "cp.k8s.io",
|
||||
},
|
||||
},
|
||||
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",
|
||||
cfg: &kubeadmapi.InitConfiguration{
|
||||
LocalAPIEndpoint: kubeadmapi.APIEndpoint{
|
||||
BindPort: 4567,
|
||||
AdvertiseAddress: "4.5.6.7",
|
||||
},
|
||||
ClusterConfiguration: kubeadmapi.ClusterConfiguration{
|
||||
ControlPlaneEndpoint: "1.2.3.4",
|
||||
},
|
||||
},
|
||||
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",
|
||||
cfg: &kubeadmapi.InitConfiguration{
|
||||
LocalAPIEndpoint: kubeadmapi.APIEndpoint{
|
||||
BindPort: 4567,
|
||||
AdvertiseAddress: "4.5.6.7",
|
||||
},
|
||||
ClusterConfiguration: kubeadmapi.ClusterConfiguration{
|
||||
|
||||
ControlPlaneEndpoint: "2001:db8::1",
|
||||
},
|
||||
},
|
||||
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",
|
||||
cfg: &kubeadmapi.InitConfiguration{
|
||||
LocalAPIEndpoint: kubeadmapi.APIEndpoint{
|
||||
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",
|
||||
cfg: &kubeadmapi.InitConfiguration{
|
||||
LocalAPIEndpoint: kubeadmapi.APIEndpoint{
|
||||
BindPort: 4567,
|
||||
AdvertiseAddress: "2001:db8::1",
|
||||
},
|
||||
},
|
||||
expectedEndpoint: "https://[2001:db8::1]:4567",
|
||||
},
|
||||
{
|
||||
name: "fail if invalid BindPort",
|
||||
api: &kubeadmapi.API{
|
||||
BindPort: 0,
|
||||
cfg: &kubeadmapi.InitConfiguration{
|
||||
LocalAPIEndpoint: kubeadmapi.APIEndpoint{
|
||||
BindPort: 0,
|
||||
},
|
||||
},
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
name: "fail if invalid ControlPlaneEndpoint (dns)",
|
||||
api: &kubeadmapi.API{
|
||||
ControlPlaneEndpoint: "bad!!.cp.k8s.io",
|
||||
BindPort: 4567,
|
||||
cfg: &kubeadmapi.InitConfiguration{
|
||||
ClusterConfiguration: kubeadmapi.ClusterConfiguration{
|
||||
ControlPlaneEndpoint: "bad!!.cp.k8s.io",
|
||||
},
|
||||
},
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
name: "fail if invalid ControlPlaneEndpoint (ip4)",
|
||||
api: &kubeadmapi.API{
|
||||
ControlPlaneEndpoint: "1..0",
|
||||
BindPort: 4567,
|
||||
cfg: &kubeadmapi.InitConfiguration{
|
||||
ClusterConfiguration: kubeadmapi.ClusterConfiguration{
|
||||
ControlPlaneEndpoint: "1..0",
|
||||
},
|
||||
},
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
name: "fail if invalid ControlPlaneEndpoint (ip6)",
|
||||
api: &kubeadmapi.API{
|
||||
ControlPlaneEndpoint: "1200::AB00:1234::2552:7777:1313",
|
||||
BindPort: 4567,
|
||||
cfg: &kubeadmapi.InitConfiguration{
|
||||
ClusterConfiguration: kubeadmapi.ClusterConfiguration{
|
||||
ControlPlaneEndpoint: "1200::AB00:1234::2552:7777:1313",
|
||||
},
|
||||
},
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
name: "fail if invalid ControlPlaneEndpoint (port)",
|
||||
api: &kubeadmapi.API{
|
||||
ControlPlaneEndpoint: "cp.k8s.io:0",
|
||||
BindPort: 4567,
|
||||
cfg: &kubeadmapi.InitConfiguration{
|
||||
ClusterConfiguration: kubeadmapi.ClusterConfiguration{
|
||||
ControlPlaneEndpoint: "cp.k8s.io:0",
|
||||
},
|
||||
},
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
name: "fail if invalid AdvertiseAddress (ip4)",
|
||||
api: &kubeadmapi.API{
|
||||
AdvertiseAddress: "1..0",
|
||||
BindPort: 4567,
|
||||
cfg: &kubeadmapi.InitConfiguration{
|
||||
LocalAPIEndpoint: kubeadmapi.APIEndpoint{
|
||||
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,
|
||||
cfg: &kubeadmapi.InitConfiguration{
|
||||
LocalAPIEndpoint: kubeadmapi.APIEndpoint{
|
||||
AdvertiseAddress: "1200::AB00:1234::2552:7777:1313",
|
||||
BindPort: 4567,
|
||||
},
|
||||
},
|
||||
expectedError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, rt := range tests {
|
||||
actualEndpoint, actualError := GetMasterEndpoint(rt.api)
|
||||
actualEndpoint, actualError := GetMasterEndpoint(rt.cfg)
|
||||
|
||||
if (actualError != nil) && !rt.expectedError {
|
||||
t.Errorf("%s unexpected failure: %v", rt.name, actualError)
|
||||
@ -328,7 +368,7 @@ func TestParsePort(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, rt := range tests {
|
||||
actualPort, actualError := parsePort(rt.port)
|
||||
actualPort, actualError := ParsePort(rt.port)
|
||||
|
||||
if (actualError != nil) && !rt.expectedError {
|
||||
t.Errorf("%s unexpected failure: %v", rt.name, actualError)
|
||||
|
10
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/error_test.go
generated
vendored
10
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/error_test.go
generated
vendored
@ -17,7 +17,7 @@ limitations under the License.
|
||||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/pkg/errors"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@ -36,7 +36,7 @@ func TestCheckErr(t *testing.T) {
|
||||
expected int
|
||||
}{
|
||||
{nil, 0},
|
||||
{fmt.Errorf(""), DefaultErrorExitCode},
|
||||
{errors.New(""), DefaultErrorExitCode},
|
||||
{&pferror{}, PreFlightExitCode},
|
||||
}
|
||||
|
||||
@ -63,14 +63,14 @@ func TestFormatErrMsg(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
errs: []error{
|
||||
fmt.Errorf(errMsg1),
|
||||
fmt.Errorf(errMsg2),
|
||||
errors.New(errMsg1),
|
||||
errors.New(errMsg2),
|
||||
},
|
||||
expect: "\t- " + errMsg1 + "\n" + "\t- " + errMsg2 + "\n",
|
||||
},
|
||||
{
|
||||
errs: []error{
|
||||
fmt.Errorf(errMsg1),
|
||||
errors.New(errMsg1),
|
||||
},
|
||||
expect: "\t- " + errMsg1 + "\n",
|
||||
},
|
||||
|
5
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/etcd/BUILD
generated
vendored
5
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/etcd/BUILD
generated
vendored
@ -8,9 +8,13 @@ go_library(
|
||||
deps = [
|
||||
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
|
||||
"//cmd/kubeadm/app/constants:go_default_library",
|
||||
"//cmd/kubeadm/app/util/config:go_default_library",
|
||||
"//cmd/kubeadm/app/util/staticpod:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/kubernetes:go_default_library",
|
||||
"//vendor/github.com/coreos/etcd/clientv3:go_default_library",
|
||||
"//vendor/github.com/coreos/etcd/pkg/transport:go_default_library",
|
||||
"//vendor/github.com/pkg/errors:go_default_library",
|
||||
"//vendor/k8s.io/klog:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
@ -20,6 +24,7 @@ go_test(
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
|
||||
"//cmd/kubeadm/app/constants:go_default_library",
|
||||
"//cmd/kubeadm/test:go_default_library",
|
||||
],
|
||||
)
|
||||
|
193
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/etcd/etcd.go
generated
vendored
193
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/etcd/etcd.go
generated
vendored
@ -20,14 +20,20 @@ import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/etcd/clientv3"
|
||||
"github.com/coreos/etcd/pkg/transport"
|
||||
"github.com/pkg/errors"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
"k8s.io/klog"
|
||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/util/config"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/util/staticpod"
|
||||
)
|
||||
|
||||
@ -39,6 +45,8 @@ type ClusterInterrogator interface {
|
||||
GetVersion() (string, error)
|
||||
HasTLS() bool
|
||||
WaitForClusterAvailable(delay time.Duration, retries int, retryInterval time.Duration) (bool, error)
|
||||
Sync() error
|
||||
AddMember(name string, peerAddrs string) ([]Member, error)
|
||||
}
|
||||
|
||||
// Client provides connection parameters for an etcd cluster
|
||||
@ -58,7 +66,7 @@ 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)
|
||||
return false, errors.Wrap(err, "failed to check if etcd pod implements TLS")
|
||||
}
|
||||
|
||||
tlsFlags := []string{
|
||||
@ -107,21 +115,154 @@ func New(endpoints []string, ca, cert, key string) (*Client, error) {
|
||||
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)
|
||||
// NewFromCluster creates an etcd client for the the etcd endpoints defined in the ClusterStatus value stored in
|
||||
// the kubeadm-config ConfigMap in kube-system namespace.
|
||||
// Once created, the client synchronizes client's endpoints with the known endpoints from the etcd membership API (reality check).
|
||||
func NewFromCluster(client clientset.Interface, certificatesDir string) (*Client, error) {
|
||||
|
||||
// Kubeadm v1.13 should manage v1.12 clusters and v1.13 clusters
|
||||
// v1.12 clusters can be have etcd listening on localhost only (if the cluster was created with kubeadm v1.12)
|
||||
// or etcd listening on localhost and API server advertise address (if the cluster was created with kubeadm v1.13).
|
||||
// The first case should be dropped in v1.14 when support for v1.12 clusters can be removed from the codebase.
|
||||
|
||||
// Detect which type of etcd we are dealing with
|
||||
// Please note that this test can be executed only on master nodes during upgrades;
|
||||
// For nodes where we are joining a new control plane node instead we should tolerate that the etcd manifest does not
|
||||
// exists and try to connect to etcd using API server advertise address; as described above this will lead to a know isse
|
||||
// for cluster created with v1.12, but a documented workaround will be provided
|
||||
oldManifest := false
|
||||
klog.V(1).Infoln("checking etcd manifest")
|
||||
|
||||
etcdManifestFile := constants.GetStaticPodFilepath(constants.Etcd, constants.GetStaticPodDirectory())
|
||||
etcdPod, err := staticpod.ReadStaticPodFromDisk(etcdManifestFile)
|
||||
if err == nil {
|
||||
etcdContainer := etcdPod.Spec.Containers[0]
|
||||
for _, arg := range etcdContainer.Command {
|
||||
if arg == "--listen-client-urls=https://127.0.0.1:2379" {
|
||||
klog.V(1).Infoln("etcd manifest created by kubeadm v1.12")
|
||||
oldManifest = true
|
||||
}
|
||||
}
|
||||
|
||||
// if etcd is listening on localhost only
|
||||
if oldManifest == true {
|
||||
// etcd cluster has a single member "by design"
|
||||
endpoints := []string{fmt.Sprintf("localhost:%d", constants.EtcdListenClientPort)}
|
||||
|
||||
etcdClient, err := New(
|
||||
endpoints,
|
||||
filepath.Join(certificatesDir, constants.EtcdCACertName),
|
||||
filepath.Join(certificatesDir, constants.EtcdHealthcheckClientCertName),
|
||||
filepath.Join(certificatesDir, constants.EtcdHealthcheckClientKeyName),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "error creating etcd client for %v endpoint", endpoints)
|
||||
}
|
||||
|
||||
return etcdClient, nil
|
||||
}
|
||||
}
|
||||
|
||||
// etcd is listening on localhost and API server advertise address, and
|
||||
// the etcd cluster can have more than one etcd members, so it is necessary to get the
|
||||
// list of endpoints from kubeadm cluster status before connecting
|
||||
|
||||
// Gets the cluster status
|
||||
clusterStatus, err := config.GetClusterStatus(client)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not read manifests from: %s, error: %v", manifestDir, err)
|
||||
return nil, err
|
||||
}
|
||||
if hasTLS {
|
||||
return New(
|
||||
endpoints,
|
||||
filepath.Join(certificatesDir, constants.EtcdCACertName),
|
||||
filepath.Join(certificatesDir, constants.EtcdHealthcheckClientCertName),
|
||||
filepath.Join(certificatesDir, constants.EtcdHealthcheckClientKeyName),
|
||||
)
|
||||
|
||||
// Get the list of etcd endpoints from cluster status
|
||||
endpoints := []string{}
|
||||
for _, e := range clusterStatus.APIEndpoints {
|
||||
endpoints = append(endpoints, GetClientURLByIP(e.AdvertiseAddress))
|
||||
}
|
||||
return New(endpoints, "", "", "")
|
||||
klog.V(1).Infof("etcd endpoints read from pods: %s", strings.Join(endpoints, ","))
|
||||
|
||||
// Creates an etcd client
|
||||
etcdClient, err := New(
|
||||
endpoints,
|
||||
filepath.Join(certificatesDir, constants.EtcdCACertName),
|
||||
filepath.Join(certificatesDir, constants.EtcdHealthcheckClientCertName),
|
||||
filepath.Join(certificatesDir, constants.EtcdHealthcheckClientKeyName),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "error creating etcd client for %v endpoints", endpoints)
|
||||
}
|
||||
|
||||
// synchronizes client's endpoints with the known endpoints from the etcd membership.
|
||||
err = etcdClient.Sync()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error syncing endpoints with etc")
|
||||
}
|
||||
|
||||
return etcdClient, nil
|
||||
}
|
||||
|
||||
// Sync synchronizes client's endpoints with the known endpoints from the etcd membership.
|
||||
func (c Client) Sync() error {
|
||||
cli, err := clientv3.New(clientv3.Config{
|
||||
Endpoints: c.Endpoints,
|
||||
DialTimeout: 20 * time.Second,
|
||||
TLS: c.TLS,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cli.Close()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
err = cli.Sync(ctx)
|
||||
cancel()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
klog.V(1).Infof("etcd endpoints read from etcd: %s", strings.Join(cli.Endpoints(), ","))
|
||||
|
||||
c.Endpoints = cli.Endpoints()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Member struct defines an etcd member; it is used for avoiding to spread github.com/coreos/etcd dependency
|
||||
// across kubeadm codebase
|
||||
type Member struct {
|
||||
Name string
|
||||
PeerURL string
|
||||
}
|
||||
|
||||
// AddMember notifies an existing etcd cluster that a new member is joining
|
||||
func (c Client) AddMember(name string, peerAddrs string) ([]Member, error) {
|
||||
cli, err := clientv3.New(clientv3.Config{
|
||||
Endpoints: c.Endpoints,
|
||||
DialTimeout: 20 * time.Second,
|
||||
TLS: c.TLS,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer cli.Close()
|
||||
|
||||
// Adds a new member to the cluster
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
resp, err := cli.MemberAdd(ctx, []string{peerAddrs})
|
||||
cancel()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Returns the updated list of etcd members
|
||||
ret := []Member{}
|
||||
for _, m := range resp.Members {
|
||||
// fixes the entry for the joining member (that doesn't have a name set in the initialCluster returned by etcd)
|
||||
if m.Name == "" {
|
||||
ret = append(ret, Member{Name: name, PeerURL: m.PeerURLs[0]})
|
||||
} else {
|
||||
ret = append(ret, Member{Name: m.Name, PeerURL: m.PeerURLs[0]})
|
||||
}
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// GetVersion returns the etcd version of the cluster.
|
||||
@ -135,12 +276,12 @@ func (c Client) GetVersion() (string, error) {
|
||||
}
|
||||
for _, v := range versions {
|
||||
if clusterVersion != "" && clusterVersion != v {
|
||||
return "", fmt.Errorf("etcd cluster contains endpoints with mismatched versions: %v", versions)
|
||||
return "", errors.Errorf("etcd cluster contains endpoints with mismatched versions: %v", versions)
|
||||
}
|
||||
clusterVersion = v
|
||||
}
|
||||
if clusterVersion == "" {
|
||||
return "", fmt.Errorf("could not determine cluster etcd version")
|
||||
return "", errors.New("could not determine cluster etcd version")
|
||||
}
|
||||
return clusterVersion, nil
|
||||
}
|
||||
@ -215,10 +356,28 @@ func (c Client) WaitForClusterAvailable(delay time.Duration, retries int, retryI
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
return false, fmt.Errorf("timeout waiting for etcd cluster to be available")
|
||||
return false, errors.New("timeout waiting for etcd cluster to be available")
|
||||
}
|
||||
|
||||
// CheckConfigurationIsHA returns true if the given MasterConfiguration etcd block appears to be an HA configuration.
|
||||
// CheckConfigurationIsHA returns true if the given InitConfiguration etcd block appears to be an HA configuration.
|
||||
func CheckConfigurationIsHA(cfg *kubeadmapi.Etcd) bool {
|
||||
return cfg.External != nil && len(cfg.External.Endpoints) > 1
|
||||
}
|
||||
|
||||
// GetClientURL creates an HTTPS URL that uses the configured advertise
|
||||
// address and client port for the API controller
|
||||
func GetClientURL(cfg *kubeadmapi.InitConfiguration) string {
|
||||
return "https://" + net.JoinHostPort(cfg.LocalAPIEndpoint.AdvertiseAddress, strconv.Itoa(constants.EtcdListenClientPort))
|
||||
}
|
||||
|
||||
// GetPeerURL creates an HTTPS URL that uses the configured advertise
|
||||
// address and peer port for the API controller
|
||||
func GetPeerURL(cfg *kubeadmapi.InitConfiguration) string {
|
||||
return "https://" + net.JoinHostPort(cfg.LocalAPIEndpoint.AdvertiseAddress, strconv.Itoa(constants.EtcdListenPeerPort))
|
||||
}
|
||||
|
||||
// GetClientURLByIP creates an HTTPS URL based on an IP address
|
||||
// and the client listening port.
|
||||
func GetClientURLByIP(ip string) string {
|
||||
return "https://" + net.JoinHostPort(ip, strconv.Itoa(constants.EtcdListenClientPort))
|
||||
}
|
||||
|
90
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/etcd/etcd_test.go
generated
vendored
90
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/etcd/etcd_test.go
generated
vendored
@ -17,12 +17,15 @@ limitations under the License.
|
||||
package etcd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||
testutil "k8s.io/kubernetes/cmd/kubeadm/test"
|
||||
)
|
||||
|
||||
@ -308,3 +311,90 @@ func TestCheckConfigurationIsHA(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func testGetURL(t *testing.T, getURLFunc func(*kubeadmapi.InitConfiguration) string, port int) {
|
||||
portStr := strconv.Itoa(port)
|
||||
var tests = []struct {
|
||||
name string
|
||||
advertiseAddress string
|
||||
expectedURL string
|
||||
}{
|
||||
{
|
||||
name: "IPv4",
|
||||
advertiseAddress: "10.10.10.10",
|
||||
expectedURL: fmt.Sprintf("https://10.10.10.10:%s", portStr),
|
||||
},
|
||||
{
|
||||
name: "IPv6",
|
||||
advertiseAddress: "2001:db8::2",
|
||||
expectedURL: fmt.Sprintf("https://[2001:db8::2]:%s", portStr),
|
||||
},
|
||||
{
|
||||
name: "IPv4 localhost",
|
||||
advertiseAddress: "127.0.0.1",
|
||||
expectedURL: fmt.Sprintf("https://127.0.0.1:%s", portStr),
|
||||
},
|
||||
{
|
||||
name: "IPv6 localhost",
|
||||
advertiseAddress: "::1",
|
||||
expectedURL: fmt.Sprintf("https://[::1]:%s", portStr),
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
cfg := &kubeadmapi.InitConfiguration{
|
||||
LocalAPIEndpoint: kubeadmapi.APIEndpoint{
|
||||
AdvertiseAddress: test.advertiseAddress,
|
||||
},
|
||||
}
|
||||
url := getURLFunc(cfg)
|
||||
if url != test.expectedURL {
|
||||
t.Errorf("expected %s, got %s", test.expectedURL, url)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetClientURL(t *testing.T) {
|
||||
testGetURL(t, GetClientURL, constants.EtcdListenClientPort)
|
||||
}
|
||||
|
||||
func TestGetPeerURL(t *testing.T) {
|
||||
testGetURL(t, GetClientURL, constants.EtcdListenClientPort)
|
||||
}
|
||||
|
||||
func TestGetClientURLByIP(t *testing.T) {
|
||||
portStr := strconv.Itoa(constants.EtcdListenClientPort)
|
||||
var tests = []struct {
|
||||
name string
|
||||
ip string
|
||||
expectedURL string
|
||||
}{
|
||||
{
|
||||
name: "IPv4",
|
||||
ip: "10.10.10.10",
|
||||
expectedURL: fmt.Sprintf("https://10.10.10.10:%s", portStr),
|
||||
},
|
||||
{
|
||||
name: "IPv6",
|
||||
ip: "2001:db8::2",
|
||||
expectedURL: fmt.Sprintf("https://[2001:db8::2]:%s", portStr),
|
||||
},
|
||||
{
|
||||
name: "IPv4 localhost",
|
||||
ip: "127.0.0.1",
|
||||
expectedURL: fmt.Sprintf("https://127.0.0.1:%s", portStr),
|
||||
},
|
||||
{
|
||||
name: "IPv6 localhost",
|
||||
ip: "::1",
|
||||
expectedURL: fmt.Sprintf("https://[::1]:%s", portStr),
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
url := GetClientURLByIP(test.ip)
|
||||
if url != test.expectedURL {
|
||||
t.Errorf("expected %s, got %s", test.expectedURL, url)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
7
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig/BUILD
generated
vendored
7
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig/BUILD
generated
vendored
@ -17,9 +17,10 @@ go_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",
|
||||
"//staging/src/k8s.io/client-go/kubernetes:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/tools/clientcmd:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/tools/clientcmd/api:go_default_library",
|
||||
"//vendor/github.com/pkg/errors:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
|
14
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig/kubeconfig.go
generated
vendored
14
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig/kubeconfig.go
generated
vendored
@ -22,17 +22,19 @@ import (
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// CreateBasic creates a basic, general KubeConfig object that then can be extended
|
||||
func CreateBasic(serverURL string, clusterName string, userName string, caCert []byte) *clientcmdapi.Config {
|
||||
func CreateBasic(serverURL, clusterName, 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,
|
||||
Server: serverURL,
|
||||
CertificateAuthorityData: caCert,
|
||||
},
|
||||
},
|
||||
@ -66,11 +68,11 @@ func CreateWithToken(serverURL, clusterName, userName string, caCert []byte, tok
|
||||
return config
|
||||
}
|
||||
|
||||
// ClientSetFromFile returns a ready-to-use client from a KubeConfig file
|
||||
// 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 nil, errors.Wrap(err, "failed to load admin kubeconfig")
|
||||
}
|
||||
return ToClientSet(config)
|
||||
}
|
||||
@ -79,12 +81,12 @@ func ClientSetFromFile(path string) (*clientset.Clientset, error) {
|
||||
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)
|
||||
return nil, errors.Wrap(err, "failed to create API client configuration from kubeconfig")
|
||||
}
|
||||
|
||||
client, err := clientset.NewForConfig(clientConfig)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create API client: %v", err)
|
||||
return nil, errors.Wrap(err, "failed to create API client")
|
||||
}
|
||||
return client, nil
|
||||
}
|
||||
|
163
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/marshal.go
generated
vendored
163
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/marshal.go
generated
vendored
@ -17,16 +17,20 @@ limitations under the License.
|
||||
package util
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"bufio"
|
||||
"bytes"
|
||||
"io"
|
||||
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
pkgerrors "github.com/pkg/errors"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
"k8s.io/apimachinery/pkg/util/errors"
|
||||
utilyaml "k8s.io/apimachinery/pkg/util/yaml"
|
||||
clientsetscheme "k8s.io/client-go/kubernetes/scheme"
|
||||
kubeadmapiv1alpha1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||
)
|
||||
|
||||
// MarshalToYaml marshals an object into yaml.
|
||||
@ -41,7 +45,7 @@ func MarshalToYamlForCodecs(obj runtime.Object, gv schema.GroupVersion, codecs s
|
||||
mediaType := "application/yaml"
|
||||
info, ok := runtime.SerializerInfoForMediaType(codecs.SupportedMediaTypes(), mediaType)
|
||||
if !ok {
|
||||
return []byte{}, fmt.Errorf("unsupported media type %q", mediaType)
|
||||
return []byte{}, pkgerrors.Errorf("unsupported media type %q", mediaType)
|
||||
}
|
||||
|
||||
encoder := codecs.EncoderForVersion(info.Serializer, gv)
|
||||
@ -60,75 +64,100 @@ func UnmarshalFromYamlForCodecs(buffer []byte, gv schema.GroupVersion, codecs se
|
||||
mediaType := "application/yaml"
|
||||
info, ok := runtime.SerializerInfoForMediaType(codecs.SupportedMediaTypes(), mediaType)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unsupported media type %q", mediaType)
|
||||
return nil, pkgerrors.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)
|
||||
// SplitYAMLDocuments reads the YAML bytes per-document, unmarshals the TypeMeta information from each document
|
||||
// and returns a map between the GroupVersionKind of the document and the document bytes
|
||||
func SplitYAMLDocuments(yamlBytes []byte) (map[schema.GroupVersionKind][]byte, error) {
|
||||
gvkmap := map[schema.GroupVersionKind][]byte{}
|
||||
knownKinds := map[string]bool{}
|
||||
errs := []error{}
|
||||
buf := bytes.NewBuffer(yamlBytes)
|
||||
reader := utilyaml.NewYAMLReader(bufio.NewReader(buf))
|
||||
for {
|
||||
typeMetaInfo := runtime.TypeMeta{}
|
||||
// Read one YAML document at a time, until io.EOF is returned
|
||||
b, err := reader.Read()
|
||||
if err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m2
|
||||
case []interface{}:
|
||||
for i, v := range x {
|
||||
x[i] = convert(v)
|
||||
if len(b) == 0 {
|
||||
break
|
||||
}
|
||||
// Deserialize the TypeMeta information of this byte slice
|
||||
if err := yaml.Unmarshal(b, &typeMetaInfo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Require TypeMeta information to be present
|
||||
if len(typeMetaInfo.APIVersion) == 0 || len(typeMetaInfo.Kind) == 0 {
|
||||
errs = append(errs, pkgerrors.New("invalid configuration: kind and apiVersion is mandatory information that needs to be specified in all YAML documents"))
|
||||
continue
|
||||
}
|
||||
// Check whether the kind has been registered before. If it has, throw an error
|
||||
if known := knownKinds[typeMetaInfo.Kind]; known {
|
||||
errs = append(errs, pkgerrors.Errorf("invalid configuration: kind %q is specified twice in YAML file", typeMetaInfo.Kind))
|
||||
continue
|
||||
}
|
||||
knownKinds[typeMetaInfo.Kind] = true
|
||||
|
||||
// Build a GroupVersionKind object from the deserialized TypeMeta object
|
||||
gv, err := schema.ParseGroupVersion(typeMetaInfo.APIVersion)
|
||||
if err != nil {
|
||||
errs = append(errs, pkgerrors.Wrap(err, "unable to parse apiVersion"))
|
||||
continue
|
||||
}
|
||||
gvk := gv.WithKind(typeMetaInfo.Kind)
|
||||
|
||||
// Save the mapping between the gvk and the bytes that object consists of
|
||||
gvkmap[gvk] = b
|
||||
}
|
||||
if err := errors.NewAggregate(errs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return gvkmap, nil
|
||||
}
|
||||
|
||||
// GroupVersionKindsFromBytes parses the bytes and returns a gvk slice
|
||||
func GroupVersionKindsFromBytes(b []byte) ([]schema.GroupVersionKind, error) {
|
||||
gvkmap, err := SplitYAMLDocuments(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gvks := []schema.GroupVersionKind{}
|
||||
for gvk := range gvkmap {
|
||||
gvks = append(gvks, gvk)
|
||||
}
|
||||
return gvks, nil
|
||||
}
|
||||
|
||||
// GroupVersionKindsHasKind returns whether the following gvk slice contains the kind given as a parameter
|
||||
func GroupVersionKindsHasKind(gvks []schema.GroupVersionKind, kind string) bool {
|
||||
for _, gvk := range gvks {
|
||||
if gvk.Kind == kind {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return i
|
||||
return false
|
||||
}
|
||||
|
||||
// GroupVersionKindsHasClusterConfiguration returns whether the following gvk slice contains a ClusterConfiguration object
|
||||
func GroupVersionKindsHasClusterConfiguration(gvks ...schema.GroupVersionKind) bool {
|
||||
return GroupVersionKindsHasKind(gvks, constants.ClusterConfigurationKind)
|
||||
}
|
||||
|
||||
// GroupVersionKindsHasInitConfiguration returns whether the following gvk slice contains a InitConfiguration object
|
||||
func GroupVersionKindsHasInitConfiguration(gvks ...schema.GroupVersionKind) bool {
|
||||
return GroupVersionKindsHasKind(gvks, constants.InitConfigurationKind)
|
||||
}
|
||||
|
||||
// GroupVersionKindsHasJoinConfiguration returns whether the following gvk slice contains a JoinConfiguration object
|
||||
func GroupVersionKindsHasJoinConfiguration(gvks ...schema.GroupVersionKind) bool {
|
||||
return GroupVersionKindsHasKind(gvks, constants.JoinConfigurationKind)
|
||||
}
|
||||
|
354
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/marshal_test.go
generated
vendored
354
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/marshal_test.go
generated
vendored
@ -17,15 +17,49 @@ limitations under the License.
|
||||
package util
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"reflect"
|
||||
"sort"
|
||||
"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"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
kubeadmapiv1beta1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta1"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||
)
|
||||
|
||||
var files = map[string][]byte{
|
||||
"foo": []byte(`
|
||||
kind: Foo
|
||||
apiVersion: foo.k8s.io/v1
|
||||
fooField: foo
|
||||
`),
|
||||
"bar": []byte(`
|
||||
apiVersion: bar.k8s.io/v2
|
||||
barField: bar
|
||||
kind: Bar
|
||||
`),
|
||||
"baz": []byte(`
|
||||
apiVersion: baz.k8s.io/v1
|
||||
kind: Baz
|
||||
baz:
|
||||
foo: bar
|
||||
`),
|
||||
"nokind": []byte(`
|
||||
apiVersion: baz.k8s.io/v1
|
||||
foo: foo
|
||||
bar: bar
|
||||
`),
|
||||
"noapiversion": []byte(`
|
||||
kind: Bar
|
||||
foo: foo
|
||||
bar: bar
|
||||
`),
|
||||
}
|
||||
|
||||
func TestMarshalUnmarshalYaml(t *testing.T) {
|
||||
pod := &corev1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@ -75,51 +109,299 @@ func TestMarshalUnmarshalYaml(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestMarshalUnmarshalToYamlForCodecs(t *testing.T) {
|
||||
cfg := &kubeadmapiext.MasterConfiguration{
|
||||
API: kubeadmapiext.API{
|
||||
AdvertiseAddress: "10.100.0.1",
|
||||
BindPort: 4332,
|
||||
cfg := &kubeadmapiv1beta1.InitConfiguration{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: constants.InitConfigurationKind,
|
||||
APIVersion: kubeadmapiv1beta1.SchemeGroupVersion.String(),
|
||||
},
|
||||
NodeName: "testNode",
|
||||
NoTaintMaster: true,
|
||||
Networking: kubeadmapiext.Networking{
|
||||
ServiceSubnet: "10.100.0.0/24",
|
||||
PodSubnet: "10.100.1.0/24",
|
||||
NodeRegistration: kubeadmapiv1beta1.NodeRegistrationOptions{
|
||||
Name: "testNode",
|
||||
CRISocket: "/var/run/cri.sock",
|
||||
},
|
||||
BootstrapTokens: []kubeadmapiv1beta1.BootstrapToken{
|
||||
{
|
||||
Token: &kubeadmapiv1beta1.BootstrapTokenString{ID: "abcdef", Secret: "abcdef0123456789"},
|
||||
},
|
||||
},
|
||||
// NOTE: Using MarshalToYamlForCodecs and UnmarshalFromYamlForCodecs for ClusterConfiguration fields here won't work
|
||||
// by design. This is because we have a `json:"-"` annotation in order to avoid struct duplication. See the comment
|
||||
// at the kubeadmapiv1beta1.InitConfiguration definition.
|
||||
}
|
||||
|
||||
bytes, err := MarshalToYamlForCodecs(cfg, kubeadmapiext.SchemeGroupVersion, scheme.Codecs)
|
||||
kubeadmapiv1beta1.SetDefaults_InitConfiguration(cfg)
|
||||
scheme := runtime.NewScheme()
|
||||
if err := kubeadmapiv1beta1.AddToScheme(scheme); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
codecs := serializer.NewCodecFactory(scheme)
|
||||
|
||||
bytes, err := MarshalToYamlForCodecs(cfg, kubeadmapiv1beta1.SchemeGroupVersion, codecs)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error marshalling MasterConfiguration: %v", err)
|
||||
t.Fatalf("unexpected error marshalling InitConfiguration: %v", err)
|
||||
}
|
||||
t.Logf("\n%s", bytes)
|
||||
|
||||
obj, err := UnmarshalFromYamlForCodecs(bytes, kubeadmapiext.SchemeGroupVersion, scheme.Codecs)
|
||||
obj, err := UnmarshalFromYamlForCodecs(bytes, kubeadmapiv1beta1.SchemeGroupVersion, codecs)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error unmarshalling MasterConfiguration: %v", err)
|
||||
t.Fatalf("unexpected error unmarshalling InitConfiguration: %v", err)
|
||||
}
|
||||
|
||||
cfg2, ok := obj.(*kubeadmapiext.MasterConfiguration)
|
||||
if !ok {
|
||||
t.Fatal("did not get MasterConfiguration back")
|
||||
cfg2, ok := obj.(*kubeadmapiv1beta1.InitConfiguration)
|
||||
if !ok || cfg2 == nil {
|
||||
t.Fatal("did not get InitConfiguration 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)
|
||||
if !reflect.DeepEqual(*cfg, *cfg2) {
|
||||
t.Errorf("expected %v, got %v", *cfg, *cfg2)
|
||||
}
|
||||
}
|
||||
|
||||
// {{InitConfiguration kubeadm.k8s.io/v1beta1} [{<nil> nil <nil> [] []}] {testNode /var/run/cri.sock [] map[]} {10.100.0.1 4332} {0xc4200ad2c0 <nil>} {10.100.0.0/24 10.100.1.0/24 cluster.local} stable-1.11 map[] map[] map[] [] [] [] [] /etc/kubernetes/pki k8s.gcr.io { /var/log/kubernetes/audit 0x156e2f4} map[] kubernetes}
|
||||
// {{InitConfiguration kubeadm.k8s.io/v1beta1} [{<nil> &Duration{Duration:24h0m0s,} <nil> [signing authentication] [system:bootstrappers:kubeadm:default-node-token]}] {testNode /var/run/cri.sock [] map[]} {10.100.0.1 4332} {0xc4205c5260 <nil>} {10.100.0.0/24 10.100.1.0/24 cluster.local} stable-1.11 map[] map[] map[] [] [] [] [] /etc/kubernetes/pki k8s.gcr.io { /var/log/kubernetes/audit 0xc4204dd82c} map[] kubernetes}
|
||||
|
||||
// {{InitConfiguration kubeadm.k8s.io/v1beta1} [{abcdef.abcdef0123456789 nil <nil> [] []}] {testNode /var/run/cri.sock [] map[]} {10.100.0.1 4332} {0xc42012ca80 <nil>} {10.100.0.0/24 10.100.1.0/24 cluster.local} stable-1.11 map[] map[] map[] [] [] [] [] /etc/kubernetes/pki k8s.gcr.io { /var/log/kubernetes/audit 0x156e2f4} map[] kubernetes}
|
||||
// {{InitConfiguration kubeadm.k8s.io/v1beta1} [{abcdef.abcdef0123456789 &Duration{Duration:24h0m0s,} <nil> [signing authentication] [system:bootstrappers:kubeadm:default-node-token]}] {testNode /var/run/cri.sock [] map[]} {10.100.0.1 4332} {0xc42039d1a0 <nil>} {10.100.0.0/24 10.100.1.0/24 cluster.local} stable-1.11 map[] map[] map[] [] [] [] [] /etc/kubernetes/pki k8s.gcr.io { /var/log/kubernetes/audit 0xc4204fef3c} map[] kubernetes}
|
||||
|
||||
func TestSplitYAMLDocuments(t *testing.T) {
|
||||
var tests = []struct {
|
||||
name string
|
||||
fileContents []byte
|
||||
gvkmap map[schema.GroupVersionKind][]byte
|
||||
expectedErr bool
|
||||
}{
|
||||
{
|
||||
name: "FooOnly",
|
||||
fileContents: files["foo"],
|
||||
gvkmap: map[schema.GroupVersionKind][]byte{
|
||||
{Group: "foo.k8s.io", Version: "v1", Kind: "Foo"}: files["foo"],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "FooBar",
|
||||
fileContents: bytes.Join([][]byte{files["foo"], files["bar"]}, []byte(constants.YAMLDocumentSeparator)),
|
||||
gvkmap: map[schema.GroupVersionKind][]byte{
|
||||
{Group: "foo.k8s.io", Version: "v1", Kind: "Foo"}: files["foo"],
|
||||
{Group: "bar.k8s.io", Version: "v2", Kind: "Bar"}: files["bar"],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "FooTwiceInvalid",
|
||||
fileContents: bytes.Join([][]byte{files["foo"], files["bar"], files["foo"]}, []byte(constants.YAMLDocumentSeparator)),
|
||||
expectedErr: true,
|
||||
},
|
||||
{
|
||||
name: "InvalidBaz",
|
||||
fileContents: bytes.Join([][]byte{files["foo"], files["baz"]}, []byte(constants.YAMLDocumentSeparator)),
|
||||
expectedErr: true,
|
||||
},
|
||||
{
|
||||
name: "InvalidNoKind",
|
||||
fileContents: files["nokind"],
|
||||
expectedErr: true,
|
||||
},
|
||||
{
|
||||
name: "InvalidNoAPIVersion",
|
||||
fileContents: files["noapiversion"],
|
||||
expectedErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, rt := range tests {
|
||||
t.Run(rt.name, func(t2 *testing.T) {
|
||||
|
||||
gvkmap, err := SplitYAMLDocuments(rt.fileContents)
|
||||
if (err != nil) != rt.expectedErr {
|
||||
t2.Errorf("expected error: %t, actual: %t", rt.expectedErr, err != nil)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(gvkmap, rt.gvkmap) {
|
||||
t2.Errorf("expected gvkmap: %s\n\tactual: %s\n", rt.gvkmap, gvkmap)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGroupVersionKindsFromBytes(t *testing.T) {
|
||||
var tests = []struct {
|
||||
name string
|
||||
fileContents []byte
|
||||
gvks []string
|
||||
expectedErr bool
|
||||
}{
|
||||
{
|
||||
name: "FooOnly",
|
||||
fileContents: files["foo"],
|
||||
gvks: []string{
|
||||
"foo.k8s.io/v1, Kind=Foo",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "FooBar",
|
||||
fileContents: bytes.Join([][]byte{files["foo"], files["bar"]}, []byte(constants.YAMLDocumentSeparator)),
|
||||
gvks: []string{
|
||||
"foo.k8s.io/v1, Kind=Foo",
|
||||
"bar.k8s.io/v2, Kind=Bar",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "FooTwiceInvalid",
|
||||
fileContents: bytes.Join([][]byte{files["foo"], files["bar"], files["foo"]}, []byte(constants.YAMLDocumentSeparator)),
|
||||
gvks: []string{},
|
||||
expectedErr: true,
|
||||
},
|
||||
{
|
||||
name: "InvalidBaz",
|
||||
fileContents: bytes.Join([][]byte{files["foo"], files["baz"]}, []byte(constants.YAMLDocumentSeparator)),
|
||||
gvks: []string{},
|
||||
expectedErr: true,
|
||||
},
|
||||
{
|
||||
name: "InvalidNoKind",
|
||||
fileContents: files["nokind"],
|
||||
gvks: []string{},
|
||||
expectedErr: true,
|
||||
},
|
||||
{
|
||||
name: "InvalidNoAPIVersion",
|
||||
fileContents: files["noapiversion"],
|
||||
gvks: []string{},
|
||||
expectedErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, rt := range tests {
|
||||
t.Run(rt.name, func(t2 *testing.T) {
|
||||
|
||||
gvks, err := GroupVersionKindsFromBytes(rt.fileContents)
|
||||
if (err != nil) != rt.expectedErr {
|
||||
t2.Errorf("expected error: %t, actual: %t", rt.expectedErr, err != nil)
|
||||
}
|
||||
|
||||
strgvks := []string{}
|
||||
for _, gvk := range gvks {
|
||||
strgvks = append(strgvks, gvk.String())
|
||||
}
|
||||
sort.Strings(strgvks)
|
||||
sort.Strings(rt.gvks)
|
||||
|
||||
if !reflect.DeepEqual(strgvks, rt.gvks) {
|
||||
t2.Errorf("expected gvks: %s\n\tactual: %s\n", rt.gvks, strgvks)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGroupVersionKindsHasKind(t *testing.T) {
|
||||
var tests = []struct {
|
||||
name string
|
||||
gvks []schema.GroupVersionKind
|
||||
kind string
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
name: "FooOnly",
|
||||
gvks: []schema.GroupVersionKind{
|
||||
{Group: "foo.k8s.io", Version: "v1", Kind: "Foo"},
|
||||
},
|
||||
kind: "Foo",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "FooBar",
|
||||
gvks: []schema.GroupVersionKind{
|
||||
{Group: "foo.k8s.io", Version: "v1", Kind: "Foo"},
|
||||
{Group: "bar.k8s.io", Version: "v2", Kind: "Bar"},
|
||||
},
|
||||
kind: "Bar",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "FooBazNoBaz",
|
||||
gvks: []schema.GroupVersionKind{
|
||||
{Group: "foo.k8s.io", Version: "v1", Kind: "Foo"},
|
||||
{Group: "bar.k8s.io", Version: "v2", Kind: "Bar"},
|
||||
},
|
||||
kind: "Baz",
|
||||
expected: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, rt := range tests {
|
||||
t.Run(rt.name, func(t2 *testing.T) {
|
||||
|
||||
actual := GroupVersionKindsHasKind(rt.gvks, rt.kind)
|
||||
if rt.expected != actual {
|
||||
t2.Errorf("expected gvks has kind: %t\n\tactual: %t\n", rt.expected, actual)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGroupVersionKindsHasInitConfiguration(t *testing.T) {
|
||||
var tests = []struct {
|
||||
name string
|
||||
gvks []schema.GroupVersionKind
|
||||
kind string
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
name: "NoInitConfiguration",
|
||||
gvks: []schema.GroupVersionKind{
|
||||
{Group: "foo.k8s.io", Version: "v1", Kind: "Foo"},
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "InitConfigurationFound",
|
||||
gvks: []schema.GroupVersionKind{
|
||||
{Group: "foo.k8s.io", Version: "v1", Kind: "Foo"},
|
||||
{Group: "bar.k8s.io", Version: "v2", Kind: "InitConfiguration"},
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, rt := range tests {
|
||||
t.Run(rt.name, func(t2 *testing.T) {
|
||||
|
||||
actual := GroupVersionKindsHasInitConfiguration(rt.gvks...)
|
||||
if rt.expected != actual {
|
||||
t2.Errorf("expected gvks has InitConfiguration: %t\n\tactual: %t\n", rt.expected, actual)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGroupVersionKindsHasJoinConfiguration(t *testing.T) {
|
||||
var tests = []struct {
|
||||
name string
|
||||
gvks []schema.GroupVersionKind
|
||||
kind string
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
name: "NoJoinConfiguration",
|
||||
gvks: []schema.GroupVersionKind{
|
||||
{Group: "foo.k8s.io", Version: "v1", Kind: "Foo"},
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "JoinConfigurationFound",
|
||||
gvks: []schema.GroupVersionKind{
|
||||
{Group: "foo.k8s.io", Version: "v1", Kind: "Foo"},
|
||||
{Group: "bar.k8s.io", Version: "v2", Kind: "JoinConfiguration"},
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, rt := range tests {
|
||||
t.Run(rt.name, func(t2 *testing.T) {
|
||||
|
||||
actual := GroupVersionKindsHasJoinConfiguration(rt.gvks...)
|
||||
if rt.expected != actual {
|
||||
t2.Errorf("expected gvks has JoinConfiguration: %t\n\tactual: %t\n", rt.expected, actual)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
45
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/pkiutil/BUILD
generated
vendored
Normal file
45
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/pkiutil/BUILD
generated
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["pki_helpers_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/util/cert:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["pki_helpers.go"],
|
||||
importpath = "k8s.io/kubernetes/cmd/kubeadm/app/util/pkiutil",
|
||||
deps = [
|
||||
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
|
||||
"//cmd/kubeadm/app/constants:go_default_library",
|
||||
"//cmd/kubeadm/app/util:go_default_library",
|
||||
"//pkg/registry/core/service/ipallocator:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/validation:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/util/cert:go_default_library",
|
||||
"//vendor/github.com/pkg/errors:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
506
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/pkiutil/pki_helpers.go
generated
vendored
Normal file
506
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/pkiutil/pki_helpers.go
generated
vendored
Normal file
@ -0,0 +1,506 @@
|
||||
/*
|
||||
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 pkiutil
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
cryptorand "crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/validation"
|
||||
certutil "k8s.io/client-go/util/cert"
|
||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
||||
"k8s.io/kubernetes/pkg/registry/core/service/ipallocator"
|
||||
)
|
||||
|
||||
// NewCertificateAuthority creates new certificate and private key for the certificate authority
|
||||
func NewCertificateAuthority(config *certutil.Config) (*x509.Certificate, *rsa.PrivateKey, error) {
|
||||
key, err := certutil.NewPrivateKey()
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "unable to create private key")
|
||||
}
|
||||
|
||||
cert, err := certutil.NewSelfSignedCACert(*config, key)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "unable to create self-signed certificate")
|
||||
}
|
||||
|
||||
return cert, key, nil
|
||||
}
|
||||
|
||||
// NewCertAndKey creates new certificate and key by passing the certificate authority certificate and key
|
||||
func NewCertAndKey(caCert *x509.Certificate, caKey *rsa.PrivateKey, config *certutil.Config) (*x509.Certificate, *rsa.PrivateKey, error) {
|
||||
key, err := certutil.NewPrivateKey()
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "unable to create private key")
|
||||
}
|
||||
|
||||
cert, err := certutil.NewSignedCert(*config, key, caCert, caKey)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "unable to sign certificate")
|
||||
}
|
||||
|
||||
return cert, key, nil
|
||||
}
|
||||
|
||||
// NewCSRAndKey generates a new key and CSR and that could be signed to create the given certificate
|
||||
func NewCSRAndKey(config *certutil.Config) (*x509.CertificateRequest, *rsa.PrivateKey, error) {
|
||||
key, err := certutil.NewPrivateKey()
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "unable to create private key")
|
||||
}
|
||||
|
||||
csr, err := NewCSR(*config, key)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "unable to generate CSR")
|
||||
}
|
||||
|
||||
return csr, key, nil
|
||||
}
|
||||
|
||||
// HasServerAuth returns true if the given certificate is a ServerAuth
|
||||
func HasServerAuth(cert *x509.Certificate) bool {
|
||||
for i := range cert.ExtKeyUsage {
|
||||
if cert.ExtKeyUsage[i] == x509.ExtKeyUsageServerAuth {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// WriteCertAndKey stores certificate and key at the specified location
|
||||
func WriteCertAndKey(pkiPath string, name string, cert *x509.Certificate, key *rsa.PrivateKey) error {
|
||||
if err := WriteKey(pkiPath, name, key); err != nil {
|
||||
return errors.Wrap(err, "couldn't write key")
|
||||
}
|
||||
|
||||
return WriteCert(pkiPath, name, cert)
|
||||
}
|
||||
|
||||
// WriteCert stores the given certificate at the given location
|
||||
func WriteCert(pkiPath, name string, cert *x509.Certificate) error {
|
||||
if cert == nil {
|
||||
return errors.New("certificate cannot be nil when writing to file")
|
||||
}
|
||||
|
||||
certificatePath := pathForCert(pkiPath, name)
|
||||
if err := certutil.WriteCert(certificatePath, certutil.EncodeCertPEM(cert)); err != nil {
|
||||
return errors.Wrapf(err, "unable to write certificate to file %s", certificatePath)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteKey stores the given key at the given location
|
||||
func WriteKey(pkiPath, name string, key *rsa.PrivateKey) error {
|
||||
if key == nil {
|
||||
return errors.New("private key cannot be nil when writing to file")
|
||||
}
|
||||
|
||||
privateKeyPath := pathForKey(pkiPath, name)
|
||||
if err := certutil.WriteKey(privateKeyPath, certutil.EncodePrivateKeyPEM(key)); err != nil {
|
||||
return errors.Wrapf(err, "unable to write private key to file %s", privateKeyPath)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteCSR writes the pem-encoded CSR data to csrPath.
|
||||
// The CSR file will be created with file mode 0644.
|
||||
// If the CSR file already exists, it will be overwritten.
|
||||
// The parent directory of the csrPath will be created as needed with file mode 0755.
|
||||
func WriteCSR(csrDir, name string, csr *x509.CertificateRequest) error {
|
||||
if csr == nil {
|
||||
return errors.New("certificate request cannot be nil when writing to file")
|
||||
}
|
||||
|
||||
csrPath := pathForCSR(csrDir, name)
|
||||
if err := os.MkdirAll(filepath.Dir(csrPath), os.FileMode(0755)); err != nil {
|
||||
return errors.Wrapf(err, "failed to make directory %s", filepath.Dir(csrPath))
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(csrPath, EncodeCSRPEM(csr), os.FileMode(0644)); err != nil {
|
||||
return errors.Wrapf(err, "unable to write CSR to file %s", csrPath)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WritePublicKey stores the given public key at the given location
|
||||
func WritePublicKey(pkiPath, name string, key *rsa.PublicKey) error {
|
||||
if key == nil {
|
||||
return errors.New("public key cannot be nil when writing to file")
|
||||
}
|
||||
|
||||
publicKeyBytes, err := certutil.EncodePublicKeyPEM(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
publicKeyPath := pathForPublicKey(pkiPath, name)
|
||||
if err := certutil.WriteKey(publicKeyPath, publicKeyBytes); err != nil {
|
||||
return errors.Wrapf(err, "unable to write public key to file %s", publicKeyPath)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CertOrKeyExist returns a boolean whether the cert or the key exists
|
||||
func CertOrKeyExist(pkiPath, name string) bool {
|
||||
certificatePath, privateKeyPath := PathsForCertAndKey(pkiPath, name)
|
||||
|
||||
_, certErr := os.Stat(certificatePath)
|
||||
_, keyErr := os.Stat(privateKeyPath)
|
||||
if os.IsNotExist(certErr) && os.IsNotExist(keyErr) {
|
||||
// The cert or the key did not exist
|
||||
return false
|
||||
}
|
||||
|
||||
// Both files exist or one of them
|
||||
return true
|
||||
}
|
||||
|
||||
// CSROrKeyExist returns true if one of the CSR or key exists
|
||||
func CSROrKeyExist(csrDir, name string) bool {
|
||||
csrPath := pathForCSR(csrDir, name)
|
||||
keyPath := pathForKey(csrDir, name)
|
||||
|
||||
_, csrErr := os.Stat(csrPath)
|
||||
_, keyErr := os.Stat(keyPath)
|
||||
|
||||
return !(os.IsNotExist(csrErr) && os.IsNotExist(keyErr))
|
||||
}
|
||||
|
||||
// TryLoadCertAndKeyFromDisk tries to load a cert and a key from the disk and validates that they are valid
|
||||
func TryLoadCertAndKeyFromDisk(pkiPath, name string) (*x509.Certificate, *rsa.PrivateKey, error) {
|
||||
cert, err := TryLoadCertFromDisk(pkiPath, name)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "failed to load certificate")
|
||||
}
|
||||
|
||||
key, err := TryLoadKeyFromDisk(pkiPath, name)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "failed to load key")
|
||||
}
|
||||
|
||||
return cert, key, nil
|
||||
}
|
||||
|
||||
// TryLoadCertFromDisk tries to load the cert from the disk and validates that it is valid
|
||||
func TryLoadCertFromDisk(pkiPath, name string) (*x509.Certificate, error) {
|
||||
certificatePath := pathForCert(pkiPath, name)
|
||||
|
||||
certs, err := certutil.CertsFromFile(certificatePath)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "couldn't load the certificate file %s", certificatePath)
|
||||
}
|
||||
|
||||
// We are only putting one certificate in the certificate pem file, so it's safe to just pick the first one
|
||||
// TODO: Support multiple certs here in order to be able to rotate certs
|
||||
cert := certs[0]
|
||||
|
||||
// Check so that the certificate is valid now
|
||||
now := time.Now()
|
||||
if now.Before(cert.NotBefore) {
|
||||
return nil, errors.New("the certificate is not valid yet")
|
||||
}
|
||||
if now.After(cert.NotAfter) {
|
||||
return nil, errors.New("the certificate has expired")
|
||||
}
|
||||
|
||||
return cert, nil
|
||||
}
|
||||
|
||||
// TryLoadKeyFromDisk tries to load the key from the disk and validates that it is valid
|
||||
func TryLoadKeyFromDisk(pkiPath, name string) (*rsa.PrivateKey, error) {
|
||||
privateKeyPath := pathForKey(pkiPath, name)
|
||||
|
||||
// Parse the private key from a file
|
||||
privKey, err := certutil.PrivateKeyFromFile(privateKeyPath)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "couldn't load the private key file %s", privateKeyPath)
|
||||
}
|
||||
|
||||
// Allow RSA format only
|
||||
var key *rsa.PrivateKey
|
||||
switch k := privKey.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
key = k
|
||||
default:
|
||||
return nil, errors.Errorf("the private key file %s isn't in RSA format", privateKeyPath)
|
||||
}
|
||||
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// TryLoadCSRAndKeyFromDisk tries to load the CSR and key from the disk
|
||||
func TryLoadCSRAndKeyFromDisk(pkiPath, name string) (*x509.CertificateRequest, *rsa.PrivateKey, error) {
|
||||
csrPath := pathForCSR(pkiPath, name)
|
||||
|
||||
csr, err := CertificateRequestFromFile(csrPath)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrapf(err, "couldn't load the certificate request %s", csrPath)
|
||||
}
|
||||
|
||||
key, err := TryLoadKeyFromDisk(pkiPath, name)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "couldn't load key file")
|
||||
}
|
||||
|
||||
return csr, key, nil
|
||||
}
|
||||
|
||||
// TryLoadPrivatePublicKeyFromDisk tries to load the key from the disk and validates that it is valid
|
||||
func TryLoadPrivatePublicKeyFromDisk(pkiPath, name string) (*rsa.PrivateKey, *rsa.PublicKey, error) {
|
||||
privateKeyPath := pathForKey(pkiPath, name)
|
||||
|
||||
// Parse the private key from a file
|
||||
privKey, err := certutil.PrivateKeyFromFile(privateKeyPath)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrapf(err, "couldn't load the private key file %s", privateKeyPath)
|
||||
}
|
||||
|
||||
publicKeyPath := pathForPublicKey(pkiPath, name)
|
||||
|
||||
// Parse the public key from a file
|
||||
pubKeys, err := certutil.PublicKeysFromFile(publicKeyPath)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrapf(err, "couldn't load the public key file %s", publicKeyPath)
|
||||
}
|
||||
|
||||
// Allow RSA format only
|
||||
k, ok := privKey.(*rsa.PrivateKey)
|
||||
if !ok {
|
||||
return nil, nil, errors.Errorf("the private key file %s isn't in RSA format", privateKeyPath)
|
||||
}
|
||||
|
||||
p := pubKeys[0].(*rsa.PublicKey)
|
||||
|
||||
return k, p, nil
|
||||
}
|
||||
|
||||
// PathsForCertAndKey returns the paths for the certificate and key given the path and basename.
|
||||
func PathsForCertAndKey(pkiPath, name string) (string, string) {
|
||||
return pathForCert(pkiPath, name), pathForKey(pkiPath, name)
|
||||
}
|
||||
|
||||
func pathForCert(pkiPath, name string) string {
|
||||
return filepath.Join(pkiPath, fmt.Sprintf("%s.crt", name))
|
||||
}
|
||||
|
||||
func pathForKey(pkiPath, name string) string {
|
||||
return filepath.Join(pkiPath, fmt.Sprintf("%s.key", name))
|
||||
}
|
||||
|
||||
func pathForPublicKey(pkiPath, name string) string {
|
||||
return filepath.Join(pkiPath, fmt.Sprintf("%s.pub", name))
|
||||
}
|
||||
|
||||
func pathForCSR(pkiPath, name string) string {
|
||||
return filepath.Join(pkiPath, fmt.Sprintf("%s.csr", name))
|
||||
}
|
||||
|
||||
// GetAPIServerAltNames builds an AltNames object for to be used when generating apiserver certificate
|
||||
func GetAPIServerAltNames(cfg *kubeadmapi.InitConfiguration) (*certutil.AltNames, error) {
|
||||
// advertise address
|
||||
advertiseAddress := net.ParseIP(cfg.LocalAPIEndpoint.AdvertiseAddress)
|
||||
if advertiseAddress == nil {
|
||||
return nil, errors.Errorf("error parsing LocalAPIEndpoint AdvertiseAddress %v: is not a valid textual representation of an IP address",
|
||||
cfg.LocalAPIEndpoint.AdvertiseAddress)
|
||||
}
|
||||
|
||||
// internal IP address for the API server
|
||||
_, svcSubnet, err := net.ParseCIDR(cfg.Networking.ServiceSubnet)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "error parsing CIDR %q", cfg.Networking.ServiceSubnet)
|
||||
}
|
||||
|
||||
internalAPIServerVirtualIP, err := ipallocator.GetIndexedIP(svcSubnet, 1)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "unable to get first IP address from the given CIDR (%s)", svcSubnet.String())
|
||||
}
|
||||
|
||||
// create AltNames with defaults DNSNames/IPs
|
||||
altNames := &certutil.AltNames{
|
||||
DNSNames: []string{
|
||||
cfg.NodeRegistration.Name,
|
||||
"kubernetes",
|
||||
"kubernetes.default",
|
||||
"kubernetes.default.svc",
|
||||
fmt.Sprintf("kubernetes.default.svc.%s", cfg.Networking.DNSDomain),
|
||||
},
|
||||
IPs: []net.IP{
|
||||
internalAPIServerVirtualIP,
|
||||
advertiseAddress,
|
||||
},
|
||||
}
|
||||
|
||||
// add cluster controlPlaneEndpoint if present (dns or ip)
|
||||
if len(cfg.ControlPlaneEndpoint) > 0 {
|
||||
if host, _, err := kubeadmutil.ParseHostPort(cfg.ControlPlaneEndpoint); err == nil {
|
||||
if ip := net.ParseIP(host); ip != nil {
|
||||
altNames.IPs = append(altNames.IPs, ip)
|
||||
} else {
|
||||
altNames.DNSNames = append(altNames.DNSNames, host)
|
||||
}
|
||||
} else {
|
||||
return nil, errors.Wrapf(err, "error parsing cluster controlPlaneEndpoint %q", cfg.ControlPlaneEndpoint)
|
||||
}
|
||||
}
|
||||
|
||||
appendSANsToAltNames(altNames, cfg.APIServer.CertSANs, kubeadmconstants.APIServerCertName)
|
||||
|
||||
return altNames, nil
|
||||
}
|
||||
|
||||
// GetEtcdAltNames builds an AltNames object for generating the etcd server certificate.
|
||||
// `advertise address` and localhost are included in the SAN since this is the interfaces the etcd static pod listens on.
|
||||
// The user can override the listen address with `Etcd.ExtraArgs` and add SANs with `Etcd.ServerCertSANs`.
|
||||
func GetEtcdAltNames(cfg *kubeadmapi.InitConfiguration) (*certutil.AltNames, error) {
|
||||
// advertise address
|
||||
advertiseAddress := net.ParseIP(cfg.LocalAPIEndpoint.AdvertiseAddress)
|
||||
if advertiseAddress == nil {
|
||||
return nil, errors.Errorf("error parsing LocalAPIEndpoint AdvertiseAddress %q: is not a valid textual representation of an IP address", cfg.LocalAPIEndpoint.AdvertiseAddress)
|
||||
}
|
||||
|
||||
// create AltNames with defaults DNSNames/IPs
|
||||
altNames := &certutil.AltNames{
|
||||
DNSNames: []string{cfg.NodeRegistration.Name, "localhost"},
|
||||
IPs: []net.IP{advertiseAddress, net.IPv4(127, 0, 0, 1), net.IPv6loopback},
|
||||
}
|
||||
|
||||
if cfg.Etcd.Local != nil {
|
||||
appendSANsToAltNames(altNames, cfg.Etcd.Local.ServerCertSANs, kubeadmconstants.EtcdServerCertName)
|
||||
}
|
||||
|
||||
return altNames, nil
|
||||
}
|
||||
|
||||
// GetEtcdPeerAltNames builds an AltNames object for generating the etcd peer certificate.
|
||||
// Hostname and `API.AdvertiseAddress` are included if the user chooses to promote the single node etcd cluster into a multi-node one (stacked etcd).
|
||||
// The user can override the listen address with `Etcd.ExtraArgs` and add SANs with `Etcd.PeerCertSANs`.
|
||||
func GetEtcdPeerAltNames(cfg *kubeadmapi.InitConfiguration) (*certutil.AltNames, error) {
|
||||
// advertise address
|
||||
advertiseAddress := net.ParseIP(cfg.LocalAPIEndpoint.AdvertiseAddress)
|
||||
if advertiseAddress == nil {
|
||||
return nil, errors.Errorf("error parsing LocalAPIEndpoint AdvertiseAddress %v: is not a valid textual representation of an IP address",
|
||||
cfg.LocalAPIEndpoint.AdvertiseAddress)
|
||||
}
|
||||
|
||||
// create AltNames with defaults DNSNames/IPs
|
||||
altNames := &certutil.AltNames{
|
||||
DNSNames: []string{cfg.NodeRegistration.Name, "localhost"},
|
||||
IPs: []net.IP{advertiseAddress, net.IPv4(127, 0, 0, 1), net.IPv6loopback},
|
||||
}
|
||||
|
||||
if cfg.Etcd.Local != nil {
|
||||
appendSANsToAltNames(altNames, cfg.Etcd.Local.PeerCertSANs, kubeadmconstants.EtcdPeerCertName)
|
||||
}
|
||||
|
||||
return altNames, nil
|
||||
}
|
||||
|
||||
// appendSANsToAltNames parses SANs from as list of strings and adds them to altNames for use on a specific cert
|
||||
// altNames is passed in with a pointer, and the struct is modified
|
||||
// valid IP address strings are parsed and added to altNames.IPs as net.IP's
|
||||
// RFC-1123 compliant DNS strings are added to altNames.DNSNames as strings
|
||||
// certNames is used to print user facing warnings and should be the name of the cert the altNames will be used for
|
||||
func appendSANsToAltNames(altNames *certutil.AltNames, SANs []string, certName string) {
|
||||
for _, altname := range SANs {
|
||||
if ip := net.ParseIP(altname); ip != nil {
|
||||
altNames.IPs = append(altNames.IPs, ip)
|
||||
} else if len(validation.IsDNS1123Subdomain(altname)) == 0 {
|
||||
altNames.DNSNames = append(altNames.DNSNames, altname)
|
||||
} else {
|
||||
fmt.Printf(
|
||||
"[certificates] WARNING: '%s' was not added to the '%s' SAN, because it is not a valid IP or RFC-1123 compliant DNS entry\n",
|
||||
altname,
|
||||
certName,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// EncodeCSRPEM returns PEM-encoded CSR data
|
||||
func EncodeCSRPEM(csr *x509.CertificateRequest) []byte {
|
||||
block := pem.Block{
|
||||
Type: certutil.CertificateRequestBlockType,
|
||||
Bytes: csr.Raw,
|
||||
}
|
||||
return pem.EncodeToMemory(&block)
|
||||
}
|
||||
|
||||
func parseCSRPEM(pemCSR []byte) (*x509.CertificateRequest, error) {
|
||||
block, _ := pem.Decode(pemCSR)
|
||||
if block == nil {
|
||||
return nil, fmt.Errorf("data doesn't contain a valid certificate request")
|
||||
}
|
||||
|
||||
if block.Type != certutil.CertificateRequestBlockType {
|
||||
var block *pem.Block
|
||||
return nil, fmt.Errorf("expected block type %q, but PEM had type %v", certutil.CertificateRequestBlockType, block.Type)
|
||||
}
|
||||
|
||||
return x509.ParseCertificateRequest(block.Bytes)
|
||||
}
|
||||
|
||||
// CertificateRequestFromFile returns the CertificateRequest from a given PEM-encoded file.
|
||||
// Returns an error if the file could not be read or if the CSR could not be parsed.
|
||||
func CertificateRequestFromFile(file string) (*x509.CertificateRequest, error) {
|
||||
pemBlock, err := ioutil.ReadFile(file)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to read file")
|
||||
}
|
||||
|
||||
csr, err := parseCSRPEM(pemBlock)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading certificate request file %s: %v", file, err)
|
||||
}
|
||||
return csr, nil
|
||||
}
|
||||
|
||||
// NewCSR creates a new CSR
|
||||
func NewCSR(cfg certutil.Config, key crypto.Signer) (*x509.CertificateRequest, error) {
|
||||
template := &x509.CertificateRequest{
|
||||
Subject: pkix.Name{
|
||||
CommonName: cfg.CommonName,
|
||||
Organization: cfg.Organization,
|
||||
},
|
||||
DNSNames: cfg.AltNames.DNSNames,
|
||||
IPAddresses: cfg.AltNames.IPs,
|
||||
}
|
||||
|
||||
csrBytes, err := x509.CreateCertificateRequest(cryptorand.Reader, template, key)
|
||||
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to create a CSR")
|
||||
}
|
||||
|
||||
return x509.ParseCertificateRequest(csrBytes)
|
||||
}
|
657
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/pkiutil/pki_helpers_test.go
generated
vendored
Normal file
657
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/pkiutil/pki_helpers_test.go
generated
vendored
Normal file
@ -0,0 +1,657 @@
|
||||
/*
|
||||
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 pkiutil
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
certutil "k8s.io/client-go/util/cert"
|
||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||
)
|
||||
|
||||
// certificateRequest is an x509 certificate request in PEM encoded format
|
||||
// openssl req -new -key rsa2048.pem -sha256 -nodes -out x509certrequest.pem -subj "/C=US/CN=not-valid"
|
||||
const certificateRequest = `-----BEGIN CERTIFICATE REQUEST-----
|
||||
MIICZjCCAU4CAQAwITELMAkGA1UEBhMCVVMxEjAQBgNVBAMMCW5vdC12YWxpZDCC
|
||||
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMdoBxV0SbSS+7XrgVDF/P4x
|
||||
tqyun+DLxeRF5265ZOFRJDXCJgYH7wKlxlkEaHZQhnNmnqFiy96MHSKaiQmlkEm4
|
||||
EhlqTf38yEWx+t98A0CDbHsIPZ0/+MPCjb2kf+OfBXJJl908io0grs02jxN9lceL
|
||||
RFrKT6vaB+6i7LxbPQcOmjF7OUqWS6S2qSpShw2GY+mJz4HM7OFb9RcN4izh+GF6
|
||||
7hajYgt7pAFyWF1ua/H98Ysn4FVgIYk30rHCNBkQpJnna7EyGYuj08VuFa088W9g
|
||||
c/DCpL+VgBDwTel9tfeMxRAoLIPF9iJ8Ftr7dsRZ/Y/SnxfUJo2ed8y7dgIiLuEC
|
||||
AwEAAaAAMA0GCSqGSIb3DQEBCwUAA4IBAQCOjPB/4LKa2G7LarMMLAeNqvWF9SIG
|
||||
y2VGQoTn9D5blXMvnfzWSYgU6nBzf/E/32q26OwiCriuOPXfxM/cxEMOJ62u7b50
|
||||
OR52JFvQdONsCZaLgylGWppl0YeqylbTosHjsWJNlp+zjXcQHjCQ9OoLgfmrwYyD
|
||||
2MsYJR4p7JZ2ZN8FF1hgMUrDzypZ0NSBKAiQMU9TFhxgyk75RNDtmX+2K35zqLyr
|
||||
0otimyYwPCGPD2GHwNfvu1oP0A+/cT+rCPz6AlXhWEbz2JkLo6/muRfRl0QSRgHE
|
||||
Q3+eWlA1YdqEBwvp3NEQI9BtMnzxJVWA5dvYluMNllsV/q8s2IEEAFG9
|
||||
-----END CERTIFICATE REQUEST-----`
|
||||
|
||||
func TestNewCertificateAuthority(t *testing.T) {
|
||||
cert, key, err := NewCertificateAuthority(&certutil.Config{CommonName: "kubernetes"})
|
||||
|
||||
if cert == nil {
|
||||
t.Errorf(
|
||||
"failed NewCertificateAuthority, cert == nil",
|
||||
)
|
||||
}
|
||||
if key == nil {
|
||||
t.Errorf(
|
||||
"failed NewCertificateAuthority, key == nil",
|
||||
)
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf(
|
||||
"failed NewCertificateAuthority with an error: %v",
|
||||
err,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewCertAndKey(t *testing.T) {
|
||||
var tests = []struct {
|
||||
caKeySize int
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
// RSA key too small
|
||||
caKeySize: 128,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
// Should succeed
|
||||
caKeySize: 2048,
|
||||
expected: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, rt := range tests {
|
||||
caKey, err := rsa.GenerateKey(rand.Reader, rt.caKeySize)
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't create rsa Private Key")
|
||||
}
|
||||
caCert := &x509.Certificate{}
|
||||
config := &certutil.Config{
|
||||
CommonName: "test",
|
||||
Organization: []string{"test"},
|
||||
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
|
||||
}
|
||||
_, _, actual := NewCertAndKey(caCert, caKey, config)
|
||||
if (actual == nil) != rt.expected {
|
||||
t.Errorf(
|
||||
"failed NewCertAndKey:\n\texpected: %t\n\t actual: %t",
|
||||
rt.expected,
|
||||
(actual == nil),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHasServerAuth(t *testing.T) {
|
||||
caCert, caKey, _ := NewCertificateAuthority(&certutil.Config{CommonName: "kubernetes"})
|
||||
|
||||
var tests = []struct {
|
||||
config certutil.Config
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
config: certutil.Config{
|
||||
CommonName: "test",
|
||||
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
config: certutil.Config{
|
||||
CommonName: "test",
|
||||
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, rt := range tests {
|
||||
cert, _, err := NewCertAndKey(caCert, caKey, &rt.config)
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't create cert: %v", err)
|
||||
}
|
||||
actual := HasServerAuth(cert)
|
||||
if actual != rt.expected {
|
||||
t.Errorf(
|
||||
"failed HasServerAuth:\n\texpected: %t\n\t actual: %t",
|
||||
rt.expected,
|
||||
actual,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteCertAndKey(t *testing.T) {
|
||||
tmpdir, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't create tmpdir")
|
||||
}
|
||||
defer os.RemoveAll(tmpdir)
|
||||
|
||||
caKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't create rsa Private Key")
|
||||
}
|
||||
caCert := &x509.Certificate{}
|
||||
actual := WriteCertAndKey(tmpdir, "foo", caCert, caKey)
|
||||
if actual != nil {
|
||||
t.Errorf(
|
||||
"failed WriteCertAndKey with an error: %v",
|
||||
actual,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteCert(t *testing.T) {
|
||||
tmpdir, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't create tmpdir")
|
||||
}
|
||||
defer os.RemoveAll(tmpdir)
|
||||
|
||||
caCert := &x509.Certificate{}
|
||||
actual := WriteCert(tmpdir, "foo", caCert)
|
||||
if actual != nil {
|
||||
t.Errorf(
|
||||
"failed WriteCertAndKey with an error: %v",
|
||||
actual,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteKey(t *testing.T) {
|
||||
tmpdir, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't create tmpdir")
|
||||
}
|
||||
defer os.RemoveAll(tmpdir)
|
||||
|
||||
caKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't create rsa Private Key")
|
||||
}
|
||||
actual := WriteKey(tmpdir, "foo", caKey)
|
||||
if actual != nil {
|
||||
t.Errorf(
|
||||
"failed WriteCertAndKey with an error: %v",
|
||||
actual,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWritePublicKey(t *testing.T) {
|
||||
tmpdir, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't create tmpdir")
|
||||
}
|
||||
defer os.RemoveAll(tmpdir)
|
||||
|
||||
caKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't create rsa Private Key")
|
||||
}
|
||||
actual := WritePublicKey(tmpdir, "foo", &caKey.PublicKey)
|
||||
if actual != nil {
|
||||
t.Errorf(
|
||||
"failed WriteCertAndKey with an error: %v",
|
||||
actual,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCertOrKeyExist(t *testing.T) {
|
||||
tmpdir, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't create tmpdir")
|
||||
}
|
||||
defer os.RemoveAll(tmpdir)
|
||||
|
||||
caKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't create rsa Private Key")
|
||||
}
|
||||
caCert := &x509.Certificate{}
|
||||
actual := WriteCertAndKey(tmpdir, "foo", caCert, caKey)
|
||||
if actual != nil {
|
||||
t.Errorf(
|
||||
"failed WriteCertAndKey with an error: %v",
|
||||
actual,
|
||||
)
|
||||
}
|
||||
|
||||
var tests = []struct {
|
||||
path string
|
||||
name string
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
path: "",
|
||||
name: "",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
path: tmpdir,
|
||||
name: "foo",
|
||||
expected: true,
|
||||
},
|
||||
}
|
||||
for _, rt := range tests {
|
||||
actual := CertOrKeyExist(rt.path, rt.name)
|
||||
if actual != rt.expected {
|
||||
t.Errorf(
|
||||
"failed CertOrKeyExist:\n\texpected: %t\n\t actual: %t",
|
||||
rt.expected,
|
||||
actual,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTryLoadCertAndKeyFromDisk(t *testing.T) {
|
||||
tmpdir, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't create tmpdir")
|
||||
}
|
||||
defer os.RemoveAll(tmpdir)
|
||||
|
||||
caCert, caKey, err := NewCertificateAuthority(&certutil.Config{CommonName: "kubernetes"})
|
||||
if err != nil {
|
||||
t.Errorf(
|
||||
"failed to create cert and key with an error: %v",
|
||||
err,
|
||||
)
|
||||
}
|
||||
err = WriteCertAndKey(tmpdir, "foo", caCert, caKey)
|
||||
if err != nil {
|
||||
t.Errorf(
|
||||
"failed to write cert and key with an error: %v",
|
||||
err,
|
||||
)
|
||||
}
|
||||
|
||||
var tests = []struct {
|
||||
path string
|
||||
name string
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
path: "",
|
||||
name: "",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
path: tmpdir,
|
||||
name: "foo",
|
||||
expected: true,
|
||||
},
|
||||
}
|
||||
for _, rt := range tests {
|
||||
_, _, actual := TryLoadCertAndKeyFromDisk(rt.path, rt.name)
|
||||
if (actual == nil) != rt.expected {
|
||||
t.Errorf(
|
||||
"failed TryLoadCertAndKeyFromDisk:\n\texpected: %t\n\t actual: %t",
|
||||
rt.expected,
|
||||
(actual == nil),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTryLoadCertFromDisk(t *testing.T) {
|
||||
tmpdir, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't create tmpdir")
|
||||
}
|
||||
defer os.RemoveAll(tmpdir)
|
||||
|
||||
caCert, _, err := NewCertificateAuthority(&certutil.Config{CommonName: "kubernetes"})
|
||||
if err != nil {
|
||||
t.Errorf(
|
||||
"failed to create cert and key with an error: %v",
|
||||
err,
|
||||
)
|
||||
}
|
||||
err = WriteCert(tmpdir, "foo", caCert)
|
||||
if err != nil {
|
||||
t.Errorf(
|
||||
"failed to write cert and key with an error: %v",
|
||||
err,
|
||||
)
|
||||
}
|
||||
|
||||
var tests = []struct {
|
||||
path string
|
||||
name string
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
path: "",
|
||||
name: "",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
path: tmpdir,
|
||||
name: "foo",
|
||||
expected: true,
|
||||
},
|
||||
}
|
||||
for _, rt := range tests {
|
||||
_, actual := TryLoadCertFromDisk(rt.path, rt.name)
|
||||
if (actual == nil) != rt.expected {
|
||||
t.Errorf(
|
||||
"failed TryLoadCertAndKeyFromDisk:\n\texpected: %t\n\t actual: %t",
|
||||
rt.expected,
|
||||
(actual == nil),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTryLoadKeyFromDisk(t *testing.T) {
|
||||
tmpdir, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't create tmpdir")
|
||||
}
|
||||
defer os.RemoveAll(tmpdir)
|
||||
|
||||
_, caKey, err := NewCertificateAuthority(&certutil.Config{CommonName: "kubernetes"})
|
||||
if err != nil {
|
||||
t.Errorf(
|
||||
"failed to create cert and key with an error: %v",
|
||||
err,
|
||||
)
|
||||
}
|
||||
err = WriteKey(tmpdir, "foo", caKey)
|
||||
if err != nil {
|
||||
t.Errorf(
|
||||
"failed to write cert and key with an error: %v",
|
||||
err,
|
||||
)
|
||||
}
|
||||
|
||||
var tests = []struct {
|
||||
path string
|
||||
name string
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
path: "",
|
||||
name: "",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
path: tmpdir,
|
||||
name: "foo",
|
||||
expected: true,
|
||||
},
|
||||
}
|
||||
for _, rt := range tests {
|
||||
_, actual := TryLoadKeyFromDisk(rt.path, rt.name)
|
||||
if (actual == nil) != rt.expected {
|
||||
t.Errorf(
|
||||
"failed TryLoadCertAndKeyFromDisk:\n\texpected: %t\n\t actual: %t",
|
||||
rt.expected,
|
||||
(actual == nil),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPathsForCertAndKey(t *testing.T) {
|
||||
crtPath, keyPath := PathsForCertAndKey("/foo", "bar")
|
||||
if crtPath != "/foo/bar.crt" {
|
||||
t.Errorf("unexpected certificate path: %s", crtPath)
|
||||
}
|
||||
if keyPath != "/foo/bar.key" {
|
||||
t.Errorf("unexpected key path: %s", keyPath)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPathForCert(t *testing.T) {
|
||||
crtPath := pathForCert("/foo", "bar")
|
||||
if crtPath != "/foo/bar.crt" {
|
||||
t.Errorf("unexpected certificate path: %s", crtPath)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPathForKey(t *testing.T) {
|
||||
keyPath := pathForKey("/foo", "bar")
|
||||
if keyPath != "/foo/bar.key" {
|
||||
t.Errorf("unexpected certificate path: %s", keyPath)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPathForPublicKey(t *testing.T) {
|
||||
pubPath := pathForPublicKey("/foo", "bar")
|
||||
if pubPath != "/foo/bar.pub" {
|
||||
t.Errorf("unexpected certificate path: %s", pubPath)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPathForCSR(t *testing.T) {
|
||||
csrPath := pathForCSR("/foo", "bar")
|
||||
if csrPath != "/foo/bar.csr" {
|
||||
t.Errorf("unexpected certificate path: %s", csrPath)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAPIServerAltNames(t *testing.T) {
|
||||
|
||||
var tests = []struct {
|
||||
name string
|
||||
cfg *kubeadmapi.InitConfiguration
|
||||
expectedDNSNames []string
|
||||
expectedIPAddresses []string
|
||||
}{
|
||||
{
|
||||
name: "",
|
||||
cfg: &kubeadmapi.InitConfiguration{
|
||||
LocalAPIEndpoint: kubeadmapi.APIEndpoint{AdvertiseAddress: "1.2.3.4"},
|
||||
ClusterConfiguration: kubeadmapi.ClusterConfiguration{
|
||||
ControlPlaneEndpoint: "api.k8s.io:6443",
|
||||
Networking: kubeadmapi.Networking{ServiceSubnet: "10.96.0.0/12", DNSDomain: "cluster.local"},
|
||||
APIServer: kubeadmapi.APIServer{
|
||||
CertSANs: []string{"10.1.245.94", "10.1.245.95", "1.2.3.L", "invalid,commas,in,DNS"},
|
||||
},
|
||||
},
|
||||
NodeRegistration: kubeadmapi.NodeRegistrationOptions{Name: "valid-hostname"},
|
||||
},
|
||||
expectedDNSNames: []string{"valid-hostname", "kubernetes", "kubernetes.default", "kubernetes.default.svc", "kubernetes.default.svc.cluster.local", "api.k8s.io"},
|
||||
expectedIPAddresses: []string{"10.96.0.1", "1.2.3.4", "10.1.245.94", "10.1.245.95"},
|
||||
},
|
||||
{
|
||||
name: "ControlPlaneEndpoint IP",
|
||||
cfg: &kubeadmapi.InitConfiguration{
|
||||
LocalAPIEndpoint: kubeadmapi.APIEndpoint{AdvertiseAddress: "1.2.3.4"},
|
||||
ClusterConfiguration: kubeadmapi.ClusterConfiguration{
|
||||
ControlPlaneEndpoint: "4.5.6.7:6443",
|
||||
Networking: kubeadmapi.Networking{ServiceSubnet: "10.96.0.0/12", DNSDomain: "cluster.local"},
|
||||
APIServer: kubeadmapi.APIServer{
|
||||
CertSANs: []string{"10.1.245.94", "10.1.245.95", "1.2.3.L", "invalid,commas,in,DNS"},
|
||||
},
|
||||
},
|
||||
NodeRegistration: kubeadmapi.NodeRegistrationOptions{Name: "valid-hostname"},
|
||||
},
|
||||
expectedDNSNames: []string{"valid-hostname", "kubernetes", "kubernetes.default", "kubernetes.default.svc", "kubernetes.default.svc.cluster.local"},
|
||||
expectedIPAddresses: []string{"10.96.0.1", "1.2.3.4", "10.1.245.94", "10.1.245.95", "4.5.6.7"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, rt := range tests {
|
||||
altNames, err := GetAPIServerAltNames(rt.cfg)
|
||||
if err != nil {
|
||||
t.Fatalf("failed calling GetAPIServerAltNames: %s: %v", rt.name, err)
|
||||
}
|
||||
|
||||
for _, DNSName := range rt.expectedDNSNames {
|
||||
found := false
|
||||
for _, val := range altNames.DNSNames {
|
||||
if val == DNSName {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
t.Errorf("%s: altNames does not contain DNSName %s but %v", rt.name, DNSName, altNames.DNSNames)
|
||||
}
|
||||
}
|
||||
|
||||
for _, IPAddress := range rt.expectedIPAddresses {
|
||||
found := false
|
||||
for _, val := range altNames.IPs {
|
||||
if val.Equal(net.ParseIP(IPAddress)) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
t.Errorf("%s: altNames does not contain IPAddress %s but %v", rt.name, IPAddress, altNames.IPs)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetEtcdAltNames(t *testing.T) {
|
||||
proxy := "user-etcd-proxy"
|
||||
proxyIP := "10.10.10.100"
|
||||
cfg := &kubeadmapi.InitConfiguration{
|
||||
LocalAPIEndpoint: kubeadmapi.APIEndpoint{
|
||||
AdvertiseAddress: "1.2.3.4",
|
||||
},
|
||||
NodeRegistration: kubeadmapi.NodeRegistrationOptions{
|
||||
Name: "myNode",
|
||||
},
|
||||
ClusterConfiguration: kubeadmapi.ClusterConfiguration{
|
||||
Etcd: kubeadmapi.Etcd{
|
||||
Local: &kubeadmapi.LocalEtcd{
|
||||
ServerCertSANs: []string{
|
||||
proxy,
|
||||
proxyIP,
|
||||
"1.2.3.L",
|
||||
"invalid,commas,in,DNS",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
altNames, err := GetEtcdAltNames(cfg)
|
||||
if err != nil {
|
||||
t.Fatalf("failed calling GetEtcdAltNames: %v", err)
|
||||
}
|
||||
|
||||
expectedDNSNames := []string{"myNode", "localhost", proxy}
|
||||
for _, DNSName := range expectedDNSNames {
|
||||
found := false
|
||||
for _, val := range altNames.DNSNames {
|
||||
if val == DNSName {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
t.Errorf("altNames does not contain DNSName %s", DNSName)
|
||||
}
|
||||
}
|
||||
|
||||
expectedIPAddresses := []string{"1.2.3.4", "127.0.0.1", net.IPv6loopback.String(), proxyIP}
|
||||
for _, IPAddress := range expectedIPAddresses {
|
||||
found := false
|
||||
for _, val := range altNames.IPs {
|
||||
if val.Equal(net.ParseIP(IPAddress)) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
t.Errorf("altNames does not contain IPAddress %s", IPAddress)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetEtcdPeerAltNames(t *testing.T) {
|
||||
hostname := "valid-hostname"
|
||||
proxy := "user-etcd-proxy"
|
||||
proxyIP := "10.10.10.100"
|
||||
advertiseIP := "1.2.3.4"
|
||||
cfg := &kubeadmapi.InitConfiguration{
|
||||
LocalAPIEndpoint: kubeadmapi.APIEndpoint{AdvertiseAddress: advertiseIP},
|
||||
ClusterConfiguration: kubeadmapi.ClusterConfiguration{
|
||||
Etcd: kubeadmapi.Etcd{
|
||||
Local: &kubeadmapi.LocalEtcd{
|
||||
PeerCertSANs: []string{
|
||||
proxy,
|
||||
proxyIP,
|
||||
"1.2.3.L",
|
||||
"invalid,commas,in,DNS",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
NodeRegistration: kubeadmapi.NodeRegistrationOptions{Name: hostname},
|
||||
}
|
||||
|
||||
altNames, err := GetEtcdPeerAltNames(cfg)
|
||||
if err != nil {
|
||||
t.Fatalf("failed calling GetEtcdPeerAltNames: %v", err)
|
||||
}
|
||||
|
||||
expectedDNSNames := []string{hostname, proxy}
|
||||
for _, DNSName := range expectedDNSNames {
|
||||
found := false
|
||||
for _, val := range altNames.DNSNames {
|
||||
if val == DNSName {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
t.Errorf("altNames does not contain DNSName %s", DNSName)
|
||||
}
|
||||
}
|
||||
|
||||
expectedIPAddresses := []string{advertiseIP, proxyIP}
|
||||
for _, IPAddress := range expectedIPAddresses {
|
||||
found := false
|
||||
for _, val := range altNames.IPs {
|
||||
if val.Equal(net.ParseIP(IPAddress)) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
t.Errorf("altNames does not contain IPAddress %s", IPAddress)
|
||||
}
|
||||
}
|
||||
}
|
1
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/pubkeypin/BUILD
generated
vendored
1
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/pubkeypin/BUILD
generated
vendored
@ -16,6 +16,7 @@ go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["pubkeypin.go"],
|
||||
importpath = "k8s.io/kubernetes/cmd/kubeadm/app/util/pubkeypin",
|
||||
deps = ["//vendor/github.com/pkg/errors:go_default_library"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
|
11
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/pubkeypin/pubkeypin.go
generated
vendored
11
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/pubkeypin/pubkeypin.go
generated
vendored
@ -22,8 +22,9 @@ import (
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -46,7 +47,7 @@ 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\"")
|
||||
return errors.New("invalid public key hash, expected \"format:value\"")
|
||||
}
|
||||
format, value := parts[0], parts[1]
|
||||
|
||||
@ -54,7 +55,7 @@ func (s *Set) Allow(pubKeyHashes ...string) error {
|
||||
case "sha256":
|
||||
return s.allowSHA256(value)
|
||||
default:
|
||||
return fmt.Errorf("unknown hash format %q", format)
|
||||
return errors.Errorf("unknown hash format %q", format)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@ -65,7 +66,7 @@ func (s *Set) Check(certificate *x509.Certificate) error {
|
||||
if s.checkSHA256(certificate) {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("public key %s not pinned", Hash(certificate))
|
||||
return errors.Errorf("public key %s not pinned", Hash(certificate))
|
||||
}
|
||||
|
||||
// Empty returns true if the Set contains no pinned public keys.
|
||||
@ -86,7 +87,7 @@ 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)
|
||||
return errors.Errorf("expected a %d byte SHA-256 hash, found %d bytes", sha256.Size, hashLength)
|
||||
}
|
||||
|
||||
// validate that the hash is valid hex
|
||||
|
40
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/runtime/BUILD
generated
vendored
Normal file
40
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/runtime/BUILD
generated
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["runtime.go"],
|
||||
importpath = "k8s.io/kubernetes/cmd/kubeadm/app/util/runtime",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//cmd/kubeadm/app/apis/kubeadm/v1beta1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/errors:go_default_library",
|
||||
"//vendor/github.com/pkg/errors:go_default_library",
|
||||
"//vendor/k8s.io/utils/exec:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["runtime_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//cmd/kubeadm/app/apis/kubeadm/v1beta1:go_default_library",
|
||||
"//vendor/github.com/pkg/errors:go_default_library",
|
||||
"//vendor/k8s.io/utils/exec:go_default_library",
|
||||
"//vendor/k8s.io/utils/exec/testing: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"],
|
||||
)
|
182
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/runtime/runtime.go
generated
vendored
Normal file
182
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/runtime/runtime.go
generated
vendored
Normal file
@ -0,0 +1,182 @@
|
||||
/*
|
||||
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 (
|
||||
"path/filepath"
|
||||
goruntime "runtime"
|
||||
"strings"
|
||||
|
||||
pkgerrors "github.com/pkg/errors"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/errors"
|
||||
kubeadmapiv1beta1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta1"
|
||||
utilsexec "k8s.io/utils/exec"
|
||||
)
|
||||
|
||||
// ContainerRuntime is an interface for working with container runtimes
|
||||
type ContainerRuntime interface {
|
||||
IsDocker() bool
|
||||
IsRunning() error
|
||||
ListKubeContainers() ([]string, error)
|
||||
RemoveContainers(containers []string) error
|
||||
PullImage(image string) error
|
||||
ImageExists(image string) (bool, error)
|
||||
}
|
||||
|
||||
// CRIRuntime is a struct that interfaces with the CRI
|
||||
type CRIRuntime struct {
|
||||
exec utilsexec.Interface
|
||||
criSocket string
|
||||
}
|
||||
|
||||
// DockerRuntime is a struct that interfaces with the Docker daemon
|
||||
type DockerRuntime struct {
|
||||
exec utilsexec.Interface
|
||||
}
|
||||
|
||||
// NewContainerRuntime sets up and returns a ContainerRuntime struct
|
||||
func NewContainerRuntime(execer utilsexec.Interface, criSocket string) (ContainerRuntime, error) {
|
||||
var toolName string
|
||||
var runtime ContainerRuntime
|
||||
|
||||
if criSocket != kubeadmapiv1beta1.DefaultCRISocket {
|
||||
toolName = "crictl"
|
||||
// !!! temporary work around crictl warning:
|
||||
// Using "/var/run/crio/crio.sock" as endpoint is deprecated,
|
||||
// please consider using full url format "unix:///var/run/crio/crio.sock"
|
||||
if filepath.IsAbs(criSocket) && goruntime.GOOS != "windows" {
|
||||
criSocket = "unix://" + criSocket
|
||||
}
|
||||
runtime = &CRIRuntime{execer, criSocket}
|
||||
} else {
|
||||
toolName = "docker"
|
||||
runtime = &DockerRuntime{execer}
|
||||
}
|
||||
|
||||
if _, err := execer.LookPath(toolName); err != nil {
|
||||
return nil, pkgerrors.Wrapf(err, "%s is required for container runtime", toolName)
|
||||
}
|
||||
|
||||
return runtime, nil
|
||||
}
|
||||
|
||||
// IsDocker returns true if the runtime is docker
|
||||
func (runtime *CRIRuntime) IsDocker() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsDocker returns true if the runtime is docker
|
||||
func (runtime *DockerRuntime) IsDocker() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// IsRunning checks if runtime is running
|
||||
func (runtime *CRIRuntime) IsRunning() error {
|
||||
if out, err := runtime.exec.Command("crictl", "-r", runtime.criSocket, "info").CombinedOutput(); err != nil {
|
||||
return pkgerrors.Wrapf(err, "container runtime is not running: output: %s, error", string(out))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsRunning checks if runtime is running
|
||||
func (runtime *DockerRuntime) IsRunning() error {
|
||||
if out, err := runtime.exec.Command("docker", "info").CombinedOutput(); err != nil {
|
||||
return pkgerrors.Wrapf(err, "container runtime is not running: output: %s, error", string(out))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListKubeContainers lists running k8s CRI pods
|
||||
func (runtime *CRIRuntime) ListKubeContainers() ([]string, error) {
|
||||
out, err := runtime.exec.Command("crictl", "-r", runtime.criSocket, "pods", "-q").CombinedOutput()
|
||||
if err != nil {
|
||||
return nil, pkgerrors.Wrapf(err, "output: %s, error", string(out))
|
||||
}
|
||||
pods := []string{}
|
||||
for _, pod := range strings.Fields(string(out)) {
|
||||
pods = append(pods, pod)
|
||||
}
|
||||
return pods, nil
|
||||
}
|
||||
|
||||
// ListKubeContainers lists running k8s containers
|
||||
func (runtime *DockerRuntime) ListKubeContainers() ([]string, error) {
|
||||
output, err := runtime.exec.Command("docker", "ps", "-a", "--filter", "name=k8s_", "-q").CombinedOutput()
|
||||
return strings.Fields(string(output)), err
|
||||
}
|
||||
|
||||
// RemoveContainers removes running k8s pods
|
||||
func (runtime *CRIRuntime) RemoveContainers(containers []string) error {
|
||||
errs := []error{}
|
||||
for _, container := range containers {
|
||||
out, err := runtime.exec.Command("crictl", "-r", runtime.criSocket, "stopp", container).CombinedOutput()
|
||||
if err != nil {
|
||||
// don't stop on errors, try to remove as many containers as possible
|
||||
errs = append(errs, pkgerrors.Wrapf(err, "failed to stop running pod %s: output: %s, error", container, string(out)))
|
||||
} else {
|
||||
out, err = runtime.exec.Command("crictl", "-r", runtime.criSocket, "rmp", container).CombinedOutput()
|
||||
if err != nil {
|
||||
errs = append(errs, pkgerrors.Wrapf(err, "failed to remove running container %s: output: %s, error", container, string(out)))
|
||||
}
|
||||
}
|
||||
}
|
||||
return errors.NewAggregate(errs)
|
||||
}
|
||||
|
||||
// RemoveContainers removes running containers
|
||||
func (runtime *DockerRuntime) RemoveContainers(containers []string) error {
|
||||
errs := []error{}
|
||||
for _, container := range containers {
|
||||
out, err := runtime.exec.Command("docker", "rm", "--force", "--volumes", container).CombinedOutput()
|
||||
if err != nil {
|
||||
// don't stop on errors, try to remove as many containers as possible
|
||||
errs = append(errs, pkgerrors.Wrapf(err, "failed to remove running container %s: output: %s, error", container, string(out)))
|
||||
}
|
||||
}
|
||||
return errors.NewAggregate(errs)
|
||||
}
|
||||
|
||||
// PullImage pulls the image
|
||||
func (runtime *CRIRuntime) PullImage(image string) error {
|
||||
out, err := runtime.exec.Command("crictl", "-r", runtime.criSocket, "pull", image).CombinedOutput()
|
||||
if err != nil {
|
||||
return pkgerrors.Wrapf(err, "output: %s, error", string(out))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// PullImage pulls the image
|
||||
func (runtime *DockerRuntime) PullImage(image string) error {
|
||||
out, err := runtime.exec.Command("docker", "pull", image).CombinedOutput()
|
||||
if err != nil {
|
||||
return pkgerrors.Wrapf(err, "output: %s, error", string(out))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ImageExists checks to see if the image exists on the system
|
||||
func (runtime *CRIRuntime) ImageExists(image string) (bool, error) {
|
||||
err := runtime.exec.Command("crictl", "-r", runtime.criSocket, "inspecti", image).Run()
|
||||
return err == nil, nil
|
||||
}
|
||||
|
||||
// ImageExists checks to see if the image exists on the system
|
||||
func (runtime *DockerRuntime) ImageExists(image string) (bool, error) {
|
||||
err := runtime.exec.Command("docker", "inspect", image).Run()
|
||||
return err == nil, nil
|
||||
}
|
307
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/runtime/runtime_test.go
generated
vendored
Normal file
307
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/runtime/runtime_test.go
generated
vendored
Normal file
@ -0,0 +1,307 @@
|
||||
/*
|
||||
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 (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
kubeadmapiv1beta1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta1"
|
||||
"k8s.io/utils/exec"
|
||||
fakeexec "k8s.io/utils/exec/testing"
|
||||
)
|
||||
|
||||
func TestNewContainerRuntime(t *testing.T) {
|
||||
execLookPathOK := fakeexec.FakeExec{
|
||||
LookPathFunc: func(cmd string) (string, error) { return "/usr/bin/crictl", nil },
|
||||
}
|
||||
execLookPathErr := fakeexec.FakeExec{
|
||||
LookPathFunc: func(cmd string) (string, error) { return "", errors.Errorf("%s not found", cmd) },
|
||||
}
|
||||
cases := []struct {
|
||||
name string
|
||||
execer fakeexec.FakeExec
|
||||
criSocket string
|
||||
isDocker bool
|
||||
isError bool
|
||||
}{
|
||||
{"valid: default cri socket", execLookPathOK, kubeadmapiv1beta1.DefaultCRISocket, true, false},
|
||||
{"valid: cri-o socket url", execLookPathOK, "unix:///var/run/crio/crio.sock", false, false},
|
||||
{"valid: cri-o socket path", execLookPathOK, "/var/run/crio/crio.sock", false, false},
|
||||
{"invalid: no crictl", execLookPathErr, "unix:///var/run/crio/crio.sock", false, true},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
runtime, err := NewContainerRuntime(&tc.execer, tc.criSocket)
|
||||
if err != nil {
|
||||
if !tc.isError {
|
||||
t.Fatalf("unexpected NewContainerRuntime error. criSocket: %s, error: %v", tc.criSocket, err)
|
||||
}
|
||||
return // expected error occurs, impossible to test runtime further
|
||||
}
|
||||
if tc.isError && err == nil {
|
||||
t.Fatalf("unexpected NewContainerRuntime success. criSocket: %s", tc.criSocket)
|
||||
}
|
||||
isDocker := runtime.IsDocker()
|
||||
if tc.isDocker != isDocker {
|
||||
t.Fatalf("unexpected isDocker() result %v for the criSocket %s", isDocker, tc.criSocket)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func genFakeActions(fcmd *fakeexec.FakeCmd, num int) []fakeexec.FakeCommandAction {
|
||||
var actions []fakeexec.FakeCommandAction
|
||||
for i := 0; i < num; i++ {
|
||||
actions = append(actions, func(cmd string, args ...string) exec.Cmd {
|
||||
return fakeexec.InitFakeCmd(fcmd, cmd, args...)
|
||||
})
|
||||
}
|
||||
return actions
|
||||
}
|
||||
|
||||
func TestIsRunning(t *testing.T) {
|
||||
fcmd := fakeexec.FakeCmd{
|
||||
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{
|
||||
func() ([]byte, error) { return nil, nil },
|
||||
func() ([]byte, error) { return []byte("error"), &fakeexec.FakeExitError{Status: 1} },
|
||||
func() ([]byte, error) { return nil, nil },
|
||||
func() ([]byte, error) { return []byte("error"), &fakeexec.FakeExitError{Status: 1} },
|
||||
},
|
||||
}
|
||||
|
||||
criExecer := fakeexec.FakeExec{
|
||||
CommandScript: genFakeActions(&fcmd, len(fcmd.CombinedOutputScript)),
|
||||
LookPathFunc: func(cmd string) (string, error) { return "/usr/bin/crictl", nil },
|
||||
}
|
||||
|
||||
dockerExecer := fakeexec.FakeExec{
|
||||
CommandScript: genFakeActions(&fcmd, len(fcmd.CombinedOutputScript)),
|
||||
LookPathFunc: func(cmd string) (string, error) { return "/usr/bin/docker", nil },
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
criSocket string
|
||||
execer fakeexec.FakeExec
|
||||
isError bool
|
||||
}{
|
||||
{"valid: CRI-O is running", "unix:///var/run/crio/crio.sock", criExecer, false},
|
||||
{"invalid: CRI-O is not running", "unix:///var/run/crio/crio.sock", criExecer, true},
|
||||
{"valid: docker is running", kubeadmapiv1beta1.DefaultCRISocket, dockerExecer, false},
|
||||
{"invalid: docker is not running", kubeadmapiv1beta1.DefaultCRISocket, dockerExecer, true},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
runtime, err := NewContainerRuntime(&tc.execer, tc.criSocket)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected NewContainerRuntime error: %v", err)
|
||||
}
|
||||
isRunning := runtime.IsRunning()
|
||||
if tc.isError && isRunning == nil {
|
||||
t.Error("unexpected IsRunning() success")
|
||||
}
|
||||
if !tc.isError && isRunning != nil {
|
||||
t.Error("unexpected IsRunning() error")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestListKubeContainers(t *testing.T) {
|
||||
fcmd := fakeexec.FakeCmd{
|
||||
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{
|
||||
func() ([]byte, error) { return []byte("k8s_p1\nk8s_p2"), nil },
|
||||
func() ([]byte, error) { return nil, &fakeexec.FakeExitError{Status: 1} },
|
||||
func() ([]byte, error) { return []byte("k8s_p1\nk8s_p2"), nil },
|
||||
},
|
||||
}
|
||||
execer := fakeexec.FakeExec{
|
||||
CommandScript: genFakeActions(&fcmd, len(fcmd.CombinedOutputScript)),
|
||||
LookPathFunc: func(cmd string) (string, error) { return "/usr/bin/crictl", nil },
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
criSocket string
|
||||
isError bool
|
||||
}{
|
||||
{"valid: list containers using CRI socket url", "unix:///var/run/crio/crio.sock", false},
|
||||
{"invalid: list containers using CRI socket url", "unix:///var/run/crio/crio.sock", true},
|
||||
{"valid: list containers using docker", kubeadmapiv1beta1.DefaultCRISocket, false},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
runtime, err := NewContainerRuntime(&execer, tc.criSocket)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected NewContainerRuntime error: %v", err)
|
||||
}
|
||||
|
||||
containers, err := runtime.ListKubeContainers()
|
||||
if tc.isError {
|
||||
if err == nil {
|
||||
t.Errorf("unexpected ListKubeContainers success")
|
||||
}
|
||||
return
|
||||
} else if err != nil {
|
||||
t.Errorf("unexpected ListKubeContainers error: %v", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(containers, []string{"k8s_p1", "k8s_p2"}) {
|
||||
t.Errorf("unexpected ListKubeContainers output: %v", containers)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveContainers(t *testing.T) {
|
||||
fakeOK := func() ([]byte, error) { return nil, nil }
|
||||
fakeErr := func() ([]byte, error) { return []byte("error"), &fakeexec.FakeExitError{Status: 1} }
|
||||
fcmd := fakeexec.FakeCmd{
|
||||
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{
|
||||
fakeOK, fakeOK, fakeOK, fakeOK, fakeOK, fakeOK, // Test case 1
|
||||
fakeOK, fakeOK, fakeOK, fakeErr, fakeOK, fakeOK,
|
||||
fakeErr, fakeOK, fakeOK, fakeErr, fakeOK,
|
||||
fakeOK, fakeOK, fakeOK,
|
||||
fakeOK, fakeErr, fakeOK,
|
||||
},
|
||||
}
|
||||
execer := fakeexec.FakeExec{
|
||||
CommandScript: genFakeActions(&fcmd, len(fcmd.CombinedOutputScript)),
|
||||
LookPathFunc: func(cmd string) (string, error) { return "/usr/bin/crictl", nil },
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
criSocket string
|
||||
containers []string
|
||||
isError bool
|
||||
}{
|
||||
{"valid: remove containers using CRI", "unix:///var/run/crio/crio.sock", []string{"k8s_p1", "k8s_p2", "k8s_p3"}, false}, // Test case 1
|
||||
{"invalid: CRI rmp failure", "unix:///var/run/crio/crio.sock", []string{"k8s_p1", "k8s_p2", "k8s_p3"}, true},
|
||||
{"invalid: CRI stopp failure", "unix:///var/run/crio/crio.sock", []string{"k8s_p1", "k8s_p2", "k8s_p3"}, true},
|
||||
{"valid: remove containers using docker", kubeadmapiv1beta1.DefaultCRISocket, []string{"k8s_c1", "k8s_c2", "k8s_c3"}, false},
|
||||
{"invalid: remove containers using docker", kubeadmapiv1beta1.DefaultCRISocket, []string{"k8s_c1", "k8s_c2", "k8s_c3"}, true},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
runtime, err := NewContainerRuntime(&execer, tc.criSocket)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected NewContainerRuntime error: %v, criSocket: %s", err, tc.criSocket)
|
||||
}
|
||||
|
||||
err = runtime.RemoveContainers(tc.containers)
|
||||
if !tc.isError && err != nil {
|
||||
t.Errorf("unexpected RemoveContainers errors: %v, criSocket: %s, containers: %v", err, tc.criSocket, tc.containers)
|
||||
}
|
||||
if tc.isError && err == nil {
|
||||
t.Errorf("unexpected RemoveContnainers success, criSocket: %s, containers: %v", tc.criSocket, tc.containers)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPullImage(t *testing.T) {
|
||||
fcmd := fakeexec.FakeCmd{
|
||||
CombinedOutputScript: []fakeexec.FakeCombinedOutputAction{
|
||||
func() ([]byte, error) { return nil, nil },
|
||||
func() ([]byte, error) { return []byte("error"), &fakeexec.FakeExitError{Status: 1} },
|
||||
func() ([]byte, error) { return nil, nil },
|
||||
func() ([]byte, error) { return []byte("error"), &fakeexec.FakeExitError{Status: 1} },
|
||||
},
|
||||
}
|
||||
execer := fakeexec.FakeExec{
|
||||
CommandScript: genFakeActions(&fcmd, len(fcmd.CombinedOutputScript)),
|
||||
LookPathFunc: func(cmd string) (string, error) { return "/usr/bin/crictl", nil },
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
criSocket string
|
||||
image string
|
||||
isError bool
|
||||
}{
|
||||
{"valid: pull image using CRI", "unix:///var/run/crio/crio.sock", "image1", false},
|
||||
{"invalid: CRI pull error", "unix:///var/run/crio/crio.sock", "image2", true},
|
||||
{"valid: pull image using docker", kubeadmapiv1beta1.DefaultCRISocket, "image1", false},
|
||||
{"invalide: docer pull error", kubeadmapiv1beta1.DefaultCRISocket, "image2", true},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
runtime, err := NewContainerRuntime(&execer, tc.criSocket)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected NewContainerRuntime error: %v, criSocket: %s", err, tc.criSocket)
|
||||
}
|
||||
|
||||
err = runtime.PullImage(tc.image)
|
||||
if !tc.isError && err != nil {
|
||||
t.Errorf("unexpected PullImage error: %v, criSocket: %s, image: %s", err, tc.criSocket, tc.image)
|
||||
}
|
||||
if tc.isError && err == nil {
|
||||
t.Errorf("unexpected PullImage success, criSocket: %s, image: %s", tc.criSocket, tc.image)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestImageExists(t *testing.T) {
|
||||
fcmd := fakeexec.FakeCmd{
|
||||
RunScript: []fakeexec.FakeRunAction{
|
||||
func() ([]byte, []byte, error) { return nil, nil, nil },
|
||||
func() ([]byte, []byte, error) { return nil, nil, &fakeexec.FakeExitError{Status: 1} },
|
||||
func() ([]byte, []byte, error) { return nil, nil, nil },
|
||||
func() ([]byte, []byte, error) { return nil, nil, &fakeexec.FakeExitError{Status: 1} },
|
||||
},
|
||||
}
|
||||
execer := fakeexec.FakeExec{
|
||||
CommandScript: genFakeActions(&fcmd, len(fcmd.RunScript)),
|
||||
LookPathFunc: func(cmd string) (string, error) { return "/usr/bin/crictl", nil },
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
criSocket string
|
||||
image string
|
||||
result bool
|
||||
}{
|
||||
{"valid: test if image exists using CRI", "unix:///var/run/crio/crio.sock", "image1", false},
|
||||
{"invalid: CRI inspecti failure", "unix:///var/run/crio/crio.sock", "image2", true},
|
||||
{"valid: test if image exists using docker", kubeadmapiv1beta1.DefaultCRISocket, "image1", false},
|
||||
{"invalid: docker inspect failure", kubeadmapiv1beta1.DefaultCRISocket, "image2", true},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
runtime, err := NewContainerRuntime(&execer, tc.criSocket)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected NewContainerRuntime error: %v, criSocket: %s", err, tc.criSocket)
|
||||
}
|
||||
|
||||
result, err := runtime.ImageExists(tc.image)
|
||||
if !tc.result != result {
|
||||
t.Errorf("unexpected ImageExists result: %t, criSocket: %s, image: %s, expected result: %t", err, tc.criSocket, tc.image, tc.result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
17
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/staticpod/BUILD
generated
vendored
17
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/staticpod/BUILD
generated
vendored
@ -13,11 +13,10 @@ go_test(
|
||||
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",
|
||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/intstr:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
@ -28,13 +27,13 @@ go_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/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",
|
||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/intstr:go_default_library",
|
||||
"//vendor/github.com/pkg/errors:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
|
55
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/staticpod/utils.go
generated
vendored
55
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/staticpod/utils.go
generated
vendored
@ -17,20 +17,21 @@ limitations under the License.
|
||||
package staticpod
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"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"
|
||||
@ -82,7 +83,7 @@ func ComponentResources(cpu string) v1.ResourceRequirements {
|
||||
}
|
||||
|
||||
// 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 {
|
||||
func ComponentProbe(cfg *kubeadmapi.InitConfiguration, componentName string, port int, path string, scheme v1.URIScheme) *v1.Probe {
|
||||
return &v1.Probe{
|
||||
Handler: v1.Handler{
|
||||
HTTPGet: &v1.HTTPGetAction{
|
||||
@ -99,7 +100,7 @@ func ComponentProbe(cfg *kubeadmapi.MasterConfiguration, componentName string, p
|
||||
}
|
||||
|
||||
// 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 {
|
||||
func EtcdProbe(cfg *kubeadmapi.InitConfiguration, 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)
|
||||
@ -146,6 +147,10 @@ func VolumeMapToSlice(volumes map[string]v1.Volume) []v1.Volume {
|
||||
v = append(v, vol)
|
||||
}
|
||||
|
||||
sort.Slice(v, func(i, j int) bool {
|
||||
return strings.Compare(v[i].Name, v[j].Name) == -1
|
||||
})
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
@ -157,6 +162,10 @@ func VolumeMountMapToSlice(volumeMounts map[string]v1.VolumeMount) []v1.VolumeMo
|
||||
v = append(v, volMount)
|
||||
}
|
||||
|
||||
sort.Slice(v, func(i, j int) bool {
|
||||
return strings.Compare(v[i].Name, v[j].Name) == -1
|
||||
})
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
@ -181,19 +190,19 @@ 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)
|
||||
return errors.Wrapf(err, "failed to create directory %q", manifestDir)
|
||||
}
|
||||
|
||||
// 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)
|
||||
return errors.Wrapf(err, "failed to marshal manifest for %q to YAML", componentName)
|
||||
}
|
||||
|
||||
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 errors.Wrapf(err, "failed to write static pod manifest file for %q (%q)", componentName, filename)
|
||||
}
|
||||
|
||||
return nil
|
||||
@ -203,12 +212,12 @@ func WriteStaticPodToDisk(componentName, manifestDir string, pod v1.Pod) error {
|
||||
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)
|
||||
return &v1.Pod{}, errors.Wrapf(err, "failed to read manifest for %q", manifestPath)
|
||||
}
|
||||
|
||||
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)
|
||||
return &v1.Pod{}, errors.Errorf("failed to unmarshal manifest for %q from YAML: %v", manifestPath, err)
|
||||
}
|
||||
|
||||
pod := obj.(*v1.Pod)
|
||||
@ -218,7 +227,7 @@ func ReadStaticPodFromDisk(manifestPath string) (*v1.Pod, error) {
|
||||
|
||||
// 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 {
|
||||
func GetProbeAddress(cfg *kubeadmapi.InitConfiguration, 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,
|
||||
@ -228,17 +237,15 @@ func GetProbeAddress(cfg *kubeadmapi.MasterConfiguration, componentName string)
|
||||
// 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
|
||||
if cfg.LocalAPIEndpoint.AdvertiseAddress != "" {
|
||||
return cfg.LocalAPIEndpoint.AdvertiseAddress
|
||||
}
|
||||
case componentName == kubeadmconstants.KubeControllerManager:
|
||||
if addr, exists := cfg.ControllerManagerExtraArgs[kubeControllerManagerAddressArg]; exists {
|
||||
if addr, exists := cfg.ControllerManager.ExtraArgs[kubeControllerManagerAddressArg]; exists {
|
||||
return addr
|
||||
}
|
||||
case componentName == kubeadmconstants.KubeScheduler:
|
||||
if addr, exists := cfg.SchedulerExtraArgs[kubeSchedulerAddressArg]; exists {
|
||||
if addr, exists := cfg.Scheduler.ExtraArgs[kubeSchedulerAddressArg]; exists {
|
||||
return addr
|
||||
}
|
||||
case componentName == kubeadmconstants.Etcd:
|
||||
@ -287,3 +294,17 @@ func GetProbeAddress(cfg *kubeadmapi.MasterConfiguration, componentName string)
|
||||
}
|
||||
return "127.0.0.1"
|
||||
}
|
||||
|
||||
// ManifestFilesAreEqual compares 2 files. It returns true if their contents are equal, false otherwise
|
||||
func ManifestFilesAreEqual(path1, path2 string) (bool, error) {
|
||||
content1, err := ioutil.ReadFile(path1)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
content2, err := ioutil.ReadFile(path2)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return bytes.Equal(content1, content2), nil
|
||||
}
|
||||
|
185
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/staticpod/utils_test.go
generated
vendored
185
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/staticpod/utils_test.go
generated
vendored
@ -22,13 +22,12 @@ import (
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"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"
|
||||
@ -46,7 +45,7 @@ func TestComponentResources(t *testing.T) {
|
||||
func TestComponentProbe(t *testing.T) {
|
||||
var tests = []struct {
|
||||
name string
|
||||
cfg *kubeadmapi.MasterConfiguration
|
||||
cfg *kubeadmapi.InitConfiguration
|
||||
component string
|
||||
port int
|
||||
path string
|
||||
@ -55,8 +54,8 @@ func TestComponentProbe(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "default apiserver advertise address with http",
|
||||
cfg: &kubeadmapi.MasterConfiguration{
|
||||
API: kubeadmapi.API{
|
||||
cfg: &kubeadmapi.InitConfiguration{
|
||||
LocalAPIEndpoint: kubeadmapi.APIEndpoint{
|
||||
AdvertiseAddress: "",
|
||||
},
|
||||
},
|
||||
@ -66,26 +65,10 @@ func TestComponentProbe(t *testing.T) {
|
||||
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{
|
||||
cfg: &kubeadmapi.InitConfiguration{
|
||||
LocalAPIEndpoint: kubeadmapi.APIEndpoint{
|
||||
AdvertiseAddress: "",
|
||||
},
|
||||
},
|
||||
@ -97,8 +80,8 @@ func TestComponentProbe(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "valid ipv4 apiserver advertise address with http",
|
||||
cfg: &kubeadmapi.MasterConfiguration{
|
||||
API: kubeadmapi.API{
|
||||
cfg: &kubeadmapi.InitConfiguration{
|
||||
LocalAPIEndpoint: kubeadmapi.APIEndpoint{
|
||||
AdvertiseAddress: "1.2.3.4",
|
||||
},
|
||||
},
|
||||
@ -110,8 +93,8 @@ func TestComponentProbe(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "valid ipv6 apiserver advertise address with http",
|
||||
cfg: &kubeadmapi.MasterConfiguration{
|
||||
API: kubeadmapi.API{
|
||||
cfg: &kubeadmapi.InitConfiguration{
|
||||
LocalAPIEndpoint: kubeadmapi.APIEndpoint{
|
||||
AdvertiseAddress: "2001:db8::1",
|
||||
},
|
||||
},
|
||||
@ -123,8 +106,12 @@ func TestComponentProbe(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "valid IPv4 controller-manager probe",
|
||||
cfg: &kubeadmapi.MasterConfiguration{
|
||||
ControllerManagerExtraArgs: map[string]string{"address": "1.2.3.4"},
|
||||
cfg: &kubeadmapi.InitConfiguration{
|
||||
ClusterConfiguration: kubeadmapi.ClusterConfiguration{
|
||||
ControllerManager: kubeadmapi.ControlPlaneComponent{
|
||||
ExtraArgs: map[string]string{"address": "1.2.3.4"},
|
||||
},
|
||||
},
|
||||
},
|
||||
component: kubeadmconstants.KubeControllerManager,
|
||||
port: 1,
|
||||
@ -134,8 +121,12 @@ func TestComponentProbe(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "valid IPv6 controller-manager probe",
|
||||
cfg: &kubeadmapi.MasterConfiguration{
|
||||
ControllerManagerExtraArgs: map[string]string{"address": "2001:db8::1"},
|
||||
cfg: &kubeadmapi.InitConfiguration{
|
||||
ClusterConfiguration: kubeadmapi.ClusterConfiguration{
|
||||
ControllerManager: kubeadmapi.ControlPlaneComponent{
|
||||
ExtraArgs: map[string]string{"address": "2001:db8::1"},
|
||||
},
|
||||
},
|
||||
},
|
||||
component: kubeadmconstants.KubeControllerManager,
|
||||
port: 1,
|
||||
@ -145,8 +136,12 @@ func TestComponentProbe(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "valid IPv4 scheduler probe",
|
||||
cfg: &kubeadmapi.MasterConfiguration{
|
||||
SchedulerExtraArgs: map[string]string{"address": "1.2.3.4"},
|
||||
cfg: &kubeadmapi.InitConfiguration{
|
||||
ClusterConfiguration: kubeadmapi.ClusterConfiguration{
|
||||
Scheduler: kubeadmapi.ControlPlaneComponent{
|
||||
ExtraArgs: map[string]string{"address": "1.2.3.4"},
|
||||
},
|
||||
},
|
||||
},
|
||||
component: kubeadmconstants.KubeScheduler,
|
||||
port: 1,
|
||||
@ -156,8 +151,12 @@ func TestComponentProbe(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "valid IPv6 scheduler probe",
|
||||
cfg: &kubeadmapi.MasterConfiguration{
|
||||
SchedulerExtraArgs: map[string]string{"address": "2001:db8::1"},
|
||||
cfg: &kubeadmapi.InitConfiguration{
|
||||
ClusterConfiguration: kubeadmapi.ClusterConfiguration{
|
||||
Scheduler: kubeadmapi.ControlPlaneComponent{
|
||||
ExtraArgs: map[string]string{"address": "2001:db8::1"},
|
||||
},
|
||||
},
|
||||
},
|
||||
component: kubeadmconstants.KubeScheduler,
|
||||
port: 1,
|
||||
@ -194,7 +193,7 @@ func TestComponentProbe(t *testing.T) {
|
||||
func TestEtcdProbe(t *testing.T) {
|
||||
var tests = []struct {
|
||||
name string
|
||||
cfg *kubeadmapi.MasterConfiguration
|
||||
cfg *kubeadmapi.ClusterConfiguration
|
||||
component string
|
||||
port int
|
||||
certsDir string
|
||||
@ -205,7 +204,7 @@ func TestEtcdProbe(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "valid etcd probe using listen-client-urls IPv4 addresses",
|
||||
cfg: &kubeadmapi.MasterConfiguration{
|
||||
cfg: &kubeadmapi.ClusterConfiguration{
|
||||
Etcd: kubeadmapi.Etcd{
|
||||
Local: &kubeadmapi.LocalEtcd{
|
||||
ExtraArgs: map[string]string{
|
||||
@ -223,7 +222,7 @@ func TestEtcdProbe(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "valid etcd probe using listen-client-urls unspecified IPv6 address",
|
||||
cfg: &kubeadmapi.MasterConfiguration{
|
||||
cfg: &kubeadmapi.ClusterConfiguration{
|
||||
Etcd: kubeadmapi.Etcd{
|
||||
Local: &kubeadmapi.LocalEtcd{
|
||||
ExtraArgs: map[string]string{
|
||||
@ -241,7 +240,7 @@ func TestEtcdProbe(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "valid etcd probe using listen-client-urls unspecified IPv6 address 2",
|
||||
cfg: &kubeadmapi.MasterConfiguration{
|
||||
cfg: &kubeadmapi.ClusterConfiguration{
|
||||
Etcd: kubeadmapi.Etcd{
|
||||
Local: &kubeadmapi.LocalEtcd{
|
||||
ExtraArgs: map[string]string{
|
||||
@ -259,7 +258,7 @@ func TestEtcdProbe(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "valid etcd probe using listen-client-urls unspecified IPv6 address 3",
|
||||
cfg: &kubeadmapi.MasterConfiguration{
|
||||
cfg: &kubeadmapi.ClusterConfiguration{
|
||||
Etcd: kubeadmapi.Etcd{
|
||||
Local: &kubeadmapi.LocalEtcd{
|
||||
ExtraArgs: map[string]string{
|
||||
@ -277,7 +276,7 @@ func TestEtcdProbe(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "valid etcd probe using listen-client-urls unspecified IPv4 address",
|
||||
cfg: &kubeadmapi.MasterConfiguration{
|
||||
cfg: &kubeadmapi.ClusterConfiguration{
|
||||
Etcd: kubeadmapi.Etcd{
|
||||
Local: &kubeadmapi.LocalEtcd{
|
||||
ExtraArgs: map[string]string{
|
||||
@ -295,7 +294,7 @@ func TestEtcdProbe(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "valid etcd probe using listen-client-urls IPv6 addresses",
|
||||
cfg: &kubeadmapi.MasterConfiguration{
|
||||
cfg: &kubeadmapi.ClusterConfiguration{
|
||||
Etcd: kubeadmapi.Etcd{
|
||||
Local: &kubeadmapi.LocalEtcd{
|
||||
ExtraArgs: map[string]string{
|
||||
@ -313,7 +312,7 @@ func TestEtcdProbe(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "valid IPv4 etcd probe using hostname for listen-client-urls",
|
||||
cfg: &kubeadmapi.MasterConfiguration{
|
||||
cfg: &kubeadmapi.ClusterConfiguration{
|
||||
Etcd: kubeadmapi.Etcd{
|
||||
Local: &kubeadmapi.LocalEtcd{
|
||||
ExtraArgs: map[string]string{
|
||||
@ -331,7 +330,11 @@ func TestEtcdProbe(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, rt := range tests {
|
||||
actual := EtcdProbe(rt.cfg, rt.component, rt.port, rt.certsDir, rt.cacert, rt.cert, rt.key)
|
||||
// TODO: Make EtcdProbe accept a ClusterConfiguration object instead of InitConfiguration
|
||||
initcfg := &kubeadmapi.InitConfiguration{
|
||||
ClusterConfiguration: *rt.cfg,
|
||||
}
|
||||
actual := EtcdProbe(initcfg, 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,
|
||||
@ -466,13 +469,19 @@ func TestVolumeMapToSlice(t *testing.T) {
|
||||
"foo": {
|
||||
Name: "foo",
|
||||
},
|
||||
"bar": {
|
||||
Name: "bar",
|
||||
},
|
||||
}
|
||||
volumeSlice := VolumeMapToSlice(testVolumes)
|
||||
if len(volumeSlice) != 1 {
|
||||
if len(volumeSlice) != 2 {
|
||||
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)
|
||||
if volumeSlice[0].Name != "bar" {
|
||||
t.Errorf("Expected first volume name \"bar\", got %s", volumeSlice[0].Name)
|
||||
}
|
||||
if volumeSlice[1].Name != "foo" {
|
||||
t.Errorf("Expected second volume name \"foo\", got %s", volumeSlice[1].Name)
|
||||
}
|
||||
}
|
||||
|
||||
@ -481,13 +490,19 @@ func TestVolumeMountMapToSlice(t *testing.T) {
|
||||
"foo": {
|
||||
Name: "foo",
|
||||
},
|
||||
"bar": {
|
||||
Name: "bar",
|
||||
},
|
||||
}
|
||||
volumeMountSlice := VolumeMountMapToSlice(testVolumeMounts)
|
||||
if len(volumeMountSlice) != 1 {
|
||||
if len(volumeMountSlice) != 2 {
|
||||
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)
|
||||
if volumeMountSlice[0].Name != "bar" {
|
||||
t.Errorf("Expected first volume mount name \"bar\", got %s", volumeMountSlice[0].Name)
|
||||
}
|
||||
if volumeMountSlice[1].Name != "foo" {
|
||||
t.Errorf("Expected second volume name \"foo\", got %s", volumeMountSlice[1].Name)
|
||||
}
|
||||
}
|
||||
|
||||
@ -607,3 +622,73 @@ func TestReadStaticPodFromDisk(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestManifestFilesAreEqual(t *testing.T) {
|
||||
var tests = []struct {
|
||||
description string
|
||||
podYamls []string
|
||||
expectedResult bool
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
description: "manifests are equal",
|
||||
podYamls: []string{validPod, validPod},
|
||||
expectedResult: true,
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
description: "manifests are not equal",
|
||||
podYamls: []string{validPod, validPod + "\n"},
|
||||
expectedResult: false,
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
description: "first manifest doesn't exist",
|
||||
podYamls: []string{validPod, ""},
|
||||
expectedResult: false,
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
description: "second manifest doesn't exist",
|
||||
podYamls: []string{"", validPod},
|
||||
expectedResult: false,
|
||||
expectErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, rt := range tests {
|
||||
tmpdir := testutil.SetupTempDir(t)
|
||||
defer os.RemoveAll(tmpdir)
|
||||
|
||||
// write 2 manifests
|
||||
for i := 0; i < 2; i++ {
|
||||
if rt.podYamls[i] != "" {
|
||||
manifestPath := filepath.Join(tmpdir, strconv.Itoa(i)+".yaml")
|
||||
err := ioutil.WriteFile(manifestPath, []byte(rt.podYamls[i]), 0644)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to write manifest file\n%s\n\tfatal error: %v", rt.description, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// compare them
|
||||
result, actualErr := ManifestFilesAreEqual(filepath.Join(tmpdir, "0.yaml"), filepath.Join(tmpdir, "1.yaml"))
|
||||
if result != rt.expectedResult {
|
||||
t.Errorf(
|
||||
"ManifestFilesAreEqual failed\n%s\nexpected result: %t\nactual result: %t",
|
||||
rt.description,
|
||||
rt.expectedResult,
|
||||
result,
|
||||
)
|
||||
}
|
||||
if (actualErr != nil) != rt.expectErr {
|
||||
t.Errorf(
|
||||
"ManifestFilesAreEqual failed\n%s\n\texpected error: %t\n\tgot: %t\n\tactual error: %v",
|
||||
rt.description,
|
||||
rt.expectErr,
|
||||
(actualErr != nil),
|
||||
actualErr,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
64
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/system/BUILD
generated
vendored
Normal file
64
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/system/BUILD
generated
vendored
Normal file
@ -0,0 +1,64 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"cgroup_validator.go",
|
||||
"docker_validator.go",
|
||||
"kernel_validator.go",
|
||||
"kernel_validator_helper.go",
|
||||
"os_validator.go",
|
||||
"package_validator.go",
|
||||
"report.go",
|
||||
"types.go",
|
||||
"types_unix.go",
|
||||
"types_windows.go",
|
||||
"validators.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/cmd/kubeadm/app/util/system",
|
||||
deps = [
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/errors:go_default_library",
|
||||
"//vendor/github.com/blang/semver:go_default_library",
|
||||
"//vendor/github.com/docker/docker/api/types:go_default_library",
|
||||
"//vendor/github.com/docker/docker/client:go_default_library",
|
||||
"//vendor/github.com/pkg/errors:go_default_library",
|
||||
"//vendor/k8s.io/klog:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"cgroup_validator_test.go",
|
||||
"docker_validator_test.go",
|
||||
"kernel_validator_test.go",
|
||||
"os_validator_test.go",
|
||||
"package_validator_test.go",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
tags = ["e2e"],
|
||||
deps = [
|
||||
"//vendor/github.com/docker/docker/api/types:go_default_library",
|
||||
"//vendor/github.com/pkg/errors:go_default_library",
|
||||
"//vendor/github.com/stretchr/testify/assert:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
96
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/system/cgroup_validator.go
generated
vendored
Normal file
96
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/system/cgroup_validator.go
generated
vendored
Normal file
@ -0,0 +1,96 @@
|
||||
/*
|
||||
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 system
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var _ Validator = &CgroupsValidator{}
|
||||
|
||||
type CgroupsValidator struct {
|
||||
Reporter Reporter
|
||||
}
|
||||
|
||||
func (c *CgroupsValidator) Name() string {
|
||||
return "cgroups"
|
||||
}
|
||||
|
||||
const (
|
||||
cgroupsConfigPrefix = "CGROUPS_"
|
||||
)
|
||||
|
||||
func (c *CgroupsValidator) Validate(spec SysSpec) (error, error) {
|
||||
subsystems, err := c.getCgroupSubsystems()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get cgroup subsystems")
|
||||
}
|
||||
return nil, c.validateCgroupSubsystems(spec.Cgroups, subsystems)
|
||||
}
|
||||
|
||||
func (c *CgroupsValidator) validateCgroupSubsystems(cgroupSpec, subsystems []string) error {
|
||||
missing := []string{}
|
||||
for _, cgroup := range cgroupSpec {
|
||||
found := false
|
||||
for _, subsystem := range subsystems {
|
||||
if cgroup == subsystem {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
item := cgroupsConfigPrefix + strings.ToUpper(cgroup)
|
||||
if found {
|
||||
c.Reporter.Report(item, "enabled", good)
|
||||
} else {
|
||||
c.Reporter.Report(item, "missing", bad)
|
||||
missing = append(missing, cgroup)
|
||||
}
|
||||
}
|
||||
if len(missing) > 0 {
|
||||
return errors.Errorf("missing cgroups: %s", strings.Join(missing, " "))
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (c *CgroupsValidator) getCgroupSubsystems() ([]string, error) {
|
||||
f, err := os.Open("/proc/cgroups")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
subsystems := []string{}
|
||||
s := bufio.NewScanner(f)
|
||||
for s.Scan() {
|
||||
if err := s.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
text := s.Text()
|
||||
if text[0] != '#' {
|
||||
parts := strings.Fields(text)
|
||||
if len(parts) >= 4 && parts[3] != "0" {
|
||||
subsystems = append(subsystems, parts[0])
|
||||
}
|
||||
}
|
||||
}
|
||||
return subsystems, nil
|
||||
}
|
56
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/system/cgroup_validator_test.go
generated
vendored
Normal file
56
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/system/cgroup_validator_test.go
generated
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
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 system
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestValidateCgroupSubsystem(t *testing.T) {
|
||||
v := &CgroupsValidator{
|
||||
Reporter: DefaultReporter,
|
||||
}
|
||||
cgroupSpec := []string{"system1", "system2"}
|
||||
for desc, test := range map[string]struct {
|
||||
cgroupSpec []string
|
||||
subsystems []string
|
||||
err bool
|
||||
}{
|
||||
"missing cgroup subsystem should report error": {
|
||||
subsystems: []string{"system1"},
|
||||
err: true,
|
||||
},
|
||||
"extra cgroup subsystems should not report error": {
|
||||
subsystems: []string{"system1", "system2", "system3"},
|
||||
err: false,
|
||||
},
|
||||
"subsystems the same with spec should not report error": {
|
||||
subsystems: []string{"system1", "system2"},
|
||||
err: false,
|
||||
},
|
||||
} {
|
||||
err := v.validateCgroupSubsystems(cgroupSpec, test.subsystems)
|
||||
if !test.err {
|
||||
assert.Nil(t, err, "%q: Expect error not to occur with cgroup", desc)
|
||||
} else {
|
||||
assert.NotNil(t, err, "%q: Expect error to occur with docker info", desc)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
100
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/system/docker_validator.go
generated
vendored
Normal file
100
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/system/docker_validator.go
generated
vendored
Normal file
@ -0,0 +1,100 @@
|
||||
/*
|
||||
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 system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"regexp"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var _ Validator = &DockerValidator{}
|
||||
|
||||
// DockerValidator validates docker configuration.
|
||||
type DockerValidator struct {
|
||||
Reporter Reporter
|
||||
}
|
||||
|
||||
func (d *DockerValidator) Name() string {
|
||||
return "docker"
|
||||
}
|
||||
|
||||
const (
|
||||
dockerConfigPrefix = "DOCKER_"
|
||||
latestValidatedDockerVersion = "18.06"
|
||||
)
|
||||
|
||||
// TODO(random-liu): Add more validating items.
|
||||
func (d *DockerValidator) Validate(spec SysSpec) (error, error) {
|
||||
if spec.RuntimeSpec.DockerSpec == nil {
|
||||
// If DockerSpec is not specified, assume current runtime is not
|
||||
// docker, skip the docker configuration validation.
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
c, err := client.NewClient(dockerEndpoint, "", nil, nil)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to create docker client")
|
||||
}
|
||||
info, err := c.Info(context.Background())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get docker info")
|
||||
}
|
||||
return d.validateDockerInfo(spec.RuntimeSpec.DockerSpec, info)
|
||||
}
|
||||
|
||||
func (d *DockerValidator) validateDockerInfo(spec *DockerSpec, info types.Info) (error, error) {
|
||||
// Validate docker version.
|
||||
matched := false
|
||||
for _, v := range spec.Version {
|
||||
r := regexp.MustCompile(v)
|
||||
if r.MatchString(info.ServerVersion) {
|
||||
d.Reporter.Report(dockerConfigPrefix+"VERSION", info.ServerVersion, good)
|
||||
matched = true
|
||||
}
|
||||
}
|
||||
if !matched {
|
||||
// If it's of the new Docker version scheme but didn't match above, it
|
||||
// must be a newer version than the most recently validated one.
|
||||
ver := `\d{2}\.\d+\.\d+(?:-[a-z]{2})?`
|
||||
r := regexp.MustCompile(ver)
|
||||
if r.MatchString(info.ServerVersion) {
|
||||
d.Reporter.Report(dockerConfigPrefix+"VERSION", info.ServerVersion, good)
|
||||
w := errors.Errorf(
|
||||
"this Docker version is not on the list of validated versions: %s. Latest validated version: %s",
|
||||
info.ServerVersion,
|
||||
latestValidatedDockerVersion,
|
||||
)
|
||||
return w, nil
|
||||
}
|
||||
d.Reporter.Report(dockerConfigPrefix+"VERSION", info.ServerVersion, bad)
|
||||
return nil, errors.Errorf("unsupported docker version: %s", info.ServerVersion)
|
||||
}
|
||||
// Validate graph driver.
|
||||
item := dockerConfigPrefix + "GRAPH_DRIVER"
|
||||
for _, gd := range spec.GraphDriver {
|
||||
if info.Driver == gd {
|
||||
d.Reporter.Report(item, info.Driver, good)
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
d.Reporter.Report(item, info.Driver, bad)
|
||||
return nil, errors.Errorf("unsupported graph driver: %s", info.Driver)
|
||||
}
|
103
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/system/docker_validator_test.go
generated
vendored
Normal file
103
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/system/docker_validator_test.go
generated
vendored
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
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 system
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestValidateDockerInfo(t *testing.T) {
|
||||
v := &DockerValidator{
|
||||
Reporter: DefaultReporter,
|
||||
}
|
||||
spec := &DockerSpec{
|
||||
Version: []string{`1\.1[1-3]\..*`, `17\.0[3,6,9]\..*`, `18\.06\..*`},
|
||||
GraphDriver: []string{"driver_1", "driver_2"},
|
||||
}
|
||||
for _, test := range []struct {
|
||||
info types.Info
|
||||
err bool
|
||||
warn bool
|
||||
}{
|
||||
{
|
||||
info: types.Info{Driver: "driver_1", ServerVersion: "1.10.1"},
|
||||
err: true,
|
||||
warn: false,
|
||||
},
|
||||
{
|
||||
info: types.Info{Driver: "bad_driver", ServerVersion: "1.11.1"},
|
||||
err: true,
|
||||
warn: false,
|
||||
},
|
||||
{
|
||||
info: types.Info{Driver: "driver_1", ServerVersion: "1.11.1"},
|
||||
err: false,
|
||||
warn: false,
|
||||
},
|
||||
{
|
||||
info: types.Info{Driver: "driver_2", ServerVersion: "1.12.1"},
|
||||
err: false,
|
||||
warn: false,
|
||||
},
|
||||
{
|
||||
info: types.Info{Driver: "driver_2", ServerVersion: "1.13.1"},
|
||||
err: false,
|
||||
warn: false,
|
||||
},
|
||||
{
|
||||
info: types.Info{Driver: "driver_2", ServerVersion: "17.03.0-ce"},
|
||||
err: false,
|
||||
warn: false,
|
||||
},
|
||||
{
|
||||
info: types.Info{Driver: "driver_2", ServerVersion: "17.06.0-ce"},
|
||||
err: false,
|
||||
warn: false,
|
||||
},
|
||||
{
|
||||
info: types.Info{Driver: "driver_2", ServerVersion: "17.09.0-ce"},
|
||||
err: false,
|
||||
warn: false,
|
||||
},
|
||||
{
|
||||
info: types.Info{Driver: "driver_2", ServerVersion: "18.06.0-ce"},
|
||||
err: false,
|
||||
warn: false,
|
||||
},
|
||||
{
|
||||
info: types.Info{Driver: "driver_2", ServerVersion: "18.09.0"},
|
||||
err: false,
|
||||
warn: true,
|
||||
},
|
||||
} {
|
||||
warn, err := v.validateDockerInfo(spec, test.info)
|
||||
if !test.err {
|
||||
assert.Nil(t, err, "Expect error not to occur with docker info %+v", test.info)
|
||||
} else {
|
||||
assert.NotNil(t, err, "Expect error to occur with docker info %+v", test.info)
|
||||
}
|
||||
if !test.warn {
|
||||
assert.Nil(t, warn, "Expect error not to occur with docker info %+v", test.info)
|
||||
} else {
|
||||
assert.NotNil(t, warn, "Expect error to occur with docker info %+v", test.info)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
264
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/system/kernel_validator.go
generated
vendored
Normal file
264
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/system/kernel_validator.go
generated
vendored
Normal file
@ -0,0 +1,264 @@
|
||||
/*
|
||||
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 system
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
pkgerrors "github.com/pkg/errors"
|
||||
"k8s.io/klog"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/errors"
|
||||
)
|
||||
|
||||
var _ Validator = &KernelValidator{}
|
||||
|
||||
// KernelValidator validates kernel. Currently only validate kernel version
|
||||
// and kernel configuration.
|
||||
type KernelValidator struct {
|
||||
kernelRelease string
|
||||
Reporter Reporter
|
||||
}
|
||||
|
||||
func (k *KernelValidator) Name() string {
|
||||
return "kernel"
|
||||
}
|
||||
|
||||
// kConfigOption is the possible kernel config option.
|
||||
type kConfigOption string
|
||||
|
||||
const (
|
||||
builtIn kConfigOption = "y"
|
||||
asModule kConfigOption = "m"
|
||||
leftOut kConfigOption = "n"
|
||||
|
||||
// validKConfigRegex is the regex matching kernel configuration line.
|
||||
validKConfigRegex = "^CONFIG_[A-Z0-9_]+=[myn]"
|
||||
// kConfigPrefix is the prefix of kernel configuration.
|
||||
kConfigPrefix = "CONFIG_"
|
||||
)
|
||||
|
||||
func (k *KernelValidator) Validate(spec SysSpec) (error, error) {
|
||||
helper := KernelValidatorHelperImpl{}
|
||||
release, err := helper.GetKernelReleaseVersion()
|
||||
if err != nil {
|
||||
return nil, pkgerrors.Wrap(err, "failed to get kernel release")
|
||||
}
|
||||
k.kernelRelease = release
|
||||
var errs []error
|
||||
errs = append(errs, k.validateKernelVersion(spec.KernelSpec))
|
||||
// only validate kernel config when necessary (currently no kernel config for windows)
|
||||
if len(spec.KernelSpec.Required) > 0 || len(spec.KernelSpec.Forbidden) > 0 || len(spec.KernelSpec.Optional) > 0 {
|
||||
errs = append(errs, k.validateKernelConfig(spec.KernelSpec))
|
||||
}
|
||||
return nil, errors.NewAggregate(errs)
|
||||
}
|
||||
|
||||
// validateKernelVersion validates the kernel version.
|
||||
func (k *KernelValidator) validateKernelVersion(kSpec KernelSpec) error {
|
||||
versionRegexps := kSpec.Versions
|
||||
for _, versionRegexp := range versionRegexps {
|
||||
r := regexp.MustCompile(versionRegexp)
|
||||
if r.MatchString(k.kernelRelease) {
|
||||
k.Reporter.Report("KERNEL_VERSION", k.kernelRelease, good)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
k.Reporter.Report("KERNEL_VERSION", k.kernelRelease, bad)
|
||||
return pkgerrors.Errorf("unsupported kernel release: %s", k.kernelRelease)
|
||||
}
|
||||
|
||||
// validateKernelConfig validates the kernel configurations.
|
||||
func (k *KernelValidator) validateKernelConfig(kSpec KernelSpec) error {
|
||||
allConfig, err := k.getKernelConfig()
|
||||
if err != nil {
|
||||
return pkgerrors.Wrap(err, "failed to parse kernel config")
|
||||
}
|
||||
return k.validateCachedKernelConfig(allConfig, kSpec)
|
||||
}
|
||||
|
||||
// validateCachedKernelConfig validates the kernel confgiurations cached in internal data type.
|
||||
func (k *KernelValidator) validateCachedKernelConfig(allConfig map[string]kConfigOption, kSpec KernelSpec) error {
|
||||
badConfigs := []string{}
|
||||
// reportAndRecord is a helper function to record bad config when
|
||||
// report.
|
||||
reportAndRecord := func(name, msg, desc string, result ValidationResultType) {
|
||||
if result == bad {
|
||||
badConfigs = append(badConfigs, name)
|
||||
}
|
||||
// report description when the config is bad or warn.
|
||||
if result != good && desc != "" {
|
||||
msg = msg + " - " + desc
|
||||
}
|
||||
k.Reporter.Report(name, msg, result)
|
||||
}
|
||||
const (
|
||||
required = iota
|
||||
optional
|
||||
forbidden
|
||||
)
|
||||
validateOpt := func(config KernelConfig, expect int) {
|
||||
var found, missing ValidationResultType
|
||||
switch expect {
|
||||
case required:
|
||||
found, missing = good, bad
|
||||
case optional:
|
||||
found, missing = good, warn
|
||||
case forbidden:
|
||||
found, missing = bad, good
|
||||
}
|
||||
var name string
|
||||
var opt kConfigOption
|
||||
var ok bool
|
||||
for _, name = range append([]string{config.Name}, config.Aliases...) {
|
||||
name = kConfigPrefix + name
|
||||
if opt, ok = allConfig[name]; ok {
|
||||
break
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
reportAndRecord(name, "not set", config.Description, missing)
|
||||
return
|
||||
}
|
||||
switch opt {
|
||||
case builtIn:
|
||||
reportAndRecord(name, "enabled", config.Description, found)
|
||||
case asModule:
|
||||
reportAndRecord(name, "enabled (as module)", config.Description, found)
|
||||
case leftOut:
|
||||
reportAndRecord(name, "disabled", config.Description, missing)
|
||||
default:
|
||||
reportAndRecord(name, fmt.Sprintf("unknown option: %s", opt), config.Description, missing)
|
||||
}
|
||||
}
|
||||
for _, config := range kSpec.Required {
|
||||
validateOpt(config, required)
|
||||
}
|
||||
for _, config := range kSpec.Optional {
|
||||
validateOpt(config, optional)
|
||||
}
|
||||
for _, config := range kSpec.Forbidden {
|
||||
validateOpt(config, forbidden)
|
||||
}
|
||||
if len(badConfigs) > 0 {
|
||||
return pkgerrors.Errorf("unexpected kernel config: %s", strings.Join(badConfigs, " "))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// getKernelConfigReader search kernel config file in a predefined list. Once the kernel config
|
||||
// file is found it will read the configurations into a byte buffer and return. If the kernel
|
||||
// config file is not found, it will try to load kernel config module and retry again.
|
||||
func (k *KernelValidator) getKernelConfigReader() (io.Reader, error) {
|
||||
possibePaths := []string{
|
||||
"/proc/config.gz",
|
||||
"/boot/config-" + k.kernelRelease,
|
||||
"/usr/src/linux-" + k.kernelRelease + "/.config",
|
||||
"/usr/src/linux/.config",
|
||||
"/usr/lib/modules/" + k.kernelRelease + "/config",
|
||||
"/usr/lib/ostree-boot/config-" + k.kernelRelease,
|
||||
"/usr/lib/kernel/config-" + k.kernelRelease,
|
||||
"/usr/src/linux-headers-" + k.kernelRelease + "/.config",
|
||||
"/lib/modules/" + k.kernelRelease + "/build/.config",
|
||||
}
|
||||
configsModule := "configs"
|
||||
modprobeCmd := "modprobe"
|
||||
// loadModule indicates whether we've tried to load kernel config module ourselves.
|
||||
loadModule := false
|
||||
for {
|
||||
for _, path := range possibePaths {
|
||||
_, err := os.Stat(path)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
// Buffer the whole file, so that we can close the file and unload
|
||||
// kernel config module in this function.
|
||||
b, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var r io.Reader
|
||||
r = bytes.NewReader(b)
|
||||
// This is a gzip file (config.gz), unzip it.
|
||||
if filepath.Ext(path) == ".gz" {
|
||||
r, err = gzip.NewReader(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
// If we've tried to load kernel config module, break and return error.
|
||||
if loadModule {
|
||||
break
|
||||
}
|
||||
// If the kernel config file is not found, try to load the kernel
|
||||
// config module and check again.
|
||||
output, err := exec.Command(modprobeCmd, configsModule).CombinedOutput()
|
||||
if err != nil {
|
||||
return nil, pkgerrors.Wrapf(err, "unable to load kernel module: %q, output: %q, err",
|
||||
configsModule, output)
|
||||
}
|
||||
// Unload the kernel config module to make sure the validation have no side effect.
|
||||
defer exec.Command(modprobeCmd, "-r", configsModule).Run()
|
||||
loadModule = true
|
||||
}
|
||||
return nil, pkgerrors.Errorf("no config path in %v is available", possibePaths)
|
||||
}
|
||||
|
||||
// getKernelConfig gets kernel config from kernel config file and convert kernel config to internal type.
|
||||
func (k *KernelValidator) getKernelConfig() (map[string]kConfigOption, error) {
|
||||
r, err := k.getKernelConfigReader()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return k.parseKernelConfig(r)
|
||||
}
|
||||
|
||||
// parseKernelConfig converts kernel config to internal type.
|
||||
func (k *KernelValidator) parseKernelConfig(r io.Reader) (map[string]kConfigOption, error) {
|
||||
config := map[string]kConfigOption{}
|
||||
regex := regexp.MustCompile(validKConfigRegex)
|
||||
s := bufio.NewScanner(r)
|
||||
for s.Scan() {
|
||||
if err := s.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
line := strings.TrimSpace(s.Text())
|
||||
if !regex.MatchString(line) {
|
||||
continue
|
||||
}
|
||||
fields := strings.Split(line, "=")
|
||||
if len(fields) != 2 {
|
||||
klog.Errorf("Unexpected fields number in config %q", line)
|
||||
continue
|
||||
}
|
||||
config[fields[0]] = kConfigOption(fields[1])
|
||||
}
|
||||
return config, nil
|
||||
|
||||
}
|
23
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/system/kernel_validator_helper.go
generated
vendored
Normal file
23
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/system/kernel_validator_helper.go
generated
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
/*
|
||||
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 system
|
||||
|
||||
// KernelValidatorHelper is an interface intended to help with os specific kernel validation
|
||||
type KernelValidatorHelper interface {
|
||||
// GetKernelReleaseVersion gets the current kernel release version of the system
|
||||
GetKernelReleaseVersion() (string, error)
|
||||
}
|
197
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/system/kernel_validator_test.go
generated
vendored
Normal file
197
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/system/kernel_validator_test.go
generated
vendored
Normal file
@ -0,0 +1,197 @@
|
||||
/*
|
||||
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 system
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestValidateKernelVersion(t *testing.T) {
|
||||
v := &KernelValidator{
|
||||
Reporter: DefaultReporter,
|
||||
}
|
||||
// Currently, testRegex is align with DefaultSysSpec.KernelVersion, but in the future
|
||||
// they may be different.
|
||||
// This is fine, because the test mainly tests the kernel version validation logic,
|
||||
// not the DefaultSysSpec. The DefaultSysSpec should be tested with node e2e.
|
||||
testRegex := []string{`3\.[1-9][0-9].*`, `4\..*`}
|
||||
for _, test := range []struct {
|
||||
version string
|
||||
err bool
|
||||
}{
|
||||
// first version regex matches
|
||||
{
|
||||
version: "3.19.9-99-test",
|
||||
err: false,
|
||||
},
|
||||
// one of version regexes matches
|
||||
{
|
||||
version: "4.4.14+",
|
||||
err: false,
|
||||
},
|
||||
// no version regex matches
|
||||
{
|
||||
version: "2.0.0",
|
||||
err: true,
|
||||
},
|
||||
{
|
||||
version: "5.0.0",
|
||||
err: true,
|
||||
},
|
||||
{
|
||||
version: "3.9.0",
|
||||
err: true,
|
||||
},
|
||||
} {
|
||||
v.kernelRelease = test.version
|
||||
err := v.validateKernelVersion(KernelSpec{Versions: testRegex})
|
||||
if !test.err {
|
||||
assert.Nil(t, err, "Expect error not to occur with kernel version %q", test.version)
|
||||
} else {
|
||||
assert.NotNil(t, err, "Expect error to occur with kenrel version %q", test.version)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateCachedKernelConfig(t *testing.T) {
|
||||
v := &KernelValidator{
|
||||
Reporter: DefaultReporter,
|
||||
}
|
||||
testKernelSpec := KernelSpec{
|
||||
Required: []KernelConfig{{Name: "REQUIRED_1"}, {Name: "REQUIRED_2", Aliases: []string{"ALIASE_REQUIRED_2"}}},
|
||||
Optional: []KernelConfig{{Name: "OPTIONAL_1"}, {Name: "OPTIONAL_2"}},
|
||||
Forbidden: []KernelConfig{
|
||||
{Name: "FORBIDDEN_1", Description: "TEST FORBIDDEN"},
|
||||
{Name: "FORBIDDEN_2", Aliases: []string{"ALIASE_FORBIDDEN_2"}},
|
||||
},
|
||||
}
|
||||
for c, test := range []struct {
|
||||
desc string
|
||||
config map[string]kConfigOption
|
||||
err bool
|
||||
}{
|
||||
{
|
||||
desc: "meet all required configurations should not report error.",
|
||||
config: map[string]kConfigOption{
|
||||
"REQUIRED_1": builtIn,
|
||||
"REQUIRED_2": asModule,
|
||||
},
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
desc: "one required configuration disabled should report error.",
|
||||
config: map[string]kConfigOption{
|
||||
"REQUIRED_1": leftOut,
|
||||
"REQUIRED_2": builtIn,
|
||||
},
|
||||
err: true,
|
||||
},
|
||||
{
|
||||
desc: "one required configuration missing should report error.",
|
||||
config: map[string]kConfigOption{
|
||||
"REQUIRED_1": builtIn,
|
||||
},
|
||||
err: true,
|
||||
},
|
||||
{
|
||||
desc: "alias of required configuration should not report error.",
|
||||
config: map[string]kConfigOption{
|
||||
"REQUIRED_1": builtIn,
|
||||
"ALIASE_REQUIRED_2": asModule,
|
||||
},
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
desc: "optional configuration set or not should not report error.",
|
||||
config: map[string]kConfigOption{
|
||||
"REQUIRED_1": builtIn,
|
||||
"REQUIRED_2": asModule,
|
||||
"OPTIONAL_1": builtIn,
|
||||
},
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
desc: "forbidden configuration disabled should not report error.",
|
||||
config: map[string]kConfigOption{
|
||||
"REQUIRED_1": builtIn,
|
||||
"REQUIRED_2": asModule,
|
||||
"FORBIDDEN_1": leftOut,
|
||||
},
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
desc: "forbidden configuration built-in should report error.",
|
||||
config: map[string]kConfigOption{
|
||||
"REQUIRED_1": builtIn,
|
||||
"REQUIRED_2": asModule,
|
||||
"FORBIDDEN_1": builtIn,
|
||||
},
|
||||
err: true,
|
||||
},
|
||||
{
|
||||
desc: "forbidden configuration built as module should report error.",
|
||||
config: map[string]kConfigOption{
|
||||
"REQUIRED_1": builtIn,
|
||||
"REQUIRED_2": asModule,
|
||||
"FORBIDDEN_1": asModule,
|
||||
},
|
||||
err: true,
|
||||
},
|
||||
{
|
||||
desc: "alias of forbidden configuration should report error.",
|
||||
config: map[string]kConfigOption{
|
||||
"REQUIRED_1": builtIn,
|
||||
"REQUIRED_2": asModule,
|
||||
"ALIASE_FORBIDDEN_2": asModule,
|
||||
},
|
||||
err: true,
|
||||
},
|
||||
} {
|
||||
t.Logf("TestCase #%d %s", c, test.desc)
|
||||
// Add kernel config prefix.
|
||||
for k, v := range test.config {
|
||||
delete(test.config, k)
|
||||
test.config[kConfigPrefix+k] = v
|
||||
}
|
||||
err := v.validateCachedKernelConfig(test.config, testKernelSpec)
|
||||
if !test.err {
|
||||
assert.Nil(t, err, "Expect error not to occur with kernel config %q", test.config)
|
||||
} else {
|
||||
assert.NotNil(t, err, "Expect error to occur with kenrel config %q", test.config)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateParseKernelConfig(t *testing.T) {
|
||||
config := `CONFIG_1=y
|
||||
CONFIG_2=m
|
||||
CONFIG_3=n`
|
||||
expected := map[string]kConfigOption{
|
||||
"CONFIG_1": builtIn,
|
||||
"CONFIG_2": asModule,
|
||||
"CONFIG_3": leftOut,
|
||||
}
|
||||
v := &KernelValidator{
|
||||
Reporter: DefaultReporter,
|
||||
}
|
||||
got, err := v.parseKernelConfig(bytes.NewReader([]byte(config)))
|
||||
assert.Nil(t, err, "Expect error not to occur when parse kernel configuration %q", config)
|
||||
assert.Equal(t, expected, got)
|
||||
}
|
51
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/system/os_validator.go
generated
vendored
Normal file
51
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/system/os_validator.go
generated
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
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 system
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var _ Validator = &OSValidator{}
|
||||
|
||||
type OSValidator struct {
|
||||
Reporter Reporter
|
||||
}
|
||||
|
||||
func (o *OSValidator) Name() string {
|
||||
return "os"
|
||||
}
|
||||
|
||||
func (o *OSValidator) Validate(spec SysSpec) (error, error) {
|
||||
os, err := exec.Command("uname").CombinedOutput()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get os name")
|
||||
}
|
||||
return nil, o.validateOS(strings.TrimSpace(string(os)), spec.OS)
|
||||
}
|
||||
|
||||
func (o *OSValidator) validateOS(os, specOS string) error {
|
||||
if os != specOS {
|
||||
o.Reporter.Report("OS", os, bad)
|
||||
return errors.Errorf("unsupported operating system: %s", os)
|
||||
}
|
||||
o.Reporter.Report("OS", os, good)
|
||||
return nil
|
||||
}
|
54
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/system/os_validator_test.go
generated
vendored
Normal file
54
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/system/os_validator_test.go
generated
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
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 system
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestValidateOS(t *testing.T) {
|
||||
v := &OSValidator{
|
||||
Reporter: DefaultReporter,
|
||||
}
|
||||
specOS := "Linux"
|
||||
for _, test := range []struct {
|
||||
os string
|
||||
err bool
|
||||
}{
|
||||
{
|
||||
os: "Linux",
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
os: "Windows",
|
||||
err: true,
|
||||
},
|
||||
{
|
||||
os: "Darwin",
|
||||
err: true,
|
||||
},
|
||||
} {
|
||||
err := v.validateOS(test.os, specOS)
|
||||
if !test.err {
|
||||
assert.Nil(t, err, "Expect error not to occur with os %q", test.os)
|
||||
} else {
|
||||
assert.NotNil(t, err, "Expect error to occur with os %q", test.os)
|
||||
}
|
||||
}
|
||||
}
|
326
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/system/package_validator.go
generated
vendored
Normal file
326
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/system/package_validator.go
generated
vendored
Normal file
@ -0,0 +1,326 @@
|
||||
/*
|
||||
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 system
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/errors"
|
||||
|
||||
"github.com/blang/semver"
|
||||
pkgerrors "github.com/pkg/errors"
|
||||
"k8s.io/klog"
|
||||
)
|
||||
|
||||
// semVerDotsCount is the number of dots in a valid semantic version.
|
||||
const semVerDotsCount int = 2
|
||||
|
||||
// packageManager is an interface that abstracts the basic operations of a
|
||||
// package manager.
|
||||
type packageManager interface {
|
||||
// getPackageVersion returns the version of the package given the
|
||||
// packageName, or an error if no such package exists.
|
||||
getPackageVersion(packageName string) (string, error)
|
||||
}
|
||||
|
||||
// newPackageManager returns the package manager on the running machine, and an
|
||||
// error if no package managers is available.
|
||||
func newPackageManager() (packageManager, error) {
|
||||
if m, ok := newDPKG(); ok {
|
||||
return m, nil
|
||||
}
|
||||
return nil, pkgerrors.New("failed to find package manager")
|
||||
}
|
||||
|
||||
// dpkg implements packageManager. It uses "dpkg-query" to retrieve package
|
||||
// information.
|
||||
type dpkg struct{}
|
||||
|
||||
// newDPKG returns a Debian package manager. It returns (nil, false) if no such
|
||||
// package manager exists on the running machine.
|
||||
func newDPKG() (packageManager, bool) {
|
||||
_, err := exec.LookPath("dpkg-query")
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
return dpkg{}, true
|
||||
}
|
||||
|
||||
// getPackageVersion returns the upstream package version for the package given
|
||||
// the packageName, and an error if no such package exists.
|
||||
func (_ dpkg) getPackageVersion(packageName string) (string, error) {
|
||||
output, err := exec.Command("dpkg-query", "--show", "--showformat='${Version}'", packageName).Output()
|
||||
if err != nil {
|
||||
return "", pkgerrors.Wrap(err, "dpkg-query failed")
|
||||
}
|
||||
version := extractUpstreamVersion(string(output))
|
||||
if version == "" {
|
||||
return "", pkgerrors.New("no version information")
|
||||
}
|
||||
return version, nil
|
||||
}
|
||||
|
||||
// packageValidator implements the Validator interface. It validates packages
|
||||
// and their versions.
|
||||
type packageValidator struct {
|
||||
reporter Reporter
|
||||
kernelRelease string
|
||||
osDistro string
|
||||
}
|
||||
|
||||
// Name returns the name of the package validator.
|
||||
func (self *packageValidator) Name() string {
|
||||
return "package"
|
||||
}
|
||||
|
||||
// Validate checks packages and their versions against the spec using the
|
||||
// package manager on the running machine, and returns an error on any
|
||||
// package/version mismatch.
|
||||
func (self *packageValidator) Validate(spec SysSpec) (error, error) {
|
||||
if len(spec.PackageSpecs) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var err error
|
||||
if self.kernelRelease, err = getKernelRelease(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if self.osDistro, err = getOSDistro(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
manager, err := newPackageManager()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
specs := applyPackageSpecOverride(spec.PackageSpecs, spec.PackageSpecOverrides, self.osDistro)
|
||||
return self.validate(specs, manager)
|
||||
}
|
||||
|
||||
// Validate checks packages and their versions against the packageSpecs using
|
||||
// the packageManager, and returns an error on any package/version mismatch.
|
||||
func (self *packageValidator) validate(packageSpecs []PackageSpec, manager packageManager) (error, error) {
|
||||
var errs []error
|
||||
for _, spec := range packageSpecs {
|
||||
// Substitute variables in package name.
|
||||
packageName := resolvePackageName(spec.Name, self.kernelRelease)
|
||||
|
||||
nameWithVerRange := fmt.Sprintf("%s (%s)", packageName, spec.VersionRange)
|
||||
|
||||
// Get the version of the package on the running machine.
|
||||
version, err := manager.getPackageVersion(packageName)
|
||||
if err != nil {
|
||||
klog.V(1).Infof("Failed to get the version for the package %q: %s\n", packageName, err)
|
||||
errs = append(errs, err)
|
||||
self.reporter.Report(nameWithVerRange, "not installed", bad)
|
||||
continue
|
||||
}
|
||||
|
||||
// Version requirement will not be enforced if version range is
|
||||
// not specified in the spec.
|
||||
if spec.VersionRange == "" {
|
||||
self.reporter.Report(packageName, version, good)
|
||||
continue
|
||||
}
|
||||
|
||||
// Convert both the version range in the spec and the version returned
|
||||
// from package manager to semantic version format, and then check if
|
||||
// the version is in the range.
|
||||
sv, err := semver.Make(toSemVer(version))
|
||||
if err != nil {
|
||||
klog.Errorf("Failed to convert %q to semantic version: %s\n", version, err)
|
||||
errs = append(errs, err)
|
||||
self.reporter.Report(nameWithVerRange, "internal error", bad)
|
||||
continue
|
||||
}
|
||||
versionRange := semver.MustParseRange(toSemVerRange(spec.VersionRange))
|
||||
if versionRange(sv) {
|
||||
self.reporter.Report(nameWithVerRange, version, good)
|
||||
} else {
|
||||
errs = append(errs, pkgerrors.Errorf("package \"%s %s\" does not meet the spec \"%s (%s)\"", packageName, sv, packageName, spec.VersionRange))
|
||||
self.reporter.Report(nameWithVerRange, version, bad)
|
||||
}
|
||||
}
|
||||
return nil, errors.NewAggregate(errs)
|
||||
}
|
||||
|
||||
// getKernelRelease returns the kernel release of the local machine.
|
||||
func getKernelRelease() (string, error) {
|
||||
output, err := exec.Command("uname", "-r").Output()
|
||||
if err != nil {
|
||||
return "", pkgerrors.Wrap(err, "failed to get kernel release")
|
||||
}
|
||||
return strings.TrimSpace(string(output)), nil
|
||||
}
|
||||
|
||||
// getOSDistro returns the OS distro of the local machine.
|
||||
func getOSDistro() (string, error) {
|
||||
f := "/etc/lsb-release"
|
||||
b, err := ioutil.ReadFile(f)
|
||||
if err != nil {
|
||||
return "", pkgerrors.Wrapf(err, "failed to read %q", f)
|
||||
}
|
||||
content := string(b)
|
||||
switch {
|
||||
case strings.Contains(content, "Ubuntu"):
|
||||
return "ubuntu", nil
|
||||
case strings.Contains(content, "Chrome OS"):
|
||||
return "cos", nil
|
||||
case strings.Contains(content, "CoreOS"):
|
||||
return "coreos", nil
|
||||
default:
|
||||
return "", pkgerrors.Errorf("failed to get OS distro: %s", content)
|
||||
}
|
||||
}
|
||||
|
||||
// resolvePackageName substitutes the variables in the packageName with the
|
||||
// local information.
|
||||
// E.g., "linux-headers-${KERNEL_RELEASE}" -> "linux-headers-4.4.0-75-generic".
|
||||
func resolvePackageName(packageName string, kernelRelease string) string {
|
||||
packageName = strings.Replace(packageName, "${KERNEL_RELEASE}", kernelRelease, -1)
|
||||
return packageName
|
||||
}
|
||||
|
||||
// applyPackageSpecOverride applies the package spec overrides for the given
|
||||
// osDistro to the packageSpecs and returns the applied result.
|
||||
func applyPackageSpecOverride(packageSpecs []PackageSpec, overrides []PackageSpecOverride, osDistro string) []PackageSpec {
|
||||
var override *PackageSpecOverride
|
||||
for _, o := range overrides {
|
||||
if o.OSDistro == osDistro {
|
||||
override = &o
|
||||
break
|
||||
}
|
||||
}
|
||||
if override == nil {
|
||||
return packageSpecs
|
||||
}
|
||||
|
||||
// Remove packages in the spec that matches the overrides in
|
||||
// Subtractions.
|
||||
var out []PackageSpec
|
||||
subtractions := make(map[string]bool)
|
||||
for _, spec := range override.Subtractions {
|
||||
subtractions[spec.Name] = true
|
||||
}
|
||||
for _, spec := range packageSpecs {
|
||||
if _, ok := subtractions[spec.Name]; !ok {
|
||||
out = append(out, spec)
|
||||
}
|
||||
}
|
||||
|
||||
// Add packages in the spec that matches the overrides in Additions.
|
||||
return append(out, override.Additions...)
|
||||
}
|
||||
|
||||
// extractUpstreamVersion returns the upstream version of the given full
|
||||
// version in dpkg format. E.g., "1:1.0.6-2ubuntu2.1" -> "1.0.6".
|
||||
func extractUpstreamVersion(version string) string {
|
||||
// The full version is in the format of
|
||||
// "[epoch:]upstream_version[-debian_revision]". See
|
||||
// https://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Version.
|
||||
version = strings.Trim(version, " '")
|
||||
if i := strings.Index(version, ":"); i != -1 {
|
||||
version = version[i+1:]
|
||||
}
|
||||
if i := strings.Index(version, "-"); i != -1 {
|
||||
version = version[:i]
|
||||
}
|
||||
return version
|
||||
}
|
||||
|
||||
// toSemVerRange converts the input to a semantic version range.
|
||||
// E.g., ">=1.0" -> ">=1.0.x"
|
||||
// ">=1" -> ">=1.x"
|
||||
// ">=1 <=2.3" -> ">=1.x <=2.3.x"
|
||||
// ">1 || >3.1.0 !4.2" -> ">1.x || >3.1.0 !4.2.x"
|
||||
func toSemVerRange(input string) string {
|
||||
var output []string
|
||||
fields := strings.Fields(input)
|
||||
for _, f := range fields {
|
||||
numDots, hasDigits := 0, false
|
||||
for _, c := range f {
|
||||
switch {
|
||||
case c == '.':
|
||||
numDots++
|
||||
case c >= '0' && c <= '9':
|
||||
hasDigits = true
|
||||
}
|
||||
}
|
||||
if hasDigits && numDots < semVerDotsCount {
|
||||
f = strings.TrimRight(f, " ")
|
||||
f += ".x"
|
||||
}
|
||||
output = append(output, f)
|
||||
}
|
||||
return strings.Join(output, " ")
|
||||
}
|
||||
|
||||
// toSemVer converts the input to a semantic version, and an empty string on
|
||||
// error.
|
||||
func toSemVer(version string) string {
|
||||
// Remove the first non-digit and non-dot character as well as the ones
|
||||
// following it.
|
||||
// E.g., "1.8.19p1" -> "1.8.19".
|
||||
if i := strings.IndexFunc(version, func(c rune) bool {
|
||||
if (c < '0' || c > '9') && c != '.' {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}); i != -1 {
|
||||
version = version[:i]
|
||||
}
|
||||
|
||||
// Remove the trailing dots if there's any, and then returns an empty
|
||||
// string if nothing left.
|
||||
version = strings.TrimRight(version, ".")
|
||||
if version == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
numDots := strings.Count(version, ".")
|
||||
switch {
|
||||
case numDots < semVerDotsCount:
|
||||
// Add minor version and patch version.
|
||||
// E.g. "1.18" -> "1.18.0" and "481" -> "481.0.0".
|
||||
version += strings.Repeat(".0", semVerDotsCount-numDots)
|
||||
case numDots > semVerDotsCount:
|
||||
// Remove anything beyond the patch version
|
||||
// E.g. "2.0.10.4" -> "2.0.10".
|
||||
for numDots != semVerDotsCount {
|
||||
if i := strings.LastIndex(version, "."); i != -1 {
|
||||
version = version[:i]
|
||||
numDots--
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove leading zeros in major/minor/patch version.
|
||||
// E.g., "2.02" -> "2.2"
|
||||
// "8.0.0095" -> "8.0.95"
|
||||
var subs []string
|
||||
for _, s := range strings.Split(version, ".") {
|
||||
s := strings.TrimLeft(s, "0")
|
||||
if s == "" {
|
||||
s = "0"
|
||||
}
|
||||
subs = append(subs, s)
|
||||
}
|
||||
return strings.Join(subs, ".")
|
||||
}
|
266
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/system/package_validator_test.go
generated
vendored
Normal file
266
vendor/k8s.io/kubernetes/cmd/kubeadm/app/util/system/package_validator_test.go
generated
vendored
Normal file
@ -0,0 +1,266 @@
|
||||
/*
|
||||
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 system
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func TestExtractUpstreamVersion(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
input: "",
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
input: "1.0.6",
|
||||
expected: "1.0.6",
|
||||
},
|
||||
{
|
||||
input: "1:1.0.6",
|
||||
expected: "1.0.6",
|
||||
},
|
||||
{
|
||||
input: "1.0.6-2ubuntu2.1",
|
||||
expected: "1.0.6",
|
||||
},
|
||||
{
|
||||
input: "1:1.0.6-2ubuntu2.1",
|
||||
expected: "1.0.6",
|
||||
},
|
||||
} {
|
||||
got := extractUpstreamVersion(test.input)
|
||||
if test.expected != got {
|
||||
t.Errorf("extractUpstreamVersion(%q) = %q, want %q", test.input, got, test.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestToSemVer(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
input: "",
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
input: "1.2.3",
|
||||
expected: "1.2.3",
|
||||
},
|
||||
{
|
||||
input: "1.8.19p1",
|
||||
expected: "1.8.19",
|
||||
},
|
||||
{
|
||||
input: "1.8.19.p1",
|
||||
expected: "1.8.19",
|
||||
},
|
||||
{
|
||||
input: "p1",
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
input: "1.18",
|
||||
expected: "1.18.0",
|
||||
},
|
||||
{
|
||||
input: "481",
|
||||
expected: "481.0.0",
|
||||
},
|
||||
{
|
||||
input: "2.0.10.4",
|
||||
expected: "2.0.10",
|
||||
},
|
||||
{
|
||||
input: "03",
|
||||
expected: "3.0.0",
|
||||
},
|
||||
{
|
||||
input: "2.02",
|
||||
expected: "2.2.0",
|
||||
},
|
||||
{
|
||||
input: "8.0.0095",
|
||||
expected: "8.0.95",
|
||||
},
|
||||
} {
|
||||
got := toSemVer(test.input)
|
||||
if test.expected != got {
|
||||
t.Errorf("toSemVer(%q) = %q, want %q", test.input, got, test.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestToSemVerRange(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
input: "",
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
input: ">=1.0.0",
|
||||
expected: ">=1.0.0",
|
||||
},
|
||||
{
|
||||
input: ">=1.0",
|
||||
expected: ">=1.0.x",
|
||||
},
|
||||
{
|
||||
input: ">=1",
|
||||
expected: ">=1.x",
|
||||
},
|
||||
{
|
||||
input: ">=1 || !2.3",
|
||||
expected: ">=1.x || !2.3.x",
|
||||
},
|
||||
{
|
||||
input: ">1 || >3.1.0 !4.2",
|
||||
expected: ">1.x || >3.1.0 !4.2.x",
|
||||
},
|
||||
} {
|
||||
got := toSemVerRange(test.input)
|
||||
if test.expected != got {
|
||||
t.Errorf("toSemVerRange(%q) = %q, want %q", test.input, got, test.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// testPackageManager implements the packageManager interface.
|
||||
type testPackageManager struct {
|
||||
packageVersions map[string]string
|
||||
}
|
||||
|
||||
func (m testPackageManager) getPackageVersion(packageName string) (string, error) {
|
||||
if v, ok := m.packageVersions[packageName]; ok {
|
||||
return v, nil
|
||||
}
|
||||
return "", errors.Errorf("package %q does not exist", packageName)
|
||||
}
|
||||
|
||||
func TestValidatePackageVersion(t *testing.T) {
|
||||
testKernelRelease := "test-kernel-release"
|
||||
manager := testPackageManager{
|
||||
packageVersions: map[string]string{
|
||||
"foo": "1.0.0",
|
||||
"bar": "2.1.0",
|
||||
"bar-" + testKernelRelease: "3.0.0",
|
||||
},
|
||||
}
|
||||
v := &packageValidator{
|
||||
reporter: DefaultReporter,
|
||||
kernelRelease: testKernelRelease,
|
||||
}
|
||||
for _, test := range []struct {
|
||||
desc string
|
||||
specs []PackageSpec
|
||||
err error
|
||||
}{
|
||||
{
|
||||
desc: "all packages meet the spec",
|
||||
specs: []PackageSpec{
|
||||
{Name: "foo", VersionRange: ">=1.0"},
|
||||
{Name: "bar", VersionRange: ">=2.0 <= 3.0"},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "package version does not meet the spec",
|
||||
specs: []PackageSpec{
|
||||
{Name: "foo", VersionRange: ">=1.0"},
|
||||
{Name: "bar", VersionRange: ">=3.0"},
|
||||
},
|
||||
err: errors.New("package \"bar 2.1.0\" does not meet the spec \"bar (>=3.0)\""),
|
||||
},
|
||||
{
|
||||
desc: "package not installed",
|
||||
specs: []PackageSpec{
|
||||
{Name: "baz"},
|
||||
},
|
||||
err: errors.New("package \"baz\" does not exist"),
|
||||
},
|
||||
{
|
||||
desc: "use variable in package name",
|
||||
specs: []PackageSpec{
|
||||
{Name: "bar-${KERNEL_RELEASE}", VersionRange: ">=3.0"},
|
||||
},
|
||||
},
|
||||
} {
|
||||
_, err := v.validate(test.specs, manager)
|
||||
if test.err == nil && err != nil {
|
||||
t.Errorf("%s: v.validate(): err = %s", test.desc, err)
|
||||
}
|
||||
if test.err != nil {
|
||||
if err == nil {
|
||||
t.Errorf("%s: v.validate() is expected to fail.", test.desc)
|
||||
} else if test.err.Error() != err.Error() {
|
||||
t.Errorf("%s: v.validate(): err = %q, want = %q", test.desc, err, test.err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyPackageOverride(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
overrides []PackageSpecOverride
|
||||
osDistro string
|
||||
specs []PackageSpec
|
||||
expected []PackageSpec
|
||||
}{
|
||||
{
|
||||
specs: []PackageSpec{{Name: "foo", VersionRange: ">=1.0"}},
|
||||
expected: []PackageSpec{{Name: "foo", VersionRange: ">=1.0"}},
|
||||
},
|
||||
{
|
||||
osDistro: "ubuntu",
|
||||
overrides: []PackageSpecOverride{
|
||||
{
|
||||
OSDistro: "ubuntu",
|
||||
Subtractions: []PackageSpec{{Name: "foo"}},
|
||||
Additions: []PackageSpec{{Name: "bar", VersionRange: ">=2.0"}},
|
||||
},
|
||||
},
|
||||
specs: []PackageSpec{{Name: "foo", VersionRange: ">=1.0"}},
|
||||
expected: []PackageSpec{{Name: "bar", VersionRange: ">=2.0"}},
|
||||
},
|
||||
{
|
||||
osDistro: "ubuntu",
|
||||
overrides: []PackageSpecOverride{
|
||||
{
|
||||
OSDistro: "debian",
|
||||
Subtractions: []PackageSpec{{Name: "foo"}},
|
||||
},
|
||||
},
|
||||
specs: []PackageSpec{{Name: "foo", VersionRange: ">=1.0"}},
|
||||
expected: []PackageSpec{{Name: "foo", VersionRange: ">=1.0"}},
|
||||
},
|
||||
} {
|
||||
got := applyPackageSpecOverride(test.specs, test.overrides, test.osDistro)
|
||||
if !reflect.DeepEqual(test.expected, got) {
|
||||
t.Errorf("applyPackageSpecOverride(%+v, %+v, %s) = %+v, want = %+v", test.specs, test.overrides, test.osDistro, got, test.expected)
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user