Skip to content
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
8 changes: 6 additions & 2 deletions api/bootstrap/v1beta1/k0s_template_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,12 @@ package v1beta1

import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
var (
conflictingFileSourceMsg = "only one of content or contentFrom may be specified for a single file"
conflictingContentFromMsg = "only one of contentFrom.secretKeyRef or contentFrom.configMapKeyRef may be specified for a single file"
pathConflictMsg = "path property must be unique among all files"
noContentMsg = "either content or contentFrom must be specified for a file"
)

func init() {
SchemeBuilder.Register(&K0sWorkerConfigTemplate{}, &K0sWorkerConfigTemplateList{})
Expand Down
111 changes: 110 additions & 1 deletion api/bootstrap/v1beta1/k0s_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,13 @@ limitations under the License.
package v1beta1

import (
"path/filepath"
"strings"

"github.com/k0sproject/k0smotron/internal/provisioner"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"path/filepath"
"k8s.io/apimachinery/pkg/util/validation/field"

clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
)
Expand Down Expand Up @@ -379,3 +382,109 @@ func (k *K0sWorkerConfig) GetJoinTokenPath() string {
}
return filepath.Join(k.Spec.WorkingDir, "k0s.token")
}

// Validate validates the K0sWorkerConfigSpec.
func (cs *K0sWorkerConfigSpec) Validate(pathPrefix *field.Path) field.ErrorList {
var allErrs field.ErrorList

// TODO: validate Ignition
allErrs = append(allErrs, cs.validateVersion(pathPrefix)...)
allErrs = append(allErrs, cs.validateFiles(pathPrefix)...)

return allErrs
}

func (cs *K0sWorkerConfigSpec) validateFiles(pathPrefix *field.Path) field.ErrorList {
var allErrs field.ErrorList

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

for i, file := range cs.Files {
if file.Content != "" && file.ContentFrom != nil {
allErrs = append(
allErrs,
field.Invalid(
pathPrefix.Child("files").Index(i),
file,
conflictingFileSourceMsg,
),
)
}

if file.ContentFrom == nil && file.Content == "" {
allErrs = append(
allErrs,
field.Invalid(
pathPrefix.Child("files").Index(i),
file,
noContentMsg,
),
)
}

if file.ContentFrom != nil {
if file.ContentFrom.SecretRef != nil && file.ContentFrom.ConfigMapRef != nil {
allErrs = append(
allErrs,
field.Invalid(
pathPrefix.Child("files").Index(i).Child("contentFrom"),
file.ContentFrom,
conflictingContentFromMsg,
),
)
}

if file.ContentFrom.SecretRef != nil && file.ContentFrom.SecretRef.Name == "" {
allErrs = append(
allErrs,
field.Required(
pathPrefix.Child("files").Index(i).Child("contentFrom").Child("secretRef").Child("name"),
"name is required",
),
)
}

if file.ContentFrom.ConfigMapRef != nil && file.ContentFrom.ConfigMapRef.Name == "" {
allErrs = append(
allErrs,
field.Required(
pathPrefix.Child("files").Index(i).Child("contentFrom").Child("configMapRef").Child("name"),
"name is required",
),
)
}
}
_, 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 (cs *K0sWorkerConfigSpec) validateVersion(pathPrefix *field.Path) field.ErrorList {
var allErrs field.ErrorList

if strings.Contains(cs.Version, "-k0s.") {
allErrs = append(
allErrs,
field.Invalid(
pathPrefix.Child("version"),
cs.Version,
"k0s specific versions must be specified using the '+k0s' suffix",
),
)
return allErrs
}

return allErrs
}
5 changes: 5 additions & 0 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,11 @@ func main() {
setupLog.Error(err, "unable to create controller", "controller", "Bootstrap")
os.Exit(1)
}

if err = (&bootstrap.K0sWorkerConfigValidator{}).SetupK0sWorkerConfigWebhookWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create validation webhook", "webhook", "K0sWorkerConfigValidator")
os.Exit(1)
}
}

