nodeup: Use functional options pattern for HostPathMapping

This means that the object is not mutated after construction, making
it easier to do validity checks (such as whether we have mounted the
same path twice).
This commit is contained in:
justinsb 2023-05-11 10:16:30 -04:00
parent 9af580f1ab
commit 6bdbbc4fd4
6 changed files with 41 additions and 36 deletions

View File

@ -667,7 +667,7 @@ func (b *KubeAPIServerBuilder) buildPod(ctx context.Context, kubeAPIServer *kops
}
// Log both to docker and to the logfile
kubemanifest.AddHostPathMapping(pod, container, "logfile", "/var/log/kube-apiserver.log").WithReadWrite()
kubemanifest.AddHostPathMapping(pod, container, "logfile", "/var/log/kube-apiserver.log", kubemanifest.WithReadWrite())
// We use lighter containers that don't include shells
// But they have richer logging support via klog
if b.IsKubernetesGTE("1.23") {
@ -718,7 +718,7 @@ func (b *KubeAPIServerBuilder) buildPod(ctx context.Context, kubeAPIServer *kops
// Renaming is not possible when the file is mounted as the host path, and will return a
// 'Device or resource busy' error
auditLogPathDir := filepath.Dir(auditLogPath)
kubemanifest.AddHostPathMapping(pod, container, "auditlogpathdir", auditLogPathDir).WithReadWrite()
kubemanifest.AddHostPathMapping(pod, container, "auditlogpathdir", auditLogPathDir, kubemanifest.WithReadWrite())
}
if kubeAPIServer.AuditPolicyFile != "" {
// The audit config dir will be used for both the audit policy and the audit webhook config

View File

@ -228,7 +228,7 @@ func (b *KubeControllerManagerBuilder) buildPod(kcm *kops.KubeControllerManagerC
}
// Log both to docker and to the logfile
kubemanifest.AddHostPathMapping(pod, container, "logfile", "/var/log/kube-controller-manager.log").WithReadWrite()
kubemanifest.AddHostPathMapping(pod, container, "logfile", "/var/log/kube-controller-manager.log", kubemanifest.WithReadWrite())
// We use lighter containers that don't include shells
// But they have richer logging support via klog
if b.IsKubernetesGTE("1.23") {
@ -266,7 +266,7 @@ func (b *KubeControllerManagerBuilder) buildPod(kcm *kops.KubeControllerManagerC
kubemanifest.AddHostPathMapping(pod, container, "varlibkcm", "/var/lib/kube-controller-manager")
kubemanifest.AddHostPathMapping(pod, container, "volplugins", volumePluginDir).WithReadWrite()
kubemanifest.AddHostPathMapping(pod, container, "volplugins", volumePluginDir, kubemanifest.WithReadWrite())
pod.Spec.Containers = append(pod.Spec.Containers, *container)

View File

@ -180,7 +180,7 @@ func (b *KubeProxyBuilder) buildPod() (*v1.Pod, error) {
}
// Log both to docker and to the logfile
kubemanifest.AddHostPathMapping(pod, container, "logfile", "/var/log/kube-proxy.log").WithReadWrite()
kubemanifest.AddHostPathMapping(pod, container, "logfile", "/var/log/kube-proxy.log", kubemanifest.WithReadWrite())
// We use lighter containers that don't include shells
// But they have richer logging support via klog
if b.IsKubernetesGTE("1.23") {
@ -205,8 +205,7 @@ func (b *KubeProxyBuilder) buildPod() (*v1.Pod, error) {
kubemanifest.AddHostPathMapping(pod, container, "modules", "/lib/modules")
// Map SSL certs from host: /usr/share/ca-certificates -> /etc/ssl/certs
sslCertsHost := kubemanifest.AddHostPathMapping(pod, container, "ssl-certs-hosts", "/usr/share/ca-certificates")
sslCertsHost.VolumeMount.MountPath = "/etc/ssl/certs"
kubemanifest.AddHostPathMapping(pod, container, "ssl-certs-hosts", "/usr/share/ca-certificates", kubemanifest.WithMountPath("/etc/ssl/certs"))
}
if b.IsGossip {
@ -216,7 +215,7 @@ func (b *KubeProxyBuilder) buildPod() (*v1.Pod, error) {
// Mount the iptables lock file
{
kubemanifest.AddHostPathMapping(pod, container, "iptableslock", "/run/xtables.lock").WithReadWrite()
kubemanifest.AddHostPathMapping(pod, container, "iptableslock", "/run/xtables.lock", kubemanifest.WithReadWrite())
vol := pod.Spec.Volumes[len(pod.Spec.Volumes)-1]
if vol.Name != "iptableslock" {

View File

@ -248,7 +248,7 @@ func (b *KubeSchedulerBuilder) buildPod(kubeScheduler *kops.KubeSchedulerConfig)
kubemanifest.AddHostPathMapping(pod, container, "srvscheduler", pathSrvScheduler)
// Log both to docker and to the logfile
kubemanifest.AddHostPathMapping(pod, container, "logfile", "/var/log/kube-scheduler.log").WithReadWrite()
kubemanifest.AddHostPathMapping(pod, container, "logfile", "/var/log/kube-scheduler.log", kubemanifest.WithReadWrite())
// We use lighter containers that don't include shells
// But they have richer logging support via klog
if b.IsKubernetesGTE("1.23") {

View File

@ -20,21 +20,12 @@ import (
v1 "k8s.io/api/core/v1"
)
// MapEtcHosts maps the /etc/hosts file into the pod (useful for gossip DNS)
func MapEtcHosts(pod *v1.Pod, container *v1.Container, readOnly bool) {
mapping := AddHostPathMapping(pod, container, "hosts", "/etc/hosts").WithType(v1.HostPathFile)
mapping.VolumeMount.ReadOnly = readOnly
}
// HostPathMapping allows fluent construction of a hostpath mount
type HostPathMapping struct {
VolumeMount *v1.VolumeMount
Volume *v1.Volume
}
// HostPathMappingOption implements the "functional options pattern" for named variable parameters.
type HostPathMappingOption func(volumeMount *v1.VolumeMount, volume *v1.Volume)
// AddHostPathMapping is a helper function for mapping a host path into a container
// It returns a HostPathMapping for tweaking the defaults (which are notably read-only)
func AddHostPathMapping(pod *v1.Pod, container *v1.Container, name, path string) *HostPathMapping {
func AddHostPathMapping(pod *v1.Pod, container *v1.Container, name, path string, options ...HostPathMappingOption) {
pod.Spec.Volumes = append(pod.Spec.Volumes, v1.Volume{
Name: name,
VolumeSource: v1.VolumeSource{
@ -50,26 +41,38 @@ func AddHostPathMapping(pod *v1.Pod, container *v1.Container, name, path string)
ReadOnly: true,
})
return &HostPathMapping{
Volume: &pod.Spec.Volumes[len(pod.Spec.Volumes)-1],
VolumeMount: &container.VolumeMounts[len(container.VolumeMounts)-1],
volume := &pod.Spec.Volumes[len(pod.Spec.Volumes)-1]
volumeMount := &container.VolumeMounts[len(container.VolumeMounts)-1]
for _, option := range options {
option(volumeMount, volume)
}
}
// WithReadWrite changes the hostpath mapping to be read-write (the default is read-only)
func (m *HostPathMapping) WithReadWrite() *HostPathMapping {
m.VolumeMount.ReadOnly = false
return m
func WithReadWrite() HostPathMappingOption {
return func(volumeMount *v1.VolumeMount, volume *v1.Volume) {
volumeMount.ReadOnly = false
}
}
// WithType changes the hostpath mount type
func (m *HostPathMapping) WithType(t v1.HostPathType) *HostPathMapping {
m.Volume.VolumeSource.HostPath.Type = &t
return m
func WithType(t v1.HostPathType) HostPathMappingOption {
return func(volumeMount *v1.VolumeMount, volume *v1.Volume) {
volume.VolumeSource.HostPath.Type = &t
}
}
// WithHostPath changes the hostpath path
func (m *HostPathMapping) WithHostPath(p string) *HostPathMapping {
m.Volume.VolumeSource.HostPath.Path = p
return m
// WithHostPath changes the host path (the path in the host)
func WithHostPath(p string) HostPathMappingOption {
return func(volumeMount *v1.VolumeMount, volume *v1.Volume) {
volume.VolumeSource.HostPath.Path = p
}
}
// WithMountPath changes the mount path (the path in the container)
func WithMountPath(p string) HostPathMappingOption {
return func(volumeMount *v1.VolumeMount, volume *v1.Volume) {
volumeMount.MountPath = p
}
}

View File

@ -518,10 +518,13 @@ func (b *EtcdManagerBuilder) buildPod(etcdCluster kops.EtcdClusterSpec, instance
},
}
kubemanifest.AddHostPathMapping(pod, container, "varlogetcd", "/var/log/etcd.log").WithReadWrite().WithType(v1.HostPathFileOrCreate).WithHostPath(logFile)
kubemanifest.AddHostPathMapping(pod, container, "varlogetcd", "/var/log/etcd.log",
kubemanifest.WithReadWrite(),
kubemanifest.WithType(v1.HostPathFileOrCreate),
kubemanifest.WithHostPath(logFile))
if fi.ValueOf(b.Cluster.Spec.UseHostCertificates) {
kubemanifest.AddHostPathMapping(pod, container, "etc-ssl-certs", "/etc/ssl/certs").WithType(v1.HostPathDirectoryOrCreate)
kubemanifest.AddHostPathMapping(pod, container, "etc-ssl-certs", "/etc/ssl/certs", kubemanifest.WithType(v1.HostPathDirectoryOrCreate))
}
}