diff --git a/cmd/kubeadm/app/apis/kubeadm/fuzzer/fuzzer.go b/cmd/kubeadm/app/apis/kubeadm/fuzzer/fuzzer.go index 8ee7b4a7be5..c804505fe00 100644 --- a/cmd/kubeadm/app/apis/kubeadm/fuzzer/fuzzer.go +++ b/cmd/kubeadm/app/apis/kubeadm/fuzzer/fuzzer.go @@ -22,6 +22,7 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer" + "k8s.io/utils/ptr" bootstraptokenv1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/bootstraptoken/v1" "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" @@ -62,6 +63,7 @@ func fuzzInitConfiguration(obj *kubeadm.InitConfiguration, c fuzz.Continue) { } obj.SkipPhases = nil obj.NodeRegistration.ImagePullPolicy = corev1.PullIfNotPresent + obj.NodeRegistration.ImagePullSerial = ptr.To(true) obj.Patches = nil obj.DryRun = false kubeadm.SetDefaultTimeouts(&obj.Timeouts) @@ -72,6 +74,7 @@ func fuzzNodeRegistration(obj *kubeadm.NodeRegistrationOptions, c fuzz.Continue) // Pinning values for fields that get defaults if fuzz value is empty string or nil (thus making the round trip test fail) obj.IgnorePreflightErrors = nil + obj.ImagePullSerial = ptr.To(true) } func fuzzClusterConfiguration(obj *kubeadm.ClusterConfiguration, c fuzz.Continue) { @@ -132,6 +135,7 @@ func fuzzJoinConfiguration(obj *kubeadm.JoinConfiguration, c fuzz.Continue) { } obj.SkipPhases = nil obj.NodeRegistration.ImagePullPolicy = corev1.PullIfNotPresent + obj.NodeRegistration.ImagePullSerial = ptr.To(true) obj.Patches = nil obj.DryRun = false kubeadm.SetDefaultTimeouts(&obj.Timeouts) diff --git a/cmd/kubeadm/app/apis/kubeadm/types.go b/cmd/kubeadm/app/apis/kubeadm/types.go index 0e8ce6d64b2..2e2b334c2f9 100644 --- a/cmd/kubeadm/app/apis/kubeadm/types.go +++ b/cmd/kubeadm/app/apis/kubeadm/types.go @@ -249,6 +249,9 @@ type NodeRegistrationOptions struct { // The value of this field must be one of "Always", "IfNotPresent" or "Never". // If this field is unset kubeadm will default it to "IfNotPresent", or pull the required images if not present on the host. ImagePullPolicy v1.PullPolicy `json:"imagePullPolicy,omitempty"` + + // ImagePullSerial specifies if image pulling performed by kubeadm must be done serially or in parallel. + ImagePullSerial *bool } // Networking contains elements describing cluster's networking configuration. diff --git a/cmd/kubeadm/app/apis/kubeadm/v1beta3/conversion.go b/cmd/kubeadm/app/apis/kubeadm/v1beta3/conversion.go index 384788cc3c0..6d1cd7d8f19 100644 --- a/cmd/kubeadm/app/apis/kubeadm/v1beta3/conversion.go +++ b/cmd/kubeadm/app/apis/kubeadm/v1beta3/conversion.go @@ -20,6 +20,7 @@ import ( "sort" "k8s.io/apimachinery/pkg/conversion" + "k8s.io/utils/ptr" "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" ) @@ -98,6 +99,7 @@ func Convert_kubeadm_LocalEtcd_To_v1beta3_LocalEtcd(in *kubeadm.LocalEtcd, out * // Convert_v1beta3_NodeRegistrationOptions_To_kubeadm_NodeRegistrationOptions converts a public NodeRegistrationOptions to private NodeRegistrationOptions. func Convert_v1beta3_NodeRegistrationOptions_To_kubeadm_NodeRegistrationOptions(in *NodeRegistrationOptions, out *kubeadm.NodeRegistrationOptions, s conversion.Scope) error { out.KubeletExtraArgs = convertToArgs(in.KubeletExtraArgs) + out.ImagePullSerial = ptr.To(true) return autoConvert_v1beta3_NodeRegistrationOptions_To_kubeadm_NodeRegistrationOptions(in, out, s) } diff --git a/cmd/kubeadm/app/apis/kubeadm/v1beta3/zz_generated.conversion.go b/cmd/kubeadm/app/apis/kubeadm/v1beta3/zz_generated.conversion.go index 53937bbf05d..39c0bc82264 100644 --- a/cmd/kubeadm/app/apis/kubeadm/v1beta3/zz_generated.conversion.go +++ b/cmd/kubeadm/app/apis/kubeadm/v1beta3/zz_generated.conversion.go @@ -708,6 +708,7 @@ func autoConvert_kubeadm_NodeRegistrationOptions_To_v1beta3_NodeRegistrationOpti // WARNING: in.KubeletExtraArgs requires manual conversion: inconvertible types ([]k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm.Arg vs map[string]string) out.IgnorePreflightErrors = *(*[]string)(unsafe.Pointer(&in.IgnorePreflightErrors)) out.ImagePullPolicy = corev1.PullPolicy(in.ImagePullPolicy) + // WARNING: in.ImagePullSerial requires manual conversion: does not exist in peer-type return nil } diff --git a/cmd/kubeadm/app/apis/kubeadm/v1beta4/defaults.go b/cmd/kubeadm/app/apis/kubeadm/v1beta4/defaults.go index 9348f3acc67..529aa8cf948 100644 --- a/cmd/kubeadm/app/apis/kubeadm/v1beta4/defaults.go +++ b/cmd/kubeadm/app/apis/kubeadm/v1beta4/defaults.go @@ -22,6 +22,7 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/utils/ptr" bootstraptokenv1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/bootstraptoken/v1" "k8s.io/kubernetes/cmd/kubeadm/app/constants" @@ -194,6 +195,9 @@ func SetDefaults_NodeRegistration(obj *NodeRegistrationOptions) { if len(obj.ImagePullPolicy) == 0 { obj.ImagePullPolicy = DefaultImagePullPolicy } + if obj.ImagePullSerial == nil { + obj.ImagePullSerial = ptr.To(true) + } } // SetDefaults_ResetConfiguration assigns default values for the ResetConfiguration object diff --git a/cmd/kubeadm/app/apis/kubeadm/v1beta4/doc.go b/cmd/kubeadm/app/apis/kubeadm/v1beta4/doc.go index 4f5d0135aef..257dee63c36 100644 --- a/cmd/kubeadm/app/apis/kubeadm/v1beta4/doc.go +++ b/cmd/kubeadm/app/apis/kubeadm/v1beta4/doc.go @@ -40,6 +40,8 @@ limitations under the License. // during cluster creation will set the same fields to `false`. // - Add a `Timeouts` structure to `InitConfiguration`, `JoinConfiguration` and `ResetConfiguration“ // that can be used to configure various timeouts. +// - Add the `NodeRegistration.ImagePullSerial` field in 'InitConfiguration` and `JoinConfiguration`, which +// can be used to control if kubeadm pulls images serially or in parallel. // // Migration from old kubeadm config versions // diff --git a/cmd/kubeadm/app/apis/kubeadm/v1beta4/types.go b/cmd/kubeadm/app/apis/kubeadm/v1beta4/types.go index 0eec1df807f..f5a74c993e2 100644 --- a/cmd/kubeadm/app/apis/kubeadm/v1beta4/types.go +++ b/cmd/kubeadm/app/apis/kubeadm/v1beta4/types.go @@ -263,6 +263,11 @@ type NodeRegistrationOptions struct { // If this field is unset kubeadm will default it to "IfNotPresent", or pull the required images if not present on the host. // +optional ImagePullPolicy corev1.PullPolicy `json:"imagePullPolicy,omitempty"` + + // ImagePullSerial specifies if image pulling performed by kubeadm must be done serially or in parallel. + // Default: true + // +optional + ImagePullSerial *bool `json:"imagePullSerial,omitempty"` } // Networking contains elements describing cluster's networking configuration diff --git a/cmd/kubeadm/app/apis/kubeadm/v1beta4/zz_generated.conversion.go b/cmd/kubeadm/app/apis/kubeadm/v1beta4/zz_generated.conversion.go index 1419357bcd7..4cff41d0938 100644 --- a/cmd/kubeadm/app/apis/kubeadm/v1beta4/zz_generated.conversion.go +++ b/cmd/kubeadm/app/apis/kubeadm/v1beta4/zz_generated.conversion.go @@ -810,6 +810,7 @@ func autoConvert_v1beta4_NodeRegistrationOptions_To_kubeadm_NodeRegistrationOpti out.KubeletExtraArgs = *(*[]kubeadm.Arg)(unsafe.Pointer(&in.KubeletExtraArgs)) out.IgnorePreflightErrors = *(*[]string)(unsafe.Pointer(&in.IgnorePreflightErrors)) out.ImagePullPolicy = v1.PullPolicy(in.ImagePullPolicy) + out.ImagePullSerial = (*bool)(unsafe.Pointer(in.ImagePullSerial)) return nil } @@ -825,6 +826,7 @@ func autoConvert_kubeadm_NodeRegistrationOptions_To_v1beta4_NodeRegistrationOpti out.KubeletExtraArgs = *(*[]Arg)(unsafe.Pointer(&in.KubeletExtraArgs)) out.IgnorePreflightErrors = *(*[]string)(unsafe.Pointer(&in.IgnorePreflightErrors)) out.ImagePullPolicy = v1.PullPolicy(in.ImagePullPolicy) + out.ImagePullSerial = (*bool)(unsafe.Pointer(in.ImagePullSerial)) return nil } diff --git a/cmd/kubeadm/app/apis/kubeadm/v1beta4/zz_generated.deepcopy.go b/cmd/kubeadm/app/apis/kubeadm/v1beta4/zz_generated.deepcopy.go index 07ba43306f2..55ec68abca4 100644 --- a/cmd/kubeadm/app/apis/kubeadm/v1beta4/zz_generated.deepcopy.go +++ b/cmd/kubeadm/app/apis/kubeadm/v1beta4/zz_generated.deepcopy.go @@ -518,6 +518,11 @@ func (in *NodeRegistrationOptions) DeepCopyInto(out *NodeRegistrationOptions) { *out = make([]string, len(*in)) copy(*out, *in) } + if in.ImagePullSerial != nil { + in, out := &in.ImagePullSerial, &out.ImagePullSerial + *out = new(bool) + **out = **in + } return } diff --git a/cmd/kubeadm/app/apis/kubeadm/zz_generated.deepcopy.go b/cmd/kubeadm/app/apis/kubeadm/zz_generated.deepcopy.go index b5d96201fcc..c9b8749d8af 100644 --- a/cmd/kubeadm/app/apis/kubeadm/zz_generated.deepcopy.go +++ b/cmd/kubeadm/app/apis/kubeadm/zz_generated.deepcopy.go @@ -558,6 +558,11 @@ func (in *NodeRegistrationOptions) DeepCopyInto(out *NodeRegistrationOptions) { *out = make([]string, len(*in)) copy(*out, *in) } + if in.ImagePullSerial != nil { + in, out := &in.ImagePullSerial, &out.ImagePullSerial + *out = new(bool) + **out = **in + } return } diff --git a/cmd/kubeadm/app/cmd/init_test.go b/cmd/kubeadm/app/cmd/init_test.go index 9fb9e8ce3ee..88a85a29833 100644 --- a/cmd/kubeadm/app/cmd/init_test.go +++ b/cmd/kubeadm/app/cmd/init_test.go @@ -30,6 +30,7 @@ import ( "github.com/stretchr/testify/assert" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/sets" + "k8s.io/utils/ptr" bootstraptokenv1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/bootstraptoken/v1" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" @@ -134,6 +135,7 @@ func TestNewInitData(t *testing.T) { CRISocket: expectedCRISocket, IgnorePreflightErrors: []string{"c", "d"}, ImagePullPolicy: "IfNotPresent", + ImagePullSerial: ptr.To(true), }, LocalAPIEndpoint: kubeadmapi.APIEndpoint{ AdvertiseAddress: "1.2.3.4", diff --git a/cmd/kubeadm/app/cmd/join_test.go b/cmd/kubeadm/app/cmd/join_test.go index 4f556f71521..31eb0a6c350 100644 --- a/cmd/kubeadm/app/cmd/join_test.go +++ b/cmd/kubeadm/app/cmd/join_test.go @@ -31,6 +31,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/klog/v2" + "k8s.io/utils/ptr" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" kubeadmapiv1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta3" @@ -221,6 +222,7 @@ func TestNewJoinData(t *testing.T) { CRISocket: expectedCRISocket, IgnorePreflightErrors: []string{"c", "d"}, ImagePullPolicy: "IfNotPresent", + ImagePullSerial: ptr.To(true), Taints: []v1.Taint{{Key: "node-role.kubernetes.io/control-plane", Effect: "NoSchedule"}}, }, CACertPath: kubeadmapiv1.DefaultCACertPath,