if isControllerEnabled(controlPlaneController) {
Expand Down
185 changes: 88 additions & 97 deletions config/clusterapi/bootstrap/kustomization.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ resources:
- ../../rbac
- ../../manager
- ../k0smotron.io
- ../../certmanager
- ./webhook
- ./bases/bootstrap.cluster.x-k8s.io_k0scontrollerconfigs.yaml
- ./bases/bootstrap.cluster.x-k8s.io_k0sworkerconfigs.yaml
- ./bases/bootstrap.cluster.x-k8s.io_k0sworkerconfigtemplates.yaml
Expand All @@ -35,6 +37,11 @@ patches:
# If you want your controller-manager to expose the /metrics
# endpoint w/o any authn/z, please comment the following line.
- path: manager_config_patch.yaml
- path: patches/webhook_in_k0sworkerconfig.yaml
- path: patches/cainjection_in_k0sworkerconfig.yaml
- path: patches/manager_webhook_patch.yaml
- path: patches/webhook_service_patch.yaml
- path: patches/certificate_patch.yaml

configurations:
#- kustomizeconfig.yaml
Expand All @@ -49,100 +56,84 @@ configurations:
## 'CERTMANAGER' needs to be enabled to use ca injection
##- webhookcainjection_patch.yaml
#
#replacements:
# - source: # Add cert-manager annotation to ValidatingWebhookConfiguration, MutatingWebhookConfiguration and CRDs
# kind: Certificate
# group: cert-manager.io
# version: v1
# name: serving-cert # this name should match the one in certificate.yaml
# fieldPath: .metadata.namespace # namespace of the certificate CR
# targets:
# - select:
# kind: ValidatingWebhookConfiguration
# fieldPaths:
# - .metadata.annotations.[cert-manager.io/inject-ca-from]
# options:
# delimiter: '/'
# index: 0
# create: true
# - select:
# kind: MutatingWebhookConfiguration
# fieldPaths:
# - .metadata.annotations.[cert-manager.io/inject-ca-from]
# options:
# delimiter: '/'
# index: 0
# create: true
# - select:
# kind: CustomResourceDefinition
# fieldPaths:
# - .metadata.annotations.[cert-manager.io/inject-ca-from]
# options:
# delimiter: '/'
# index: 0
# create: true
# - source:
# kind: Certificate
# group: cert-manager.io
# version: v1
# name: serving-cert # this name should match the one in certificate.yaml
# fieldPath: .metadata.name
# targets:
# - select:
# kind: ValidatingWebhookConfiguration
# fieldPaths:
# - .metadata.annotations.[cert-manager.io/inject-ca-from]
# options:
# delimiter: '/'
# index: 1
# create: true
# - select:
# kind: MutatingWebhookConfiguration
# fieldPaths:
# - .metadata.annotations.[cert-manager.io/inject-ca-from]
# options:
# delimiter: '/'
# index: 1
# create: true
# - select:
# kind: CustomResourceDefinition
# fieldPaths:
# - .metadata.annotations.[cert-manager.io/inject-ca-from]
# options:
# delimiter: '/'
# index: 1
# create: true
# - source: # Add cert-manager annotation to the webhook Service
# kind: Service
# version: v1
# name: webhook-service
# fieldPath: .metadata.name # namespace of the service
# targets:
# - select:
# kind: Certificate
# group: cert-manager.io
# version: v1
# fieldPaths:
# - .spec.dnsNames.0
# - .spec.dnsNames.1
# options:
# delimiter: '.'
# index: 0
# create: true
# - source:
# kind: Service
# version: v1
# name: webhook-service
# fieldPath: .metadata.namespace # namespace of the service
# targets:
# - select:
# kind: Certificate
# group: cert-manager.io
# version: v1
# fieldPaths:
# - .spec.dnsNames.0
# - .spec.dnsNames.1
# options:
# delimiter: '.'
# index: 1
# create: true
replacements:
- source: # Add cert-manager annotation to ValidatingWebhookConfiguration, MutatingWebhookConfiguration and CRDs
kind: Certificate
group: cert-manager.io
version: v1
name: serving-cert # this name should match the one in certificate.yaml
fieldPath: .metadata.namespace # namespace of the certificate CR
targets:
- select:
kind: ValidatingWebhookConfiguration
fieldPaths:
- .metadata.annotations.[cert-manager.io/inject-ca-from]
options:
delimiter: '/'
index: 0
create: true
- select:
kind: CustomResourceDefinition
fieldPaths:
- .metadata.annotations.[cert-manager.io/inject-ca-from]
options:
delimiter: '/'
index: 0
create: true
- source:
kind: Certificate
group: cert-manager.io
version: v1
name: serving-cert # this name should match the one in certificate.yaml
fieldPath: .metadata.name
targets:
- select:
kind: ValidatingWebhookConfiguration
fieldPaths:
- .metadata.annotations.[cert-manager.io/inject-ca-from]
options:
delimiter: '/'
index: 1
create: true
- select:
kind: CustomResourceDefinition
fieldPaths:
- .metadata.annotations.[cert-manager.io/inject-ca-from]
options:
delimiter: '/'
index: 1
create: true
- source: # Add cert-manager annotation to the webhook Service
kind: Service
version: v1
name: webhook-service
fieldPath: .metadata.name # namespace of the service
targets:
- select:
kind: Certificate
group: cert-manager.io
version: v1
fieldPaths:
- .spec.dnsNames.0
- .spec.dnsNames.1
options:
delimiter: '.'
index: 0
create: true
- source:
kind: Service
version: v1
name: webhook-service
fieldPath: .metadata.namespace # namespace of the service
targets:
- select:
kind: Certificate
group: cert-manager.io
version: v1
fieldPaths:
- .spec.dnsNames.0
- .spec.dnsNames.1
options:
delimiter: '.'
index: 1
create: true
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# The following patch adds a directive for certmanager to inject CA into the CRD
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
cert-manager.io/inject-ca-from: CERTIFICATE_NAMESPACE/CERTIFICATE_NAME
name: k0sworkerconfigs.bootstrap.cluster.x-k8s.io
14 changes: 14 additions & 0 deletions config/clusterapi/bootstrap/patches/certificate_patch.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
labels:
app.kubernetes.io/name: certificate
app.kubernetes.io/instance: serving-cert
app.kubernetes.io/component: certificate
app.kubernetes.io/created-by: k0smotron
app.kubernetes.io/part-of: k0smotron
app.kubernetes.io/managed-by: kustomize
name: serving-cert # this name should match the one appeared in kustomizeconfig.yaml
namespace: system
spec:
secretName: k0smotron-webhook-server-cert-bootstrap
Loading
Loading