Skip to content

Commit

Permalink
Move kubeadm webhooks to representative internal packages
Browse files Browse the repository at this point in the history
  • Loading branch information
odvarkadaniel committed Sep 19, 2023
1 parent a29df47 commit b753064
Show file tree
Hide file tree
Showing 29 changed files with 590 additions and 840 deletions.
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/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,
),
)
}
}

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
18 changes: 18 additions & 0 deletions bootstrap/kubeadm/internal/webhooks/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
Copyright 2020 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 webhooks provides the validating webhooks for KubeadmConfig and KubeadmConfigTemplate.
package webhooks
Loading

0 comments on commit b753064

Please sign in to comment.