Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🌱 Move Kubeadm API v1beta1 webhooks to separate package #9410

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ issues:
# should be removed as the referenced deprecated item is removed from the project.
- linters:
- staticcheck
text: "SA1019: (bootstrapv1.ClusterStatus|scope.Config.Spec.UseExperimentalRetryJoin|DockerMachine.Spec.Bootstrapped|machineStatus.Bootstrapped) is deprecated"
text: "SA1019: (bootstrapv1.ClusterStatus|KubeadmConfigSpec.UseExperimentalRetryJoin|scope.Config.Spec.UseExperimentalRetryJoin|DockerMachine.Spec.Bootstrapped|machineStatus.Bootstrapped) is deprecated"
# Specific exclude rules for deprecated packages that are still part of the codebase. These
# should be removed as the referenced deprecated packages are removed from the project.
- linters:
Expand Down
5 changes: 5 additions & 0 deletions bootstrap/kubeadm/api/.import-restrictions
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
rules:
odvarkadaniel marked this conversation as resolved.
Show resolved Hide resolved
- selectorRegexp: sigs[.]k8s[.]io/controller-runtime
allowedPrefixes:
- "sigs.k8s.io/controller-runtime/pkg/conversion"
forbiddenPrefixes: []
17 changes: 13 additions & 4 deletions bootstrap/kubeadm/api/v1alpha4/groupversion_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,28 @@ limitations under the License.
package v1alpha4

import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/controller-runtime/pkg/scheme"
)

var (
// GroupVersion is group version used to register these objects.
GroupVersion = schema.GroupVersion{Group: "bootstrap.cluster.x-k8s.io", Version: "v1alpha4"}

// SchemeBuilder is used to add go types to the GroupVersionKind scheme.
SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}
schemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)

// AddToScheme adds the types in this group-version to the given scheme.
AddToScheme = SchemeBuilder.AddToScheme
AddToScheme = schemeBuilder.AddToScheme

localSchemeBuilder = SchemeBuilder.SchemeBuilder
objectTypes = []runtime.Object{}

localSchemeBuilder = schemeBuilder
)

func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(GroupVersion, objectTypes...)
metav1.AddToGroupVersion(scheme, GroupVersion)
return nil
}
2 changes: 1 addition & 1 deletion bootstrap/kubeadm/api/v1alpha4/kubeadmconfig_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ type KubeadmConfigList struct {
}

func init() {
SchemeBuilder.Register(&KubeadmConfig{}, &KubeadmConfigList{})
objectTypes = append(objectTypes, &KubeadmConfig{}, &KubeadmConfigList{})
}

// Encoding specifies the cloud-init file encoding.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,5 +58,5 @@ type KubeadmConfigTemplateList struct {
}

func init() {
SchemeBuilder.Register(&KubeadmConfigTemplate{}, &KubeadmConfigTemplateList{})
objectTypes = append(objectTypes, &KubeadmConfigTemplate{}, &KubeadmConfigTemplateList{})
}
5 changes: 5 additions & 0 deletions bootstrap/kubeadm/api/v1beta1/.import-restrictions
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
rules:
- selectorRegexp: sigs[.]k8s[.]io/controller-runtime
allowedPrefixes: []
forbiddenPrefixes:
- "sigs.k8s.io/controller-runtime"
250 changes: 250 additions & 0 deletions bootstrap/kubeadm/api/v1beta1/kubeadmconfig_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,13 @@ limitations under the License.
package v1beta1

import (
"fmt"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/validation/field"

clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
"sigs.k8s.io/cluster-api/feature"
)

// Format specifies the output format of the bootstrap data
Expand All @@ -34,6 +38,16 @@ const (
Ignition Format = "ignition"
)

var (
cannotUseWithIgnition = fmt.Sprintf("not supported when spec.format is set to: %q", Ignition)
conflictingFileSourceMsg = "only one of content or contentFrom may be specified for a single file"
conflictingUserSourceMsg = "only one of passwd or passwdFrom may be specified for a single user"
kubeadmBootstrapFormatIgnitionFeatureDisabledMsg = "can be set only if the KubeadmBootstrapFormatIgnition feature gate is enabled"
missingSecretNameMsg = "secret file source must specify non-empty secret name"
missingSecretKeyMsg = "secret file source must specify non-empty secret key"
pathConflictMsg = "path property must be unique among all files"
)

