Skip to content
Open
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
17 changes: 17 additions & 0 deletions api/v1/installation_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,23 @@ type InstallationSpec struct {
// +optional
FlexVolumePath string `json:"flexVolumePath,omitempty"`

// CalicoRunHostPath optionally specifies the host path mounted into calico-node containers at
// /var/run/calico. Environments such as microk8s place Calico runtime state under a non-standard
// host directory (for example /var/snap/microk8s/current/var/run/calico); set this field so the
// operator continues using that path after a manifest-to-operator migration.
// +optional
// +kubebuilder:default:="/var/run/calico"
// +kubebuilder:validation:MaxLength=1024
CalicoRunHostPath string `json:"calicoRunHostPath,omitempty"`

// CalicoLibHostPath optionally specifies the host path mounted into calico-node containers at
// /var/lib/calico. Pair this with CalicoRunHostPath when Calico data lives under a non-standard
// host directory (for example microk8s uses /var/snap/microk8s/current/var/lib/calico).
// +optional
// +kubebuilder:default:="/var/lib/calico"
// +kubebuilder:validation:MaxLength=1024
CalicoLibHostPath string `json:"calicoLibHostPath,omitempty"`

// KubeletVolumePluginPath optionally specifies enablement of Calico CSI plugin. If not specified,
// CSI will be enabled by default. If set to 'None', CSI will be disabled.
// Default: /var/lib/kubelet
Expand Down
36 changes: 34 additions & 2 deletions pkg/controller/migration/convert/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,10 +140,12 @@ func handleCore(c *components, install *operatorv1.Installation) error {
if err := checkNodeHostPathVolume(c.node.Spec.Template.Spec, "lib-modules", "/lib/modules"); err != nil {
return err
}
if err := checkNodeHostPathVolume(c.node.Spec.Template.Spec, "var-run-calico", "/var/run/calico"); err != nil {
// var-run-calico / var-lib-calico may use non-default hostPaths (e.g. microk8s snap paths).
// Capture them onto Installation so the operator continues rendering the same host paths.
if err := handleCalicoHostPathVolume(c.node.Spec.Template.Spec, "var-run-calico", "/var/run/calico", &install.Spec.CalicoRunHostPath); err != nil {
return err
}
if err := checkNodeHostPathVolume(c.node.Spec.Template.Spec, "var-lib-calico", "/var/lib/calico"); err != nil {
if err := handleCalicoHostPathVolume(c.node.Spec.Template.Spec, "var-lib-calico", "/var/lib/calico", &install.Spec.CalicoLibHostPath); err != nil {
return err
}
if err := checkNodeHostPathVolume(c.node.Spec.Template.Spec, "xtables-lock", "/run/xtables.lock"); err != nil {
Expand Down Expand Up @@ -221,6 +223,36 @@ func checkNodeHostPathVolume(spec corev1.PodSpec, name, path string) error {
return nil
}

// handleCalicoHostPathVolume verifies that a hostPath volume with the given name exists.
// When the host path matches defaultPath, dest is left empty so the operator uses its default.
// When the host path differs (for example microk8s snap-prefixed paths that still end with the
// standard suffix), dest is set so the operator continues using the existing path after migration.
func handleCalicoHostPathVolume(spec corev1.PodSpec, name, defaultPath string, dest *string) error {
v := getVolume(spec, name)
if v == nil || v.HostPath == nil || v.HostPath.Path == "" {
return ErrIncompatibleCluster{
err: fmt.Sprintf("missing expected volume '%s' with hostPath '%s'", name, defaultPath),
component: ComponentCalicoNode,
fix: fmt.Sprintf("add the expected volume to %s", ComponentCalicoNode),
}
}
path := v.HostPath.Path
// Accept the standard path, or a path that ends with it (microk8s: /var/snap/.../var/run/calico).
if path != defaultPath && !strings.HasSuffix(path, defaultPath) {
return ErrIncompatibleCluster{
err: fmt.Sprintf("volume '%s' has unsupported hostPath '%s' (expected '%s' or a path ending with it)",
name, path, defaultPath),
component: ComponentCalicoNode,
fix: fmt.Sprintf("set volume '%s' hostPath to '%s' (or a path ending with '%s'), or set Installation.spec accordingly",
name, defaultPath, defaultPath),
}
}
if path != defaultPath {
*dest = path
}
return nil
}

// addResources adds the rescReq resource for the specified component if none was previously set. If installation
// already had a resource for compName then they are compared and if they are different then an error is returned.
// If the Resource is added to installation or the existing one matches then nil is returned.
Expand Down
33 changes: 33 additions & 0 deletions pkg/controller/migration/convert/core_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,39 @@ var _ = Describe("core handler", func() {
})
})

Context("calico host paths", func() {
It("should accept default var-run/var-lib calico hostPaths without setting Installation fields", func() {
Expect(handleCore(&comps, i)).ToNot(HaveOccurred())
Expect(i.Spec.CalicoRunHostPath).To(BeEmpty())
Expect(i.Spec.CalicoLibHostPath).To(BeEmpty())
})

It("should accept microk8s-style hostPaths and record them on Installation", func() {
for idx, vol := range comps.node.Spec.Template.Spec.Volumes {
switch vol.Name {
case "var-run-calico":
comps.node.Spec.Template.Spec.Volumes[idx].HostPath.Path = "/var/snap/microk8s/current/var/run/calico"
case "var-lib-calico":
comps.node.Spec.Template.Spec.Volumes[idx].HostPath.Path = "/var/snap/microk8s/current/var/lib/calico"
}
}
Expect(handleCore(&comps, i)).ToNot(HaveOccurred())
Expect(i.Spec.CalicoRunHostPath).To(Equal("/var/snap/microk8s/current/var/run/calico"))
Expect(i.Spec.CalicoLibHostPath).To(Equal("/var/snap/microk8s/current/var/lib/calico"))
})

It("should reject hostPaths that do not end with the expected suffix", func() {
for idx, vol := range comps.node.Spec.Template.Spec.Volumes {
if vol.Name == "var-run-calico" {
comps.node.Spec.Template.Spec.Volumes[idx].HostPath.Path = "/opt/other/calico-run"
}
}
err := handleCore(&comps, i)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("var-run-calico"))
})
})

Context("cni", func() {
It("should not raise an error if CNI_CONF_NAME is 10-calico.conflist", func() {
comps.node.Spec.Template.Spec.InitContainers[0].Env = []v1.EnvVar{{
Expand Down
34 changes: 34 additions & 0 deletions pkg/imports/crds/operator/operator.tigera.io_installations.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3039,6 +3039,23 @@ spec:
type: object
type: object
type: object
calicoLibHostPath:
default: /var/lib/calico
description: |-
CalicoLibHostPath optionally specifies the host path mounted into calico-node containers at
/var/lib/calico. Pair this with CalicoRunHostPath when Calico data lives under a non-standard
host directory (for example microk8s uses /var/snap/microk8s/current/var/lib/calico).
maxLength: 1024
type: string
calicoRunHostPath:
default: /var/run/calico
description: |-
CalicoRunHostPath optionally specifies the host path mounted into calico-node containers at
/var/run/calico. Environments such as microk8s place Calico runtime state under a non-standard
host directory (for example /var/snap/microk8s/current/var/run/calico); set this field so the
operator continues using that path after a manifest-to-operator migration.
maxLength: 1024
type: string
calicoNodeWindowsDaemonSet:
description:
CalicoNodeWindowsDaemonSet configures the calico-node-windows
Expand Down Expand Up @@ -12425,6 +12442,23 @@ spec:
type: object
type: object
type: object
calicoLibHostPath:
default: /var/lib/calico
description: |-
CalicoLibHostPath optionally specifies the host path mounted into calico-node containers at
/var/lib/calico. Pair this with CalicoRunHostPath when Calico data lives under a non-standard
host directory (for example microk8s uses /var/snap/microk8s/current/var/lib/calico).
maxLength: 1024
type: string
calicoRunHostPath:
default: /var/run/calico
description: |-
CalicoRunHostPath optionally specifies the host path mounted into calico-node containers at
/var/run/calico. Environments such as microk8s place Calico runtime state under a non-standard
host directory (for example /var/snap/microk8s/current/var/run/calico); set this field so the
operator continues using that path after a manifest-to-operator migration.
maxLength: 1024
type: string
calicoNodeWindowsDaemonSet:
description:
CalicoNodeWindowsDaemonSet configures the calico-node-windows
Expand Down
30 changes: 27 additions & 3 deletions pkg/render/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -1112,7 +1112,7 @@ func (c *nodeComponent) nodeVolumes() []corev1.Volume {
c.cfg.TLS.TrustedBundle.Volume(),
c.cfg.TLS.NodeSecret.Volume(),
c.varRunCalicoVolume(),
corev1.Volume{Name: "var-lib-calico", VolumeSource: corev1.VolumeSource{HostPath: &corev1.HostPathVolumeSource{Path: "/var/lib/calico", Type: &dirOrCreate}}},
c.varLibCalicoVolume(),
// Volume for the containing directory so that the init container can mount the child bpf directory if needed.
corev1.Volume{Name: "sys-fs", VolumeSource: corev1.VolumeSource{HostPath: &corev1.HostPathVolumeSource{Path: "/sys/fs", Type: &dirOrCreate}}},
// Volume for the bpffs itself, used by the main node container.
Expand All @@ -1128,7 +1128,7 @@ func (c *nodeComponent) nodeVolumes() []corev1.Volume {
if c.vppDataplaneEnabled() {
volumes = append(volumes,
// Volume that contains the felix dataplane binary
corev1.Volume{Name: "felix-plugins", VolumeSource: corev1.VolumeSource{HostPath: &corev1.HostPathVolumeSource{Path: "/var/lib/calico/felix-plugins"}}},
corev1.Volume{Name: "felix-plugins", VolumeSource: corev1.VolumeSource{HostPath: &corev1.HostPathVolumeSource{Path: filepath.Join(c.calicoLibHostPath(), "felix-plugins")}}},
)
}

Expand Down Expand Up @@ -1197,12 +1197,36 @@ func (c *nodeComponent) nodeVolumes() []corev1.Volume {
return volumes
}

func (c *nodeComponent) calicoRunHostPath() string {
if c.cfg.Installation != nil && c.cfg.Installation.CalicoRunHostPath != "" {
return c.cfg.Installation.CalicoRunHostPath
}
return "/var/run/calico"
}

func (c *nodeComponent) calicoLibHostPath() string {
if c.cfg.Installation != nil && c.cfg.Installation.CalicoLibHostPath != "" {
return c.cfg.Installation.CalicoLibHostPath
}
return "/var/lib/calico"
}

func (c *nodeComponent) varRunCalicoVolume() corev1.Volume {
dirOrCreate := corev1.HostPathDirectoryOrCreate
return corev1.Volume{
Name: "var-run-calico",
VolumeSource: corev1.VolumeSource{
HostPath: &corev1.HostPathVolumeSource{Path: "/var/run/calico", Type: &dirOrCreate},
HostPath: &corev1.HostPathVolumeSource{Path: c.calicoRunHostPath(), Type: &dirOrCreate},
},
}
}

func (c *nodeComponent) varLibCalicoVolume() corev1.Volume {
dirOrCreate := corev1.HostPathDirectoryOrCreate
return corev1.Volume{
Name: "var-lib-calico",
VolumeSource: corev1.VolumeSource{
HostPath: &corev1.HostPathVolumeSource{Path: c.calicoLibHostPath(), Type: &dirOrCreate},
},
}
}
Expand Down
Loading