mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-29 14:37:00 +00:00
Merge pull request #130345 from HirazawaUi/kubeadm-yaml-json
kubeadm: Replace the yaml in the log/comments with a generic term.
This commit is contained in:
commit
eacb9beec1
@ -399,9 +399,9 @@ func TestNewCmdConfigPrintActionDefaults(t *testing.T) {
|
|||||||
t.Fatalf("Error from running the print command: %v", err)
|
t.Fatalf("Error from running the print command: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
gvkmap, err := kubeadmutil.SplitYAMLDocuments(output.Bytes())
|
gvkmap, err := kubeadmutil.SplitConfigDocuments(output.Bytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected failure of SplitYAMLDocuments: %v", err)
|
t.Fatalf("unexpected failure of SplitConfigDocuments: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
gotKinds := []string{}
|
gotKinds := []string{}
|
||||||
|
@ -86,7 +86,7 @@ func (h *handler) fromConfigMap(client clientset.Interface, cmName, cmKey string
|
|||||||
return nil, errors.Errorf("unexpected error when reading %s ConfigMap: %s key value pair missing", cmName, cmKey)
|
return nil, errors.Errorf("unexpected error when reading %s ConfigMap: %s key value pair missing", cmName, cmKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
gvkmap, err := kubeadmutil.SplitYAMLDocuments([]byte(configData))
|
gvkmap, err := kubeadmutil.SplitConfigDocuments([]byte(configData))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -78,9 +78,9 @@ func TestFetchFromDocumentMap(t *testing.T) {
|
|||||||
apiVersion: kubelet.config.k8s.io/v1beta1
|
apiVersion: kubelet.config.k8s.io/v1beta1
|
||||||
kind: KubeletConfiguration
|
kind: KubeletConfiguration
|
||||||
`)
|
`)
|
||||||
gvkmap, err := kubeadmutil.SplitYAMLDocuments([]byte(test))
|
gvkmap, err := kubeadmutil.SplitConfigDocuments([]byte(test))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected failure of SplitYAMLDocuments: %v", err)
|
t.Fatalf("unexpected failure of SplitConfigDocuments: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
clusterCfg := testClusterCfg()
|
clusterCfg := testClusterCfg()
|
||||||
|
@ -293,9 +293,9 @@ func TestConfigBaseUnmarshal(t *testing.T) {
|
|||||||
config: validUnmarshallableClusterConfig.obj,
|
config: validUnmarshallableClusterConfig.obj,
|
||||||
}
|
}
|
||||||
|
|
||||||
gvkmap, err := kubeadmutil.SplitYAMLDocuments([]byte(validUnmarshallableClusterConfig.yaml))
|
gvkmap, err := kubeadmutil.SplitConfigDocuments([]byte(validUnmarshallableClusterConfig.yaml))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected failure of SplitYAMLDocuments: %v", err)
|
t.Fatalf("unexpected failure of SplitConfigDocuments: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
got := &clusterConfig{
|
got := &clusterConfig{
|
||||||
@ -461,9 +461,9 @@ func runClusterConfigFromTest(t *testing.T, perform func(t *testing.T, in string
|
|||||||
|
|
||||||
func TestLoadingFromDocumentMap(t *testing.T) {
|
func TestLoadingFromDocumentMap(t *testing.T) {
|
||||||
runClusterConfigFromTest(t, func(t *testing.T, in string) (kubeadmapi.ComponentConfig, error) {
|
runClusterConfigFromTest(t, func(t *testing.T, in string) (kubeadmapi.ComponentConfig, error) {
|
||||||
gvkmap, err := kubeadmutil.SplitYAMLDocuments([]byte(in))
|
gvkmap, err := kubeadmutil.SplitConfigDocuments([]byte(in))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected failure of SplitYAMLDocuments: %v", err)
|
t.Fatalf("unexpected failure of SplitConfigDocuments: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return clusterConfigHandler.FromDocumentMap(gvkmap)
|
return clusterConfigHandler.FromDocumentMap(gvkmap)
|
||||||
|
@ -110,7 +110,7 @@ func ApplyPatchesToConfig(cfg *kubeadmapi.ClusterConfiguration, patchesDir strin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gvkmap, err := kubeadmutil.SplitYAMLDocuments(kubeletBytes)
|
gvkmap, err := kubeadmutil.SplitConfigDocuments(kubeletBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -60,7 +60,7 @@ func FetchInitConfigurationFromCluster(client clientset.Interface, printer outpu
|
|||||||
}
|
}
|
||||||
_, _ = printer.Printf("[%s] Reading configuration from the %q ConfigMap in namespace %q...\n",
|
_, _ = printer.Printf("[%s] Reading configuration from the %q ConfigMap in namespace %q...\n",
|
||||||
logPrefix, constants.KubeadmConfigConfigMap, metav1.NamespaceSystem)
|
logPrefix, constants.KubeadmConfigConfigMap, metav1.NamespaceSystem)
|
||||||
_, _ = printer.Printf("[%s] Use 'kubeadm init phase upload-config --config your-config.yaml' to re-upload it.\n", logPrefix)
|
_, _ = printer.Printf("[%s] Use 'kubeadm init phase upload-config --config your-config-file' to re-upload it.\n", logPrefix)
|
||||||
|
|
||||||
// Fetch the actual config from cluster
|
// Fetch the actual config from cluster
|
||||||
cfg, err := getInitConfigurationFromCluster(constants.KubernetesDir, client, newControlPlane, skipComponentConfigs)
|
cfg, err := getInitConfigurationFromCluster(constants.KubernetesDir, client, newControlPlane, skipComponentConfigs)
|
||||||
@ -214,7 +214,7 @@ func GetNodeRegistration(kubeconfigFile string, client clientset.Interface, node
|
|||||||
|
|
||||||
// getNodeNameFromKubeletConfig gets the node name from a kubelet config file
|
// getNodeNameFromKubeletConfig 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
|
// 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
|
// information in the local kubelet config configuration file.
|
||||||
func getNodeNameFromKubeletConfig(fileName string) (string, error) {
|
func getNodeNameFromKubeletConfig(fileName string) (string, error) {
|
||||||
// loads the kubelet.conf file
|
// loads the kubelet.conf file
|
||||||
config, err := clientcmd.LoadFromFile(fileName)
|
config, err := clientcmd.LoadFromFile(fileName)
|
||||||
|
@ -94,11 +94,11 @@ func validateSupportedVersion(gvk schema.GroupVersionKind, allowDeprecated, allo
|
|||||||
gvString := gvk.GroupVersion().String()
|
gvString := gvk.GroupVersion().String()
|
||||||
|
|
||||||
if useKubeadmVersion := oldKnownAPIVersions[gvString]; useKubeadmVersion != "" {
|
if useKubeadmVersion := oldKnownAPIVersions[gvString]; useKubeadmVersion != "" {
|
||||||
return errors.Errorf("your configuration file uses an old API spec: %q (kind: %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.", gvString, gvk.Kind, useKubeadmVersion)
|
return errors.Errorf("your configuration file uses an old API spec: %q (kind: %q). Please use kubeadm %s instead and run 'kubeadm config migrate --old-config old-config-file --new-config new-config-file', which will write the new, similar spec using a newer API version.", gvString, gvk.Kind, useKubeadmVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, present := deprecatedAPIVersions[gvString]; present && !allowDeprecated {
|
if _, present := deprecatedAPIVersions[gvString]; present && !allowDeprecated {
|
||||||
klog.Warningf("your configuration file uses a deprecated API spec: %q (kind: %q). Please use 'kubeadm config migrate --old-config old.yaml --new-config new.yaml', which will write the new, similar spec using a newer API version.", gvString, gvk.Kind)
|
klog.Warningf("your configuration file uses a deprecated API spec: %q (kind: %q). Please use 'kubeadm config migrate --old-config old-config-file --new-config new-config-file', which will write the new, similar spec using a newer API version.", gvString, gvk.Kind)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, present := experimentalAPIVersions[gvString]; present && !allowExperimental {
|
if _, present := experimentalAPIVersions[gvString]; present && !allowExperimental {
|
||||||
@ -256,7 +256,7 @@ func MigrateOldConfig(oldConfig []byte, allowExperimental bool, mutators migrate
|
|||||||
mutators = defaultMigrateMutators()
|
mutators = defaultMigrateMutators()
|
||||||
}
|
}
|
||||||
|
|
||||||
gvkmap, err := kubeadmutil.SplitYAMLDocuments(oldConfig)
|
gvkmap, err := kubeadmutil.SplitConfigDocuments(oldConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []byte{}, err
|
return []byte{}, err
|
||||||
}
|
}
|
||||||
@ -329,7 +329,7 @@ func MigrateOldConfig(oldConfig []byte, allowExperimental bool, mutators migrate
|
|||||||
// ValidateConfig takes a byte slice containing a kubeadm configuration and performs conversion
|
// ValidateConfig takes a byte slice containing a kubeadm configuration and performs conversion
|
||||||
// to internal types and validation.
|
// to internal types and validation.
|
||||||
func ValidateConfig(config []byte, allowExperimental bool) error {
|
func ValidateConfig(config []byte, allowExperimental bool) error {
|
||||||
gvkmap, err := kubeadmutil.SplitYAMLDocuments(config)
|
gvkmap, err := kubeadmutil.SplitConfigDocuments(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -29,8 +29,10 @@ import (
|
|||||||
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
"k8s.io/apimachinery/pkg/util/json"
|
||||||
"k8s.io/apimachinery/pkg/util/version"
|
"k8s.io/apimachinery/pkg/util/version"
|
||||||
apimachineryversion "k8s.io/apimachinery/pkg/version"
|
apimachineryversion "k8s.io/apimachinery/pkg/version"
|
||||||
|
"sigs.k8s.io/yaml"
|
||||||
|
|
||||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||||
kubeadmapiv1old "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta3"
|
kubeadmapiv1old "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta3"
|
||||||
@ -41,6 +43,14 @@ import (
|
|||||||
|
|
||||||
const KubeadmGroupName = "kubeadm.k8s.io"
|
const KubeadmGroupName = "kubeadm.k8s.io"
|
||||||
|
|
||||||
|
var formats = []struct {
|
||||||
|
name string
|
||||||
|
marshal func(interface{}) ([]byte, error)
|
||||||
|
}{
|
||||||
|
{name: "JSON", marshal: json.Marshal},
|
||||||
|
{name: "YAML", marshal: yaml.Marshal},
|
||||||
|
}
|
||||||
|
|
||||||
func TestValidateSupportedVersion(t *testing.T) {
|
func TestValidateSupportedVersion(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
gvk schema.GroupVersionKind
|
gvk schema.GroupVersionKind
|
||||||
|
@ -291,11 +291,12 @@ func LoadOrDefaultInitConfiguration(cfgPath string, versionedInitCfg *kubeadmapi
|
|||||||
}
|
}
|
||||||
|
|
||||||
// BytesToInitConfiguration converts a byte slice to an internal, defaulted and validated InitConfiguration object.
|
// BytesToInitConfiguration converts a byte slice to an internal, defaulted and validated InitConfiguration object.
|
||||||
// The map may contain many different YAML documents. These YAML documents are parsed one-by-one
|
// The map may contain many different YAML/JSON documents. These documents are parsed one-by-one
|
||||||
// and well-known ComponentConfig GroupVersionKinds are stored inside of the internal InitConfiguration struct.
|
// and well-known ComponentConfig GroupVersionKinds are stored inside of the internal InitConfiguration struct.
|
||||||
// The resulting InitConfiguration is then dynamically defaulted and validated prior to return.
|
// The resulting InitConfiguration is then dynamically defaulted and validated prior to return.
|
||||||
func BytesToInitConfiguration(b []byte, skipCRIDetect bool) (*kubeadmapi.InitConfiguration, error) {
|
func BytesToInitConfiguration(b []byte, skipCRIDetect bool) (*kubeadmapi.InitConfiguration, error) {
|
||||||
gvkmap, err := kubeadmutil.SplitYAMLDocuments(b)
|
// Split the YAML/JSON documents in the file into a DocumentMap
|
||||||
|
gvkmap, err := kubeadmutil.SplitConfigDocuments(b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -303,7 +304,7 @@ func BytesToInitConfiguration(b []byte, skipCRIDetect bool) (*kubeadmapi.InitCon
|
|||||||
return documentMapToInitConfiguration(gvkmap, false, false, false, skipCRIDetect)
|
return documentMapToInitConfiguration(gvkmap, false, false, false, skipCRIDetect)
|
||||||
}
|
}
|
||||||
|
|
||||||
// documentMapToInitConfiguration converts a map of GVKs and YAML documents to defaulted and validated configuration object.
|
// documentMapToInitConfiguration converts a map of GVKs and YAML/JSON documents to defaulted and validated configuration object.
|
||||||
func documentMapToInitConfiguration(gvkmap kubeadmapi.DocumentMap, allowDeprecated, allowExperimental, strictErrors, skipCRIDetect bool) (*kubeadmapi.InitConfiguration, error) {
|
func documentMapToInitConfiguration(gvkmap kubeadmapi.DocumentMap, allowDeprecated, allowExperimental, strictErrors, skipCRIDetect bool) (*kubeadmapi.InitConfiguration, error) {
|
||||||
var initcfg *kubeadmapi.InitConfiguration
|
var initcfg *kubeadmapi.InitConfiguration
|
||||||
var clustercfg *kubeadmapi.ClusterConfiguration
|
var clustercfg *kubeadmapi.ClusterConfiguration
|
||||||
@ -326,7 +327,7 @@ func documentMapToInitConfiguration(gvkmap kubeadmapi.DocumentMap, allowDeprecat
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify the validity of the YAML
|
// verify the validity of the JSON/YAML
|
||||||
if err := strict.VerifyUnmarshalStrict([]*runtime.Scheme{kubeadmscheme.Scheme, componentconfigs.Scheme}, gvk, fileContent); err != nil {
|
if err := strict.VerifyUnmarshalStrict([]*runtime.Scheme{kubeadmscheme.Scheme, componentconfigs.Scheme}, gvk, fileContent); err != nil {
|
||||||
if !strictErrors {
|
if !strictErrors {
|
||||||
klog.Warning(err.Error())
|
klog.Warning(err.Error())
|
||||||
@ -358,13 +359,13 @@ func documentMapToInitConfiguration(gvkmap kubeadmapi.DocumentMap, allowDeprecat
|
|||||||
|
|
||||||
// If the group is neither a kubeadm core type or of a supported component config group, we dump a warning about it being ignored
|
// If the group is neither a kubeadm core type or of a supported component config group, we dump a warning about it being ignored
|
||||||
if !componentconfigs.Scheme.IsGroupRegistered(gvk.Group) {
|
if !componentconfigs.Scheme.IsGroupRegistered(gvk.Group) {
|
||||||
klog.Warningf("[config] WARNING: Ignored YAML document with GroupVersionKind %v\n", gvk)
|
klog.Warningf("[config] WARNING: Ignored configuration document with GroupVersionKind %v\n", gvk)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enforce that InitConfiguration and/or ClusterConfiguration has to exist among the YAML documents
|
// Enforce that InitConfiguration and/or ClusterConfiguration has to exist among the configuration documents
|
||||||
if initcfg == nil && clustercfg == nil {
|
if initcfg == nil && clustercfg == nil {
|
||||||
return nil, errors.New("no InitConfiguration or ClusterConfiguration kind was found in the YAML file")
|
return nil, errors.New("no InitConfiguration or ClusterConfiguration kind was found in the configuration file")
|
||||||
}
|
}
|
||||||
|
|
||||||
// If InitConfiguration wasn't given, default it by creating an external struct instance, default it and convert into the internal type
|
// If InitConfiguration wasn't given, default it by creating an external struct instance, default it and convert into the internal type
|
||||||
@ -408,7 +409,7 @@ func documentMapToInitConfiguration(gvkmap kubeadmapi.DocumentMap, allowDeprecat
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MarshalInitConfigurationToBytes marshals the internal InitConfiguration object to bytes. It writes the embedded
|
// MarshalInitConfigurationToBytes marshals the internal InitConfiguration object to bytes. It writes the embedded
|
||||||
// ClusterConfiguration object with ComponentConfigs out as separate YAML documents
|
// ClusterConfiguration object with ComponentConfigs out as separate YAML/JSON documents
|
||||||
func MarshalInitConfigurationToBytes(cfg *kubeadmapi.InitConfiguration, gv schema.GroupVersion) ([]byte, error) {
|
func MarshalInitConfigurationToBytes(cfg *kubeadmapi.InitConfiguration, gv schema.GroupVersion) ([]byte, error) {
|
||||||
initbytes, err := kubeadmutil.MarshalToYamlForCodecs(cfg, gv, kubeadmscheme.Codecs)
|
initbytes, err := kubeadmutil.MarshalToYamlForCodecs(cfg, gv, kubeadmscheme.Codecs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -17,110 +17,76 @@ limitations under the License.
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
|
"github.com/google/go-cmp/cmp/cmpopts"
|
||||||
|
"github.com/lithammer/dedent"
|
||||||
|
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"sigs.k8s.io/yaml"
|
"k8s.io/utils/ptr"
|
||||||
|
|
||||||
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||||
kubeadmapiv1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta4"
|
kubeadmapiv1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta4"
|
||||||
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestLoadInitConfigurationFromFile(t *testing.T) {
|
func TestLoadInitConfigurationFromFile(t *testing.T) {
|
||||||
certDir := "/tmp/foo"
|
|
||||||
clusterCfg := []byte(fmt.Sprintf(`
|
|
||||||
apiVersion: %s
|
|
||||||
kind: ClusterConfiguration
|
|
||||||
certificatesDir: %s
|
|
||||||
kubernetesVersion: %s`, kubeadmapiv1.SchemeGroupVersion.String(), certDir, constants.MinimumControlPlaneVersion.String()))
|
|
||||||
|
|
||||||
// Create temp folder for the test case
|
|
||||||
tmpdir, err := os.MkdirTemp("", "")
|
tmpdir, err := os.MkdirTemp("", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Couldn't create tmpdir: %v", err)
|
t.Fatalf("Couldn't create tmpdir: %v", err)
|
||||||
}
|
}
|
||||||
defer os.RemoveAll(tmpdir)
|
defer func() {
|
||||||
|
if err := os.RemoveAll(tmpdir); err != nil {
|
||||||
|
t.Fatalf("Couldn't remove tmpdir: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
filename := "kubeadmConfig"
|
||||||
|
filePath := filepath.Join(tmpdir, filename)
|
||||||
|
options := LoadOrDefaultConfigurationOptions{}
|
||||||
|
|
||||||
// cfgFiles is in cluster_test.go
|
tests := []struct {
|
||||||
var tests = []struct {
|
|
||||||
name string
|
name string
|
||||||
fileContents []byte
|
cfgPath string
|
||||||
expectErr bool
|
fileContents string
|
||||||
validate func(*testing.T, *kubeadm.InitConfiguration)
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "v1beta3.partial1",
|
name: "Config file does not exists",
|
||||||
fileContents: cfgFiles["InitConfiguration_v1beta3"],
|
cfgPath: "tmp",
|
||||||
|
wantErr: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "v1beta3.partial2",
|
name: "Valid kubeadm config",
|
||||||
fileContents: bytes.Join([][]byte{
|
cfgPath: filePath,
|
||||||
cfgFiles["InitConfiguration_v1beta3"],
|
fileContents: dedent.Dedent(`
|
||||||
clusterCfg,
|
apiVersion: kubeadm.k8s.io/v1beta4
|
||||||
}, []byte(constants.YAMLDocumentSeparator)),
|
kind: InitConfiguration
|
||||||
validate: func(t *testing.T, cfg *kubeadm.InitConfiguration) {
|
`),
|
||||||
if cfg.ClusterConfiguration.CertificatesDir != certDir {
|
wantErr: false,
|
||||||
t.Errorf("CertificatesDir from ClusterConfiguration holds the wrong value, Expected: %v. Actual: %v", certDir, cfg.ClusterConfiguration.CertificatesDir)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "v1beta3.full",
|
|
||||||
fileContents: bytes.Join([][]byte{
|
|
||||||
cfgFiles["InitConfiguration_v1beta3"],
|
|
||||||
cfgFiles["ClusterConfiguration_v1beta3"],
|
|
||||||
cfgFiles["Kube-proxy_componentconfig"],
|
|
||||||
cfgFiles["Kubelet_componentconfig"],
|
|
||||||
}, []byte(constants.YAMLDocumentSeparator)),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "v1beta4.full",
|
|
||||||
fileContents: bytes.Join([][]byte{
|
|
||||||
cfgFiles["InitConfiguration_v1beta4"],
|
|
||||||
cfgFiles["ClusterConfiguration_v1beta4"],
|
|
||||||
cfgFiles["Kube-proxy_componentconfig"],
|
|
||||||
cfgFiles["Kubelet_componentconfig"],
|
|
||||||
}, []byte(constants.YAMLDocumentSeparator)),
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
for _, rt := range tests {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
t.Run(rt.name, func(t2 *testing.T) {
|
if tt.cfgPath == filePath {
|
||||||
cfgPath := filepath.Join(tmpdir, rt.name)
|
err = os.WriteFile(tt.cfgPath, []byte(tt.fileContents), 0644)
|
||||||
err := os.WriteFile(cfgPath, rt.fileContents, 0644)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Couldn't create file: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
opts := LoadOrDefaultConfigurationOptions{
|
|
||||||
SkipCRIDetect: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
obj, err := LoadInitConfigurationFromFile(cfgPath, opts)
|
|
||||||
if rt.expectErr {
|
|
||||||
if err == nil {
|
|
||||||
t.Error("Unexpected success")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error reading file: %v", err)
|
t.Fatalf("Couldn't write content to file: %v", err)
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if obj == nil {
|
|
||||||
t.Error("Unexpected nil return value")
|
|
||||||
}
|
}
|
||||||
|
defer func() {
|
||||||
|
if err := os.RemoveAll(filePath); err != nil {
|
||||||
|
t.Fatalf("Couldn't remove filePath: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
// exec additional validation on the returned value
|
|
||||||
if rt.validate != nil {
|
_, err = LoadInitConfigurationFromFile(tt.cfgPath, options)
|
||||||
rt.validate(t, obj)
|
if (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("LoadInitConfigurationFromFile() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -184,20 +150,167 @@ func TestDefaultTaintsMarshaling(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range tests {
|
for _, tc := range tests {
|
||||||
t.Run(tc.desc, func(t *testing.T) {
|
for _, format := range formats {
|
||||||
b, err := yaml.Marshal(tc.cfg)
|
t.Run(fmt.Sprintf("%s_%s", tc.desc, format.name), func(t *testing.T) {
|
||||||
if err != nil {
|
b, err := format.marshal(tc.cfg)
|
||||||
t.Fatalf("unexpected error while marshalling to YAML: %v", err)
|
if err != nil {
|
||||||
}
|
t.Fatalf("unexpected error while marshalling to %s: %v", format.name, err)
|
||||||
|
}
|
||||||
|
|
||||||
cfg, err := BytesToInitConfiguration(b, true)
|
cfg, err := BytesToInitConfiguration(b, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error of BytesToInitConfiguration: %v\nconfig: %s", err, string(b))
|
t.Fatalf("unexpected error of BytesToInitConfiguration: %v\nconfig: %s", err, string(b))
|
||||||
}
|
}
|
||||||
|
|
||||||
if tc.expectedTaintCnt != len(cfg.NodeRegistration.Taints) {
|
if tc.expectedTaintCnt != len(cfg.NodeRegistration.Taints) {
|
||||||
t.Fatalf("unexpected taints count\nexpected: %d\ngot: %d\ntaints: %v", tc.expectedTaintCnt, len(cfg.NodeRegistration.Taints), cfg.NodeRegistration.Taints)
|
t.Fatalf("unexpected taints count\nexpected: %d\ngot: %d\ntaints: %v", tc.expectedTaintCnt, len(cfg.NodeRegistration.Taints), cfg.NodeRegistration.Taints)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBytesToInitConfiguration(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
cfg interface{}
|
||||||
|
expectedCfg kubeadmapi.InitConfiguration
|
||||||
|
expectedError bool
|
||||||
|
skipCRIDetect bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "default config is set correctly",
|
||||||
|
cfg: kubeadmapiv1.InitConfiguration{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
APIVersion: kubeadmapiv1.SchemeGroupVersion.String(),
|
||||||
|
Kind: constants.InitConfigurationKind,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedCfg: kubeadmapi.InitConfiguration{
|
||||||
|
LocalAPIEndpoint: kubeadmapi.APIEndpoint{
|
||||||
|
AdvertiseAddress: "",
|
||||||
|
BindPort: 0,
|
||||||
|
},
|
||||||
|
NodeRegistration: kubeadmapi.NodeRegistrationOptions{
|
||||||
|
CRISocket: "unix:///var/run/containerd/containerd.sock",
|
||||||
|
Name: "",
|
||||||
|
Taints: []v1.Taint{
|
||||||
|
{
|
||||||
|
Key: "node-role.kubernetes.io/control-plane",
|
||||||
|
Effect: "NoSchedule",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ImagePullPolicy: "IfNotPresent",
|
||||||
|
ImagePullSerial: ptr.To(true),
|
||||||
|
},
|
||||||
|
ClusterConfiguration: kubeadmapi.ClusterConfiguration{
|
||||||
|
Etcd: kubeadmapi.Etcd{
|
||||||
|
Local: &kubeadmapi.LocalEtcd{
|
||||||
|
DataDir: "/var/lib/etcd",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
KubernetesVersion: "stable-1",
|
||||||
|
ImageRepository: kubeadmapiv1.DefaultImageRepository,
|
||||||
|
ClusterName: kubeadmapiv1.DefaultClusterName,
|
||||||
|
EncryptionAlgorithm: kubeadmapi.EncryptionAlgorithmType(kubeadmapiv1.DefaultEncryptionAlgorithm),
|
||||||
|
Networking: kubeadmapi.Networking{
|
||||||
|
ServiceSubnet: "10.96.0.0/12",
|
||||||
|
DNSDomain: "cluster.local",
|
||||||
|
},
|
||||||
|
CertificatesDir: "/etc/kubernetes/pki",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
skipCRIDetect: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "partial config with custom values",
|
||||||
|
cfg: kubeadmapiv1.InitConfiguration{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
APIVersion: kubeadmapiv1.SchemeGroupVersion.String(),
|
||||||
|
Kind: constants.InitConfigurationKind,
|
||||||
|
},
|
||||||
|
NodeRegistration: kubeadmapiv1.NodeRegistrationOptions{
|
||||||
|
Name: "test-node",
|
||||||
|
CRISocket: "unix:///var/run/containerd/containerd.sock",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedCfg: kubeadmapi.InitConfiguration{
|
||||||
|
LocalAPIEndpoint: kubeadmapi.APIEndpoint{
|
||||||
|
AdvertiseAddress: "",
|
||||||
|
BindPort: 0,
|
||||||
|
},
|
||||||
|
NodeRegistration: kubeadmapi.NodeRegistrationOptions{
|
||||||
|
CRISocket: "unix:///var/run/containerd/containerd.sock",
|
||||||
|
Name: "test-node",
|
||||||
|
Taints: []v1.Taint{
|
||||||
|
{
|
||||||
|
Key: "node-role.kubernetes.io/control-plane",
|
||||||
|
Effect: "NoSchedule",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ImagePullPolicy: "IfNotPresent",
|
||||||
|
ImagePullSerial: ptr.To(true),
|
||||||
|
},
|
||||||
|
ClusterConfiguration: kubeadmapi.ClusterConfiguration{
|
||||||
|
Etcd: kubeadmapi.Etcd{
|
||||||
|
Local: &kubeadmapi.LocalEtcd{
|
||||||
|
DataDir: "/var/lib/etcd",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
KubernetesVersion: "stable-1",
|
||||||
|
ImageRepository: kubeadmapiv1.DefaultImageRepository,
|
||||||
|
ClusterName: kubeadmapiv1.DefaultClusterName,
|
||||||
|
EncryptionAlgorithm: kubeadmapi.EncryptionAlgorithmType(kubeadmapiv1.DefaultEncryptionAlgorithm),
|
||||||
|
Networking: kubeadmapi.Networking{
|
||||||
|
ServiceSubnet: "10.96.0.0/12",
|
||||||
|
DNSDomain: "cluster.local",
|
||||||
|
},
|
||||||
|
CertificatesDir: "/etc/kubernetes/pki",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
skipCRIDetect: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid configuration type",
|
||||||
|
cfg: kubeadmapiv1.UpgradeConfiguration{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
APIVersion: kubeadmapiv1.SchemeGroupVersion.String(),
|
||||||
|
Kind: constants.UpgradeConfigurationKind,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedError: true,
|
||||||
|
skipCRIDetect: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tests {
|
||||||
|
for _, format := range formats {
|
||||||
|
t.Run(fmt.Sprintf("%s_%s", tc.name, format.name), func(t *testing.T) {
|
||||||
|
b, err := format.marshal(tc.cfg)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error marshaling %s: %v", format.name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg, err := BytesToInitConfiguration(b, tc.skipCRIDetect)
|
||||||
|
if (err != nil) != tc.expectedError {
|
||||||
|
t.Fatalf("expected error: %v, got error: %v\nError: %v", tc.expectedError, err != nil, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !tc.expectedError {
|
||||||
|
// Ignore dynamic fields that may be set during defaulting
|
||||||
|
diffOpts := []cmp.Option{
|
||||||
|
cmpopts.IgnoreFields(kubeadmapi.NodeRegistrationOptions{}, "Name"),
|
||||||
|
cmpopts.IgnoreFields(kubeadmapi.InitConfiguration{}, "Timeouts", "BootstrapTokens", "LocalAPIEndpoint"),
|
||||||
|
cmpopts.IgnoreFields(kubeadmapi.APIServer{}, "TimeoutForControlPlane"),
|
||||||
|
cmpopts.IgnoreFields(kubeadmapi.ClusterConfiguration{}, "ComponentConfigs", "KubernetesVersion",
|
||||||
|
"CertificateValidityPeriod", "CACertificateValidityPeriod"),
|
||||||
|
}
|
||||||
|
|
||||||
|
if diff := cmp.Diff(*cfg, tc.expectedCfg, diffOpts...); diff != "" {
|
||||||
|
t.Fatalf("unexpected configuration difference (-want +got):\n%s", diff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -87,7 +87,15 @@ func LoadJoinConfigurationFromFile(cfgPath string, opts LoadOrDefaultConfigurati
|
|||||||
return nil, errors.Wrapf(err, "unable to read config from %q ", cfgPath)
|
return nil, errors.Wrapf(err, "unable to read config from %q ", cfgPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
gvkmap, err := kubeadmutil.SplitYAMLDocuments(b)
|
return BytesToJoinConfiguration(b, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BytesToJoinConfiguration converts a byte slice to an internal, defaulted and validated JoinConfiguration object.
|
||||||
|
// The map may contain many different YAML/JSON documents. These documents are parsed one-by-one.
|
||||||
|
// The resulting JoinConfiguration is then dynamically defaulted and validated prior to return.
|
||||||
|
func BytesToJoinConfiguration(b []byte, opts LoadOrDefaultConfigurationOptions) (*kubeadmapi.JoinConfiguration, error) {
|
||||||
|
// Split the YAML/JSON documents in the file into a DocumentMap
|
||||||
|
gvkmap, err := kubeadmutil.SplitConfigDocuments(b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -95,13 +103,14 @@ func LoadJoinConfigurationFromFile(cfgPath string, opts LoadOrDefaultConfigurati
|
|||||||
return documentMapToJoinConfiguration(gvkmap, false, false, false, opts.SkipCRIDetect)
|
return documentMapToJoinConfiguration(gvkmap, false, false, false, opts.SkipCRIDetect)
|
||||||
}
|
}
|
||||||
|
|
||||||
// documentMapToJoinConfiguration takes a map between GVKs and YAML documents (as returned by SplitYAMLDocuments),
|
// documentMapToJoinConfiguration takes a map between GVKs and YAML/JSON documents (as returned by SplitYAMLDocuments),
|
||||||
// finds a JoinConfiguration, decodes it, dynamically defaults it and then validates it prior to return.
|
// finds a JoinConfiguration, decodes it, dynamically defaults it and then validates it prior to return.
|
||||||
func documentMapToJoinConfiguration(gvkmap kubeadmapi.DocumentMap, allowDeprecated, allowExperimental, strictErrors, skipCRIDetect bool) (*kubeadmapi.JoinConfiguration, error) {
|
func documentMapToJoinConfiguration(gvkmap kubeadmapi.DocumentMap, allowDeprecated, allowExperimental, strictErrors, skipCRIDetect bool) (*kubeadmapi.JoinConfiguration, error) {
|
||||||
joinBytes := []byte{}
|
joinBytes := []byte{}
|
||||||
for gvk, bytes := range gvkmap {
|
for gvk, bytes := range gvkmap {
|
||||||
// not interested in anything other than JoinConfiguration
|
// not interested in anything other than JoinConfiguration
|
||||||
if gvk.Kind != constants.JoinConfigurationKind {
|
if gvk.Kind != constants.JoinConfigurationKind {
|
||||||
|
klog.Warningf("[config] WARNING: Ignored configuration document with GroupVersionKind %v\n", gvk)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,7 +119,7 @@ func documentMapToJoinConfiguration(gvkmap kubeadmapi.DocumentMap, allowDeprecat
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify the validity of the YAML
|
// verify the validity of the YAML/JSON
|
||||||
if err := strict.VerifyUnmarshalStrict([]*runtime.Scheme{kubeadmscheme.Scheme}, gvk, bytes); err != nil {
|
if err := strict.VerifyUnmarshalStrict([]*runtime.Scheme{kubeadmscheme.Scheme}, gvk, bytes); err != nil {
|
||||||
if !strictErrors {
|
if !strictErrors {
|
||||||
klog.Warning(err.Error())
|
klog.Warning(err.Error())
|
||||||
|
@ -17,85 +17,184 @@ limitations under the License.
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
|
"github.com/google/go-cmp/cmp/cmpopts"
|
||||||
"github.com/lithammer/dedent"
|
"github.com/lithammer/dedent"
|
||||||
|
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/utils/ptr"
|
||||||
|
|
||||||
|
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||||
|
kubeadmapiv1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta4"
|
||||||
|
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestBytesToJoinConfiguration(t *testing.T) {
|
||||||
|
options := LoadOrDefaultConfigurationOptions{}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
cfg *kubeadmapiv1.JoinConfiguration
|
||||||
|
want *kubeadmapi.JoinConfiguration
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Normal configuration",
|
||||||
|
cfg: &kubeadmapiv1.JoinConfiguration{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
APIVersion: kubeadmapiv1.SchemeGroupVersion.String(),
|
||||||
|
Kind: constants.JoinConfigurationKind,
|
||||||
|
},
|
||||||
|
NodeRegistration: kubeadmapiv1.NodeRegistrationOptions{
|
||||||
|
Name: "node-1",
|
||||||
|
CRISocket: "unix:///var/run/crio/crio.sock",
|
||||||
|
},
|
||||||
|
CACertPath: "/some/cert.crt",
|
||||||
|
Discovery: kubeadmapiv1.Discovery{
|
||||||
|
BootstrapToken: &kubeadmapiv1.BootstrapTokenDiscovery{
|
||||||
|
Token: "abcdef.1234567890123456",
|
||||||
|
APIServerEndpoint: "1.2.3.4:6443",
|
||||||
|
CACertHashes: []string{"aaaa"},
|
||||||
|
},
|
||||||
|
TLSBootstrapToken: "abcdef.1234567890123456",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: &kubeadmapi.JoinConfiguration{
|
||||||
|
NodeRegistration: kubeadmapi.NodeRegistrationOptions{
|
||||||
|
Name: "node-1",
|
||||||
|
CRISocket: "unix:///var/run/crio/crio.sock",
|
||||||
|
ImagePullPolicy: "IfNotPresent",
|
||||||
|
ImagePullSerial: ptr.To(true),
|
||||||
|
},
|
||||||
|
CACertPath: "/some/cert.crt",
|
||||||
|
Discovery: kubeadmapi.Discovery{
|
||||||
|
BootstrapToken: &kubeadmapi.BootstrapTokenDiscovery{
|
||||||
|
Token: "abcdef.1234567890123456",
|
||||||
|
APIServerEndpoint: "1.2.3.4:6443",
|
||||||
|
CACertHashes: []string{"aaaa"},
|
||||||
|
},
|
||||||
|
TLSBootstrapToken: "abcdef.1234567890123456",
|
||||||
|
},
|
||||||
|
ControlPlane: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Only contains Discovery configuration",
|
||||||
|
cfg: &kubeadmapiv1.JoinConfiguration{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
APIVersion: kubeadmapiv1.SchemeGroupVersion.String(),
|
||||||
|
Kind: constants.JoinConfigurationKind,
|
||||||
|
},
|
||||||
|
Discovery: kubeadmapiv1.Discovery{
|
||||||
|
BootstrapToken: &kubeadmapiv1.BootstrapTokenDiscovery{
|
||||||
|
Token: "abcdef.1234567890123456",
|
||||||
|
APIServerEndpoint: "1.2.3.4:6443",
|
||||||
|
CACertHashes: []string{"aaaa"},
|
||||||
|
},
|
||||||
|
TLSBootstrapToken: "abcdef.1234567890123456",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: &kubeadmapi.JoinConfiguration{
|
||||||
|
NodeRegistration: kubeadmapi.NodeRegistrationOptions{
|
||||||
|
CRISocket: "unix:///var/run/containerd/containerd.sock",
|
||||||
|
ImagePullPolicy: "IfNotPresent",
|
||||||
|
ImagePullSerial: ptr.To(true),
|
||||||
|
},
|
||||||
|
CACertPath: "/etc/kubernetes/pki/ca.crt",
|
||||||
|
Discovery: kubeadmapi.Discovery{
|
||||||
|
BootstrapToken: &kubeadmapi.BootstrapTokenDiscovery{
|
||||||
|
Token: "abcdef.1234567890123456",
|
||||||
|
APIServerEndpoint: "1.2.3.4:6443",
|
||||||
|
CACertHashes: []string{"aaaa"},
|
||||||
|
},
|
||||||
|
TLSBootstrapToken: "abcdef.1234567890123456",
|
||||||
|
},
|
||||||
|
ControlPlane: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
for _, format := range formats {
|
||||||
|
t.Run(fmt.Sprintf("%s_%s", tt.name, format.name), func(t *testing.T) {
|
||||||
|
bytes, err := format.marshal(tt.cfg)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Could not marshal test config: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
got, _ := BytesToJoinConfiguration(bytes, options)
|
||||||
|
if diff := cmp.Diff(got, tt.want, cmpopts.IgnoreFields(kubeadmapi.JoinConfiguration{}, "Timeouts"),
|
||||||
|
cmpopts.IgnoreFields(kubeadmapi.Discovery{}, "Timeout"), cmpopts.IgnoreFields(kubeadmapi.NodeRegistrationOptions{}, "Name")); diff != "" {
|
||||||
|
t.Errorf("LoadJoinConfigurationFromFile returned unexpected diff (-want,+got):\n%s", diff)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestLoadJoinConfigurationFromFile(t *testing.T) {
|
func TestLoadJoinConfigurationFromFile(t *testing.T) {
|
||||||
// Create temp folder for the test case
|
// Create temp folder for the test case
|
||||||
tmpdir, err := os.MkdirTemp("", "")
|
tmpdir, err := os.MkdirTemp("", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Couldn't create tmpdir: %v", err)
|
t.Fatalf("Couldn't create tmpdir: %v", err)
|
||||||
}
|
}
|
||||||
defer os.RemoveAll(tmpdir)
|
defer func() {
|
||||||
|
if err := os.RemoveAll(tmpdir); err != nil {
|
||||||
|
t.Fatalf("Couldn't remove tmpdir: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
filename := "kubeadmConfig"
|
||||||
|
filePath := filepath.Join(tmpdir, filename)
|
||||||
|
options := LoadOrDefaultConfigurationOptions{}
|
||||||
|
|
||||||
// cfgFiles is in cluster_test.go
|
tests := []struct {
|
||||||
var tests = []struct {
|
|
||||||
name string
|
name string
|
||||||
|
cfgPath string
|
||||||
fileContents string
|
fileContents string
|
||||||
expectErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "empty file causes error",
|
name: "Config file does not exists",
|
||||||
expectErr: true,
|
cfgPath: "tmp",
|
||||||
|
wantErr: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Invalid v1beta4 causes error",
|
name: "Valid kubeadm config",
|
||||||
|
cfgPath: filePath,
|
||||||
fileContents: dedent.Dedent(`
|
fileContents: dedent.Dedent(`
|
||||||
apiVersion: kubeadm.k8s.io/v1beta4
|
apiVersion: kubeadm.k8s.io/v1beta4
|
||||||
kind: JoinConfiguration
|
discovery:
|
||||||
`),
|
bootstrapToken:
|
||||||
expectErr: true,
|
apiServerEndpoint: 1.2.3.4:6443
|
||||||
},
|
caCertHashes:
|
||||||
{
|
- aaaa
|
||||||
name: "valid v1beta4 is loaded",
|
token: abcdef.1234567890123456
|
||||||
fileContents: dedent.Dedent(`
|
tlsBootstrapToken: abcdef.1234567890123456
|
||||||
apiVersion: kubeadm.k8s.io/v1beta4
|
kind: JoinConfiguration`),
|
||||||
kind: JoinConfiguration
|
wantErr: false,
|
||||||
caCertPath: /etc/kubernetes/pki/ca.crt
|
|
||||||
nodeRegistration:
|
|
||||||
criSocket: "unix:///var/run/unknown.sock"
|
|
||||||
discovery:
|
|
||||||
bootstrapToken:
|
|
||||||
apiServerEndpoint: kube-apiserver:6443
|
|
||||||
token: abcdef.0123456789abcdef
|
|
||||||
unsafeSkipCAVerification: true
|
|
||||||
timeout: 5m0s
|
|
||||||
tlsBootstrapToken: abcdef.0123456789abcdef
|
|
||||||
`),
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
for _, rt := range tests {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
t.Run(rt.name, func(t2 *testing.T) {
|
if tt.cfgPath == filePath {
|
||||||
cfgPath := filepath.Join(tmpdir, rt.name)
|
err = os.WriteFile(tt.cfgPath, []byte(tt.fileContents), 0644)
|
||||||
err := os.WriteFile(cfgPath, []byte(rt.fileContents), 0644)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Couldn't create file: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
opts := LoadOrDefaultConfigurationOptions{
|
|
||||||
SkipCRIDetect: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
obj, err := LoadJoinConfigurationFromFile(cfgPath, opts)
|
|
||||||
if rt.expectErr {
|
|
||||||
if err == nil {
|
|
||||||
t.Error("Unexpected success")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error reading file: %v", err)
|
t.Fatalf("Couldn't write content to file: %v", err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
defer func() {
|
||||||
|
if err := os.RemoveAll(filePath); err != nil {
|
||||||
|
t.Fatalf("Couldn't remove filePath: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
if obj == nil {
|
_, err = LoadJoinConfigurationFromFile(tt.cfgPath, options)
|
||||||
t.Error("Unexpected nil return value")
|
if (err != nil) != tt.wantErr {
|
||||||
}
|
t.Errorf("LoadJoinConfigurationFromFile() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -91,7 +91,15 @@ func LoadResetConfigurationFromFile(cfgPath string, opts LoadOrDefaultConfigurat
|
|||||||
return nil, errors.Wrapf(err, "unable to read config from %q ", cfgPath)
|
return nil, errors.Wrapf(err, "unable to read config from %q ", cfgPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
gvkmap, err := kubeadmutil.SplitYAMLDocuments(b)
|
return BytesToResetConfiguration(b, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BytesToResetConfiguration converts a byte slice to an internal, defaulted and validated ResetConfiguration object.
|
||||||
|
// The map may contain many different YAML/JSON documents. These documents are parsed one-by-one.
|
||||||
|
// The resulting ResetConfiguration is then dynamically defaulted and validated prior to return.
|
||||||
|
func BytesToResetConfiguration(b []byte, opts LoadOrDefaultConfigurationOptions) (*kubeadmapi.ResetConfiguration, error) {
|
||||||
|
// Split the YAML/JSON documents in the file into a DocumentMap
|
||||||
|
gvkmap, err := kubeadmutil.SplitConfigDocuments(b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -99,13 +107,14 @@ func LoadResetConfigurationFromFile(cfgPath string, opts LoadOrDefaultConfigurat
|
|||||||
return documentMapToResetConfiguration(gvkmap, false, opts.AllowExperimental, false, opts.SkipCRIDetect)
|
return documentMapToResetConfiguration(gvkmap, false, opts.AllowExperimental, false, opts.SkipCRIDetect)
|
||||||
}
|
}
|
||||||
|
|
||||||
// documentMapToResetConfiguration takes a map between GVKs and YAML documents (as returned by SplitYAMLDocuments),
|
// documentMapToResetConfiguration takes a map between GVKs and YAML/JSON documents (as returned by SplitYAMLDocuments),
|
||||||
// finds a ResetConfiguration, decodes it, dynamically defaults it and then validates it prior to return.
|
// finds a ResetConfiguration, decodes it, dynamically defaults it and then validates it prior to return.
|
||||||
func documentMapToResetConfiguration(gvkmap kubeadmapi.DocumentMap, allowDeprecated, allowExperimental bool, strictErrors bool, skipCRIDetect bool) (*kubeadmapi.ResetConfiguration, error) {
|
func documentMapToResetConfiguration(gvkmap kubeadmapi.DocumentMap, allowDeprecated, allowExperimental bool, strictErrors bool, skipCRIDetect bool) (*kubeadmapi.ResetConfiguration, error) {
|
||||||
resetBytes := []byte{}
|
resetBytes := []byte{}
|
||||||
for gvk, bytes := range gvkmap {
|
for gvk, bytes := range gvkmap {
|
||||||
// not interested in anything other than ResetConfiguration
|
// not interested in anything other than ResetConfiguration
|
||||||
if gvk.Kind != constants.ResetConfigurationKind {
|
if gvk.Kind != constants.ResetConfigurationKind {
|
||||||
|
klog.Warningf("[config] WARNING: Ignored configuration document with GroupVersionKind %v\n", gvk)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,7 +123,7 @@ func documentMapToResetConfiguration(gvkmap kubeadmapi.DocumentMap, allowDepreca
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify the validity of the YAML
|
// verify the validity of the YAML/JSON
|
||||||
if err := strict.VerifyUnmarshalStrict([]*runtime.Scheme{kubeadmscheme.Scheme}, gvk, bytes); err != nil {
|
if err := strict.VerifyUnmarshalStrict([]*runtime.Scheme{kubeadmscheme.Scheme}, gvk, bytes); err != nil {
|
||||||
if !strictErrors {
|
if !strictErrors {
|
||||||
klog.Warning(err.Error())
|
klog.Warning(err.Error())
|
||||||
|
@ -17,86 +17,132 @@ limitations under the License.
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
|
"github.com/google/go-cmp/cmp/cmpopts"
|
||||||
"github.com/lithammer/dedent"
|
"github.com/lithammer/dedent"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||||
|
kubeadmapiv1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta4"
|
||||||
|
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestBytesToResetConfiguration(t *testing.T) {
|
||||||
|
options := LoadOrDefaultConfigurationOptions{}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
cfg *kubeadmapiv1.ResetConfiguration
|
||||||
|
want *kubeadmapi.ResetConfiguration
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Normal configuration",
|
||||||
|
cfg: &kubeadmapiv1.ResetConfiguration{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
APIVersion: kubeadmapiv1.SchemeGroupVersion.String(),
|
||||||
|
Kind: constants.ResetConfigurationKind,
|
||||||
|
},
|
||||||
|
CertificatesDir: "/custom/certs",
|
||||||
|
CleanupTmpDir: true,
|
||||||
|
},
|
||||||
|
want: &kubeadmapi.ResetConfiguration{
|
||||||
|
CertificatesDir: "/custom/certs",
|
||||||
|
CleanupTmpDir: true,
|
||||||
|
SkipPhases: nil,
|
||||||
|
CRISocket: "unix:///var/run/containerd/containerd.sock",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Default configuration",
|
||||||
|
cfg: &kubeadmapiv1.ResetConfiguration{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
APIVersion: kubeadmapiv1.SchemeGroupVersion.String(),
|
||||||
|
Kind: constants.ResetConfigurationKind,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: &kubeadmapi.ResetConfiguration{
|
||||||
|
CertificatesDir: "/etc/kubernetes/pki",
|
||||||
|
CleanupTmpDir: false,
|
||||||
|
SkipPhases: nil,
|
||||||
|
CRISocket: "unix:///var/run/containerd/containerd.sock",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
for _, format := range formats {
|
||||||
|
t.Run(fmt.Sprintf("%s_%s", tt.name, format.name), func(t *testing.T) {
|
||||||
|
bytes, err := format.marshal(tt.cfg)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Could not marshal test config: %v", err)
|
||||||
|
}
|
||||||
|
got, _ := BytesToResetConfiguration(bytes, options)
|
||||||
|
if diff := cmp.Diff(got, tt.want, cmpopts.IgnoreFields(kubeadmapi.ResetConfiguration{}, "Timeouts")); diff != "" {
|
||||||
|
t.Errorf("LoadResetConfigurationFromFile returned unexpected diff (-want,+got):\n%s", diff)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestLoadResetConfigurationFromFile(t *testing.T) {
|
func TestLoadResetConfigurationFromFile(t *testing.T) {
|
||||||
// Create temp folder for the test case
|
|
||||||
tmpdir, err := os.MkdirTemp("", "")
|
tmpdir, err := os.MkdirTemp("", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Couldn't create tmpdir: %v", err)
|
t.Fatalf("Couldn't create tmpdir: %v", err)
|
||||||
}
|
}
|
||||||
defer os.RemoveAll(tmpdir)
|
defer func() {
|
||||||
|
if err := os.RemoveAll(tmpdir); err != nil {
|
||||||
|
t.Fatalf("Couldn't remove tmpdir: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
filename := "kubeadmConfig"
|
||||||
|
filePath := filepath.Join(tmpdir, filename)
|
||||||
|
options := LoadOrDefaultConfigurationOptions{}
|
||||||
|
|
||||||
var tests = []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
cfgPath string
|
||||||
fileContents string
|
fileContents string
|
||||||
expectErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "empty file causes error",
|
name: "Config file does not exists",
|
||||||
expectErr: true,
|
cfgPath: "tmp",
|
||||||
|
wantErr: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Invalid v1beta4 causes error",
|
name: "Valid kubeadm config",
|
||||||
fileContents: dedent.Dedent(`
|
cfgPath: filePath,
|
||||||
apiVersion: kubeadm.k8s.io/unknownVersion
|
|
||||||
kind: ResetConfiguration
|
|
||||||
criSocket: unix:///var/run/containerd/containerd.sock
|
|
||||||
`),
|
|
||||||
expectErr: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "valid v1beta4 is loaded",
|
|
||||||
fileContents: dedent.Dedent(`
|
fileContents: dedent.Dedent(`
|
||||||
apiVersion: kubeadm.k8s.io/v1beta4
|
apiVersion: kubeadm.k8s.io/v1beta4
|
||||||
kind: ResetConfiguration
|
kind: ResetConfiguration`),
|
||||||
force: true
|
wantErr: false,
|
||||||
cleanupTmpDir: true
|
|
||||||
criSocket: unix:///var/run/containerd/containerd.sock
|
|
||||||
certificatesDir: /etc/kubernetes/pki
|
|
||||||
ignorePreflightErrors:
|
|
||||||
- a
|
|
||||||
- b
|
|
||||||
`),
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
for _, rt := range tests {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
t.Run(rt.name, func(t2 *testing.T) {
|
if tt.cfgPath == filePath {
|
||||||
cfgPath := filepath.Join(tmpdir, rt.name)
|
err = os.WriteFile(tt.cfgPath, []byte(tt.fileContents), 0644)
|
||||||
err := os.WriteFile(cfgPath, []byte(rt.fileContents), 0644)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Couldn't create file: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
opts := LoadOrDefaultConfigurationOptions{
|
|
||||||
AllowExperimental: true,
|
|
||||||
SkipCRIDetect: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
obj, err := LoadResetConfigurationFromFile(cfgPath, opts)
|
|
||||||
if rt.expectErr {
|
|
||||||
if err == nil {
|
|
||||||
t.Error("Unexpected success")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error reading file: %v", err)
|
t.Fatalf("Couldn't write content to file: %v", err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
defer func() {
|
||||||
|
if err := os.RemoveAll(filePath); err != nil {
|
||||||
|
t.Fatalf("Couldn't remove filePath: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
if obj == nil {
|
_, err = LoadResetConfigurationFromFile(tt.cfgPath, options)
|
||||||
t.Error("Unexpected nil return value")
|
if (err != nil) != tt.wantErr {
|
||||||
}
|
t.Errorf("LoadResetConfigurationFromFile() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -28,25 +28,19 @@ import (
|
|||||||
kubeadmscheme "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/scheme"
|
kubeadmscheme "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/scheme"
|
||||||
kubeadmapiv1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta4"
|
kubeadmapiv1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta4"
|
||||||
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation"
|
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation"
|
||||||
"k8s.io/kubernetes/cmd/kubeadm/app/componentconfigs"
|
|
||||||
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||||
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
||||||
"k8s.io/kubernetes/cmd/kubeadm/app/util/config/strict"
|
"k8s.io/kubernetes/cmd/kubeadm/app/util/config/strict"
|
||||||
)
|
)
|
||||||
|
|
||||||
// documentMapToUpgradeConfiguration takes a map between GVKs and YAML documents (as returned by SplitYAMLDocuments),
|
// documentMapToUpgradeConfiguration takes a map between GVKs and YAML/JSON documents (as returned by SplitYAMLDocuments),
|
||||||
// finds a UpgradeConfiguration, decodes it, dynamically defaults it and then validates it prior to return.
|
// finds a UpgradeConfiguration, decodes it, dynamically defaults it and then validates it prior to return.
|
||||||
func documentMapToUpgradeConfiguration(gvkmap kubeadmapi.DocumentMap, allowDeprecated bool) (*kubeadmapi.UpgradeConfiguration, error) {
|
func documentMapToUpgradeConfiguration(gvkmap kubeadmapi.DocumentMap, allowDeprecated bool) (*kubeadmapi.UpgradeConfiguration, error) {
|
||||||
upgradeBytes := []byte{}
|
upgradeBytes := []byte{}
|
||||||
|
|
||||||
for gvk, bytes := range gvkmap {
|
for gvk, bytes := range gvkmap {
|
||||||
if kubeadmutil.GroupVersionKindsHasInitConfiguration(gvk) || kubeadmutil.GroupVersionKindsHasClusterConfiguration(gvk) || componentconfigs.Scheme.IsGroupRegistered(gvk.Group) {
|
|
||||||
klog.Warningf("[config] WARNING: YAML document with GroupVersionKind %v is deprecated for upgrade, please use config file with kind of UpgradeConfiguration instead \n", gvk)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if gvk.Kind != constants.UpgradeConfigurationKind {
|
if gvk.Kind != constants.UpgradeConfigurationKind {
|
||||||
klog.Warningf("[config] WARNING: Ignored YAML document with GroupVersionKind %v\n", gvk)
|
klog.Warningf("[config] WARNING: Ignored configuration document with GroupVersionKind %v\n", gvk)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,7 +49,7 @@ func documentMapToUpgradeConfiguration(gvkmap kubeadmapi.DocumentMap, allowDepre
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify the validity of the YAML
|
// verify the validity of the YAML/JSON
|
||||||
if err := strict.VerifyUnmarshalStrict([]*runtime.Scheme{kubeadmscheme.Scheme}, gvk, bytes); err != nil {
|
if err := strict.VerifyUnmarshalStrict([]*runtime.Scheme{kubeadmscheme.Scheme}, gvk, bytes); err != nil {
|
||||||
klog.Warning(err.Error())
|
klog.Warning(err.Error())
|
||||||
}
|
}
|
||||||
@ -83,11 +77,6 @@ func documentMapToUpgradeConfiguration(gvkmap kubeadmapi.DocumentMap, allowDepre
|
|||||||
return internalcfg, nil
|
return internalcfg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DocMapToUpgradeConfiguration converts documentMap to an internal, defaulted and validated UpgradeConfiguration object.
|
|
||||||
func DocMapToUpgradeConfiguration(gvkmap kubeadmapi.DocumentMap) (*kubeadmapi.UpgradeConfiguration, error) {
|
|
||||||
return documentMapToUpgradeConfiguration(gvkmap, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoadUpgradeConfigurationFromFile loads UpgradeConfiguration from a file.
|
// LoadUpgradeConfigurationFromFile loads UpgradeConfiguration from a file.
|
||||||
func LoadUpgradeConfigurationFromFile(cfgPath string, _ LoadOrDefaultConfigurationOptions) (*kubeadmapi.UpgradeConfiguration, error) {
|
func LoadUpgradeConfigurationFromFile(cfgPath string, _ LoadOrDefaultConfigurationOptions) (*kubeadmapi.UpgradeConfiguration, error) {
|
||||||
var err error
|
var err error
|
||||||
@ -99,22 +88,29 @@ func LoadUpgradeConfigurationFromFile(cfgPath string, _ LoadOrDefaultConfigurati
|
|||||||
return nil, errors.Wrapf(err, "unable to load config from file %q", cfgPath)
|
return nil, errors.Wrapf(err, "unable to load config from file %q", cfgPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Split the YAML documents in the file into a DocumentMap
|
|
||||||
docmap, err := kubeadmutil.SplitYAMLDocuments(configBytes)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert documentMap to internal UpgradeConfiguration, InitConfiguration and ClusterConfiguration from config file will be ignored.
|
// Convert documentMap to internal UpgradeConfiguration, InitConfiguration and ClusterConfiguration from config file will be ignored.
|
||||||
// Upgrade should respect the cluster configuration from the existing cluster, re-configure the cluster with a InitConfiguration and
|
// Upgrade should respect the cluster configuration from the existing cluster, re-configure the cluster with a InitConfiguration and
|
||||||
// ClusterConfiguration from the config file is not allowed for upgrade.
|
// ClusterConfiguration from the config file is not allowed for upgrade.
|
||||||
if upgradeCfg, err = DocMapToUpgradeConfiguration(docmap); err != nil {
|
if upgradeCfg, err = BytesToUpgradeConfiguration(configBytes); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return upgradeCfg, nil
|
return upgradeCfg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BytesToUpgradeConfiguration converts a byte slice to an internal, defaulted and validated UpgradeConfiguration object.
|
||||||
|
// The map may contain many different YAML/JSON documents. These documents are parsed one-by-one.
|
||||||
|
// The resulting UpgradeConfiguration is then dynamically defaulted and validated prior to return.
|
||||||
|
func BytesToUpgradeConfiguration(b []byte) (*kubeadmapi.UpgradeConfiguration, error) {
|
||||||
|
// Split the YAML/JSON documents in the file into a DocumentMap
|
||||||
|
gvkmap, err := kubeadmutil.SplitConfigDocuments(b)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return documentMapToUpgradeConfiguration(gvkmap, false)
|
||||||
|
}
|
||||||
|
|
||||||
// LoadOrDefaultUpgradeConfiguration takes a path to a config file and a versioned configuration that can serve as the default config
|
// LoadOrDefaultUpgradeConfiguration 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.
|
// 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.
|
// Then the external, versioned configuration is defaulted and converted to the internal type.
|
||||||
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
@ -26,17 +27,15 @@ import (
|
|||||||
"github.com/lithammer/dedent"
|
"github.com/lithammer/dedent"
|
||||||
|
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"k8s.io/utils/ptr"
|
"k8s.io/utils/ptr"
|
||||||
"sigs.k8s.io/yaml"
|
|
||||||
|
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||||
kubeadmapiv1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta4"
|
kubeadmapiv1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta4"
|
||||||
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||||
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestDocMapToUpgradeConfiguration(t *testing.T) {
|
func TestBytesToUpgradeConfiguration(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
cfg interface{}
|
cfg interface{}
|
||||||
@ -117,25 +116,25 @@ func TestDocMapToUpgradeConfiguration(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range tests {
|
for _, tc := range tests {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
for _, format := range formats {
|
||||||
b, err := yaml.Marshal(tc.cfg)
|
t.Run(fmt.Sprintf("%s_%s", tc.name, format.name), func(t *testing.T) {
|
||||||
if err != nil {
|
b, err := format.marshal(tc.cfg)
|
||||||
t.Fatalf("unexpected error while marshalling to YAML: %v", err)
|
if err != nil {
|
||||||
}
|
t.Fatalf("unexpected error while marshalling to %s: %v", format.name, err)
|
||||||
docmap, err := kubeadmutil.SplitYAMLDocuments(b)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Unexpected error of SplitYAMLDocuments: %v", err)
|
|
||||||
}
|
|
||||||
cfg, err := DocMapToUpgradeConfiguration(docmap)
|
|
||||||
if (err != nil) != tc.expectedError {
|
|
||||||
t.Fatalf("failed DocMapToUpgradeConfiguration:\n\texpected error: %t\n\t actual error: %v", tc.expectedError, err)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
if diff := cmp.Diff(*cfg, tc.expectedCfg, cmpopts.IgnoreFields(kubeadmapi.UpgradeConfiguration{}, "Timeouts")); diff != "" {
|
|
||||||
t.Fatalf("DocMapToUpgradeConfiguration returned unexpected diff (-want,+got):\n%s", diff)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
})
|
cfg, err := BytesToUpgradeConfiguration(b)
|
||||||
|
if (err != nil) != tc.expectedError {
|
||||||
|
t.Fatalf("failed BytesToUpgradeConfiguration:\n\texpected error: %t\n\t actual error: %v", tc.expectedError, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
if diff := cmp.Diff(*cfg, tc.expectedCfg, cmpopts.IgnoreFields(kubeadmapi.UpgradeConfiguration{}, "Timeouts")); diff != "" {
|
||||||
|
t.Fatalf("BytesToUpgradeConfiguration returned unexpected diff (-want,+got):\n%s", diff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,30 +156,11 @@ func TestLoadUpgradeConfigurationFromFile(t *testing.T) {
|
|||||||
name string
|
name string
|
||||||
cfgPath string
|
cfgPath string
|
||||||
fileContents string
|
fileContents string
|
||||||
want *kubeadmapi.UpgradeConfiguration
|
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "Config file does not exists",
|
name: "Config file does not exists",
|
||||||
cfgPath: "tmp",
|
cfgPath: "tmp",
|
||||||
want: nil,
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Config file format is basic text",
|
|
||||||
cfgPath: filePath,
|
|
||||||
want: nil,
|
|
||||||
fileContents: "some-text",
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Unknown kind UpgradeConfiguration for kubeadm.k8s.io/unknown",
|
|
||||||
cfgPath: filePath,
|
|
||||||
fileContents: dedent.Dedent(`
|
|
||||||
apiVersion: kubeadm.k8s.io/unknown
|
|
||||||
kind: UpgradeConfiguration
|
|
||||||
`),
|
|
||||||
want: nil,
|
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -188,24 +168,8 @@ func TestLoadUpgradeConfigurationFromFile(t *testing.T) {
|
|||||||
cfgPath: filePath,
|
cfgPath: filePath,
|
||||||
fileContents: dedent.Dedent(`
|
fileContents: dedent.Dedent(`
|
||||||
apiVersion: kubeadm.k8s.io/v1beta4
|
apiVersion: kubeadm.k8s.io/v1beta4
|
||||||
kind: UpgradeConfiguration`),
|
kind: UpgradeConfiguration
|
||||||
want: &kubeadmapi.UpgradeConfiguration{
|
`),
|
||||||
Apply: kubeadmapi.UpgradeApplyConfiguration{
|
|
||||||
CertificateRenewal: ptr.To(true),
|
|
||||||
EtcdUpgrade: ptr.To(true),
|
|
||||||
ImagePullPolicy: v1.PullIfNotPresent,
|
|
||||||
ImagePullSerial: ptr.To(true),
|
|
||||||
},
|
|
||||||
Node: kubeadmapi.UpgradeNodeConfiguration{
|
|
||||||
CertificateRenewal: ptr.To(true),
|
|
||||||
EtcdUpgrade: ptr.To(true),
|
|
||||||
ImagePullPolicy: v1.PullIfNotPresent,
|
|
||||||
ImagePullSerial: ptr.To(true),
|
|
||||||
},
|
|
||||||
Plan: kubeadmapi.UpgradePlanConfiguration{
|
|
||||||
EtcdUpgrade: ptr.To(true),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -223,17 +187,10 @@ func TestLoadUpgradeConfigurationFromFile(t *testing.T) {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
got, err := LoadUpgradeConfigurationFromFile(tt.cfgPath, options)
|
_, err = LoadUpgradeConfigurationFromFile(tt.cfgPath, options)
|
||||||
if (err != nil) != tt.wantErr {
|
if (err != nil) != tt.wantErr {
|
||||||
t.Errorf("LoadUpgradeConfigurationFromFile() error = %v, wantErr %v", err, tt.wantErr)
|
t.Errorf("LoadUpgradeConfigurationFromFile() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
}
|
}
|
||||||
if tt.want == nil && got != tt.want {
|
|
||||||
t.Errorf("LoadUpgradeConfigurationFromFile() got = %v, want %v", got, tt.want)
|
|
||||||
} else if tt.want != nil {
|
|
||||||
if diff := cmp.Diff(got, tt.want, cmpopts.IgnoreFields(kubeadmapi.UpgradeConfiguration{}, "Timeouts")); diff != "" {
|
|
||||||
t.Errorf("LoadUpgradeConfigurationFromFile returned unexpected diff (-want,+got):\n%s", diff)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -415,21 +372,24 @@ func TestLoadOrDefaultUpgradeConfiguration(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
bytes, err := yaml.Marshal(tt.cfg)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Could not marshal test config: %v", err)
|
|
||||||
}
|
|
||||||
err = os.WriteFile(filePath, bytes, 0644)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Couldn't write content to file: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
got, _ := LoadOrDefaultUpgradeConfiguration(tt.cfgPath, tt.cfg, options)
|
for _, tt := range tests {
|
||||||
if diff := cmp.Diff(got, tt.want, cmpopts.IgnoreFields(kubeadmapi.UpgradeConfiguration{}, "Timeouts")); diff != "" {
|
for _, format := range formats {
|
||||||
t.Errorf("LoadOrDefaultUpgradeConfiguration returned unexpected diff (-want,+got):\n%s", diff)
|
t.Run(fmt.Sprintf("%s_%s", tt.name, format.name), func(t *testing.T) {
|
||||||
}
|
bytes, err := format.marshal(tt.cfg)
|
||||||
})
|
if err != nil {
|
||||||
|
t.Fatalf("Could not marshal test config: %v", err)
|
||||||
|
}
|
||||||
|
err = os.WriteFile(filePath, bytes, 0644)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Couldn't write content to file: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
got, _ := LoadOrDefaultUpgradeConfiguration(tt.cfgPath, tt.cfg, options)
|
||||||
|
if diff := cmp.Diff(got, tt.want, cmpopts.IgnoreFields(kubeadmapi.UpgradeConfiguration{}, "Timeouts")); diff != "" {
|
||||||
|
t.Errorf("LoadOrDefaultUpgradeConfiguration returned unexpected diff (-want,+got):\n%s", diff)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,13 +65,13 @@ func UniversalUnmarshal(buffer []byte) (runtime.Object, error) {
|
|||||||
return obj, nil
|
return obj, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SplitYAMLDocuments reads the YAML bytes per-document, unmarshals the TypeMeta information from each document
|
// SplitConfigDocuments reads the YAML/JSON bytes per-document, unmarshals the TypeMeta information from each document
|
||||||
// and returns a map between the GroupVersionKind of the document and the document bytes
|
// and returns a map between the GroupVersionKind of the document and the document bytes
|
||||||
func SplitYAMLDocuments(yamlBytes []byte) (kubeadmapi.DocumentMap, error) {
|
func SplitConfigDocuments(documentBytes []byte) (kubeadmapi.DocumentMap, error) {
|
||||||
gvkmap := kubeadmapi.DocumentMap{}
|
gvkmap := kubeadmapi.DocumentMap{}
|
||||||
knownKinds := map[string]bool{}
|
knownKinds := map[string]bool{}
|
||||||
errs := []error{}
|
errs := []error{}
|
||||||
buf := bytes.NewBuffer(yamlBytes)
|
buf := bytes.NewBuffer(documentBytes)
|
||||||
reader := utilyaml.NewYAMLReader(bufio.NewReader(buf))
|
reader := utilyaml.NewYAMLReader(bufio.NewReader(buf))
|
||||||
for {
|
for {
|
||||||
// Read one YAML document at a time, until io.EOF is returned
|
// Read one YAML document at a time, until io.EOF is returned
|
||||||
@ -111,7 +111,7 @@ func SplitYAMLDocuments(yamlBytes []byte) (kubeadmapi.DocumentMap, error) {
|
|||||||
|
|
||||||
// GroupVersionKindsFromBytes parses the bytes and returns a gvk slice
|
// GroupVersionKindsFromBytes parses the bytes and returns a gvk slice
|
||||||
func GroupVersionKindsFromBytes(b []byte) ([]schema.GroupVersionKind, error) {
|
func GroupVersionKindsFromBytes(b []byte) ([]schema.GroupVersionKind, error) {
|
||||||
gvkmap, err := SplitYAMLDocuments(b)
|
gvkmap, err := SplitConfigDocuments(b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -199,7 +199,7 @@ func TestSplitYAMLDocuments(t *testing.T) {
|
|||||||
for _, rt := range tests {
|
for _, rt := range tests {
|
||||||
t.Run(rt.name, func(t2 *testing.T) {
|
t.Run(rt.name, func(t2 *testing.T) {
|
||||||
|
|
||||||
gvkmap, err := SplitYAMLDocuments(rt.fileContents)
|
gvkmap, err := SplitConfigDocuments(rt.fileContents)
|
||||||
if (err != nil) != rt.expectedErr {
|
if (err != nil) != rt.expectedErr {
|
||||||
t2.Errorf("expected error: %t, actual: %t", rt.expectedErr, err != nil)
|
t2.Errorf("expected error: %t, actual: %t", rt.expectedErr, err != nil)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user