// KubeadmConfigSpec defines the desired state of KubeadmConfig.
// Either ClusterConfiguration and InitConfiguration should be defined or the JoinConfiguration should be defined.
type KubeadmConfigSpec struct {
Expand Down Expand Up @@ -107,6 +121,242 @@ type KubeadmConfigSpec struct {
Ignition *IgnitionSpec `json:"ignition,omitempty"`
}

// Default defaults a KubeadmConfigSpec.
func (c *KubeadmConfigSpec) Default() {
if c.Format == "" {
c.Format = CloudConfig
}
if c.InitConfiguration != nil && c.InitConfiguration.NodeRegistration.ImagePullPolicy == "" {
c.InitConfiguration.NodeRegistration.ImagePullPolicy = "IfNotPresent"
}
if c.JoinConfiguration != nil && c.JoinConfiguration.NodeRegistration.ImagePullPolicy == "" {
c.JoinConfiguration.NodeRegistration.ImagePullPolicy = "IfNotPresent"
}
}

// Validate ensures the KubeadmConfigSpec is valid.
func (c *KubeadmConfigSpec) Validate(pathPrefix *field.Path) field.ErrorList {
var allErrs field.ErrorList

allErrs = append(allErrs, c.validateFiles(pathPrefix)...)
allErrs = append(allErrs, c.validateUsers(pathPrefix)...)
allErrs = append(allErrs, c.validateIgnition(pathPrefix)...)

return allErrs
}

func (c *KubeadmConfigSpec) validateFiles(pathPrefix *field.Path) field.ErrorList {
var allErrs field.ErrorList

knownPaths := map[string]struct{}{}

for i := range c.Files {
file := c.Files[i]
if file.Content != "" && file.ContentFrom != nil {
allErrs = append(
allErrs,
field.Invalid(
pathPrefix.Child("files").Index(i),
file,
conflictingFileSourceMsg,
),
)
}
// n.b.: if we ever add types besides Secret as a ContentFrom
// Source, we must add webhook validation here for one of the
// sources being non-nil.
if file.ContentFrom != nil {
if file.ContentFrom.Secret.Name == "" {
allErrs = append(
allErrs,
field.Required(
pathPrefix.Child("files").Index(i).Child("contentFrom", "secret", "name"),
missingSecretNameMsg,
),
)
}
if file.ContentFrom.Secret.Key == "" {
allErrs = append(
allErrs,
field.Required(
pathPrefix.Child("files").Index(i).Child("contentFrom", "secret", "key"),
missingSecretKeyMsg,
),
)
}
}
_, conflict := knownPaths[file.Path]
if conflict {
allErrs = append(
allErrs,
field.Invalid(
pathPrefix.Child("files").Index(i).Child("path"),
file,
pathConflictMsg,
),
)
}
knownPaths[file.Path] = struct{}{}
}

return allErrs
}

func (c *KubeadmConfigSpec) validateUsers(pathPrefix *field.Path) field.ErrorList {
var allErrs field.ErrorList

for i := range c.Users {
user := c.Users[i]
if user.Passwd != nil && user.PasswdFrom != nil {
allErrs = append(
allErrs,
field.Invalid(
pathPrefix.Child("users").Index(i),
user,
conflictingUserSourceMsg,
),
)
}
// n.b.: if we ever add types besides Secret as a PasswdFrom
// Source, we must add webhook validation here for one of the
// sources being non-nil.
if user.PasswdFrom != nil {
if user.PasswdFrom.Secret.Name == "" {
allErrs = append(
allErrs,
field.Required(
pathPrefix.Child("users").Index(i).Child("passwdFrom", "secret", "name"),
missingSecretNameMsg,
),
)
}
if user.PasswdFrom.Secret.Key == "" {
allErrs = append(
allErrs,
field.Required(
pathPrefix.Child("users").Index(i).Child("passwdFrom", "secret", "key"),
missingSecretKeyMsg,
),
)
}
}
}

return allErrs
}

func (c *KubeadmConfigSpec) validateIgnition(pathPrefix *field.Path) field.ErrorList {
var allErrs field.ErrorList

if !feature.Gates.Enabled(feature.KubeadmBootstrapFormatIgnition) {
if c.Format == Ignition {
allErrs = append(allErrs, field.Forbidden(
pathPrefix.Child("format"), kubeadmBootstrapFormatIgnitionFeatureDisabledMsg))
}

if c.Ignition != nil {
allErrs = append(allErrs, field.Forbidden(
pathPrefix.Child("ignition"), kubeadmBootstrapFormatIgnitionFeatureDisabledMsg))
}

return allErrs
}

if c.Format != Ignition {
if c.Ignition != nil {
allErrs = append(
allErrs,
field.Invalid(
pathPrefix.Child("format"),
c.Format,
fmt.Sprintf("must be set to %q if spec.ignition is set", Ignition),
),
)
}

return allErrs
}

for i, user := range c.Users {
if user.Inactive != nil && *user.Inactive {
allErrs = append(
allErrs,
field.Forbidden(
pathPrefix.Child("users").Index(i).Child("inactive"),
cannotUseWithIgnition,
),
)
}
}

odvarkadaniel marked this conversation as resolved.
Show resolved Hide resolved
if c.UseExperimentalRetryJoin {
allErrs = append(
allErrs,
field.Forbidden(
pathPrefix.Child("useExperimentalRetryJoin"),
cannotUseWithIgnition,
),
)
}

for i, file := range c.Files {
if file.Encoding == Gzip || file.Encoding == GzipBase64 {
allErrs = append(
allErrs,
field.Forbidden(
pathPrefix.Child("files").Index(i).Child("encoding"),
cannotUseWithIgnition,
),
)
}
}

if c.DiskSetup == nil {
return allErrs
}

for i, partition := range c.DiskSetup.Partitions {
if partition.TableType != nil && *partition.TableType != "gpt" {
allErrs = append(
allErrs,
field.Invalid(
pathPrefix.Child("diskSetup", "partitions").Index(i).Child("tableType"),
*partition.TableType,
fmt.Sprintf(
"only partition type %q is supported when spec.format is set to %q",
"gpt",
Ignition,
),
),
)
}
}

for i, fs := range c.DiskSetup.Filesystems {
if fs.ReplaceFS != nil {
allErrs = append(
allErrs,
field.Forbidden(
pathPrefix.Child("diskSetup", "filesystems").Index(i).Child("replaceFS"),
cannotUseWithIgnition,
),
)
}

if fs.Partition != nil {
allErrs = append(
allErrs,
field.Forbidden(
pathPrefix.Child("diskSetup", "filesystems").Index(i).Child("partition"),
cannotUseWithIgnition,
),
)
}
}

return allErrs
}

// IgnitionSpec contains Ignition specific configuration.
type IgnitionSpec struct {
// ContainerLinuxConfig contains CLC specific configuration.
Expand Down
Loading
Loading