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

Implement Enabled field on ManagedOSVersionChannels #800

Merged
merged 9 commits into from
Jul 30, 2024
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
3 changes: 3 additions & 0 deletions .obs/chartfile/crds/templates/crds.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2570,6 +2570,9 @@ spec:
DeleteNoLongerInSyncVersions automatically deletes
all no-longer-in-sync ManagedOSVersions that were created by this channel.
type: boolean
enabled:
default: true
type: boolean
options:
x-kubernetes-preserve-unknown-fields: true
syncInterval:
Expand Down
43 changes: 31 additions & 12 deletions .obs/chartfile/operator/questions.yaml
Original file line number Diff line number Diff line change
@@ -1,24 +1,43 @@
questions:
- variable: defaultChannels.sleMicro55.included
show_if: "defaultChannels.sleMicro55"
default: true
description: "Default channel that can be used for any generic workload."
type: boolean
label: SLE Micro 5.5
group: "Default Elemental OS Channels"
- variable: defaultChannels.sleMicro55KVM.included
show_if: "defaultChannels.sleMicro55KVM"
default: true
description: "Ready to be used with KVM. Contains QEMU Guest agent by default."
type: boolean
label: SLE Micro 5.5 KVM
group: "Default Elemental OS Channels"
- variable: defaultChannels.sleMicro55RT.included
show_if: "defaultChannels.sleMicro55RT"
default: true
description: "Channel that can be used for any generic workload with a Real-Time kernel."
type: boolean
label: SLE Micro 5.5 RT
group: "Default Elemental OS Channels"
- variable: channel.defaultChannel
default: "true"
description: "Provide an Elemental OS Channel container image"
label: Elemental OS Channel
default: "false"
description: "Provide a Custom OS Channel container image"
label: Custom OS Channel
type: boolean
show_subquestion_if: true
group: "Elemental OS Channel"
group: "Custom OS Channel"
subquestions:
- variable: channel.image
default: "%%IMG_REPO%%/rancher/elemental-channel"
description: "Specify the Elemental OS channel: for air-gapped scenarios you need to provide your own OS channel image (see https://elemental.docs.rancher.com/airgap for detailed instructions)"
description: "Specify the custom OS channel: for air-gapped scenarios please see https://elemental.docs.rancher.com/airgap"
type: string
label: Elemental OS Channel Image
group: "Elemental OS Channel"
label: Custom OS Channel Image
group: "Custom OS Channel"
- variable: channel.tag
default: "%VERSION%"
description: "Specify Elemental OS channel image tag"
description: "Specify Custom OS Channel image tag"
type: string
label: "Elemental OS Channel Tag"
group: "Elemental OS Channel"
label: "Custom OS Channel Tag"
group: "Custom OS Channel"
- variable: debug
default: "false"
description: "Enable debug logging in the Elemental operator"
Expand Down
22 changes: 19 additions & 3 deletions .obs/chartfile/operator/templates/channels.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,24 @@ spec:
type: custom
{{ end }}

{{ range $key, $channel := .Values.defaultChannels }}
{{ if and $channel.included (not (lookup "elemental.cattle.io/v1beta1" "ManagedOSVersionChannel" "fleet-default" "$channel.name")) }}
---
apiVersion: elemental.cattle.io/v1beta1
kind: ManagedOSVersionChannel
metadata:
name: {{ $channel.name }}
namespace: fleet-default
spec:
deleteNoLongerInSyncVersions: {{ $channel.deleteNoLongerInSyncVersions }}
enabled: {{ $channel.enabled }}
options:
image: {{ $channel.image }}
type: custom
{{ end }}
{{ end }}


# Keep pre-existing channels managed by Helm if they do not match with the current default
# this way if an upgrade introduces a new channel any pre-existing channel managed by Helm is not deleted
{{ range $index, $channel := (lookup "elemental.cattle.io/v1beta1" "ManagedOSVersionChannel" "fleet-default" "").items }}
Expand All @@ -23,8 +41,6 @@ metadata:
name: {{ $channel.metadata.name }}
namespace: fleet-default
spec:
options:
image: {{ $channel.spec.options.image }}
type: custom
{{- toYaml $channel.spec | nindent 2}}
{{ end }}
{{ end }}
30 changes: 26 additions & 4 deletions .obs/chartfile/operator/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,32 @@ seedImage:
tag: "%VERSION%"
imagePullPolicy: IfNotPresent

channel:
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is still supported if users want to bring in (or are already bringing in) a channel from a value. We just removed the default.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about keeping them with empty values or even just have them commented out? 🤔
Could be handy to have them mentioned somehow in the values.yaml to easily keep track of what we reference in the templates yaml.

name: "sle-micro-5.5"
image: "%%IMG_REPO%%/rancher/elemental-channel/sle-micro"
tag: "5.5"
# a custom channel to install
#channel:
# name: "my-os-channel"
# image: "my-repo/my-os-channel"
# tag: "1.2.3"

# default Elemental channels
defaultChannels:
sleMicro55:
included: true
name: sle-micro-5.5
enabled: false
image: registry.opensuse.org/isv/rancher/elemental/dev/containers/rancher/elemental-channel/sle-micro:5.5
deleteNoLongerInSyncVersions: true
sleMicro55KVM:
included: true
name: sle-micro-5.5-kvm
enabled: false
image: registry.opensuse.org/isv/rancher/elemental/dev/containers/rancher/elemental-channel/sle-micro:5.5-kvm
deleteNoLongerInSyncVersions: true
sleMicro55RT:
included: true
name: sle-micro-5.5-rt
enabled: false
image: registry.opensuse.org/isv/rancher/elemental/dev/containers/rancher/elemental-channel/sle-micro:5.5-rt
deleteNoLongerInSyncVersions: true

# number of operator replicas to deploy
replicas: 1
Expand Down
5 changes: 3 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -193,13 +193,14 @@ setup-full-cluster: build-docker-operator build-docker-seedimage-builder chart s
kind load docker-image --name $(CLUSTER_NAME) ${REGISTRY_HEADER}${REPO_SEEDIMAGE}:${TAG_SEEDIMAGE} && \
cd $(ROOT_DIR)/tests && $(GINKGO) -r -v --label-filter="do-nothing" ./e2e

# This builds the docker image, generates the chart, loads the image into the kind cluster and upgrades the chart to latest
# This generates the chart, builds the docker image, loads the image into the kind cluster and upgrades the chart to latest
# useful to test changes into the operator with a running system, without clearing the operator namespace
# thus losing any registration/inventories/os CRDs already created
reload-operator: build-docker-operator chart
reload-operator: chart build-docker-operator
kind load docker-image --name $(CLUSTER_NAME) ${REGISTRY_HEADER}${REPO}:${CHART_VERSION}
helm upgrade -n cattle-elemental-system elemental-operator-crds $(CHART_CRDS)
helm upgrade -n cattle-elemental-system elemental-operator $(CHART)
kubectl -n cattle-elemental-system rollout restart deployment/elemental-operator

.PHONY: vendor
vendor:
Expand Down
3 changes: 3 additions & 0 deletions api/v1beta1/condition_consts.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,9 @@ const (

// FailedToCreatePodReason documents that managed OS version channel failed to create the synchronization pod
FailedToCreatePodReason = "FailedToCreatePod"

// ChannelDisabledReason documents that the managed OS version channel is not enabled
ChannelDisabledReason = "ChannelDisabled"
)

// Managed OS Image conditions
Expand Down
3 changes: 3 additions & 0 deletions api/v1beta1/managedosversionchannel_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ type ManagedOSVersionChannelSpec struct {
// +optional
// +kubebuilder:default:=false
DeleteNoLongerInSyncVersions bool `json:"deleteNoLongerInSyncVersions,omitempty"`
// +optional
// +kubebuilder:default:=true
Enabled bool `json:"enabled"`
// +kubebuilder:validation:Schemaless
// +kubebuilder:validation:XPreserveUnknownFields
// +optional
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ spec:
DeleteNoLongerInSyncVersions automatically deletes
all no-longer-in-sync ManagedOSVersions that were created by this channel.
type: boolean
enabled:
default: true
type: boolean
options:
x-kubernetes-preserve-unknown-fields: true
syncInterval:
Expand Down
82 changes: 66 additions & 16 deletions controllers/managedosversionchannel_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,29 @@ func (r *ManagedOSVersionChannelReconciler) reconcile(ctx context.Context, manag
return ctrl.Result{}, nil
}

if !managedOSVersionChannel.Spec.Enabled {
logger.Info("Channel is disabled. Skipping sync.")
curVersions := r.getAllOwnedManagedOSVersions(ctx, client.ObjectKey{
Name: managedOSVersionChannel.Name,
Namespace: managedOSVersionChannel.Namespace,
})
for _, version := range curVersions {
if err := r.deprecateVersion(ctx, *managedOSVersionChannel, version); err != nil {
return ctrl.Result{}, fmt.Errorf("Deprecating ManagedOSVersion %s: %w", version.Name, err)
}
}
if err := r.deleteSyncerPod(ctx, *managedOSVersionChannel); err != nil {
return ctrl.Result{}, fmt.Errorf("deleting syncer pod: %w", err)
}
meta.SetStatusCondition(&managedOSVersionChannel.Status.Conditions, metav1.Condition{
Type: elementalv1.ReadyCondition,
Reason: elementalv1.ChannelDisabledReason,
Status: metav1.ConditionTrue,
Message: "Channel is disabled",
})
return ctrl.Result{}, nil
}

reachedNextInterval := false
lastSync := managedOSVersionChannel.Status.LastSyncedTime
if lastSync != nil && lastSync.Add(interval).Before(time.Now()) {
Expand Down Expand Up @@ -365,29 +388,38 @@ func (r *ManagedOSVersionChannelReconciler) createManagedOSVersions(ctx context.
// Flagging orphan versions
for _, version := range curVersions {
if lastSyncTime, found := version.Annotations[elementalv1.ElementalManagedOSVersionChannelLastSyncAnnotation]; !found || (lastSyncTime != syncTimestamp) {
logger.Info("ManagedOSVersion no longer synced through this channel", "name", version.Name)
patchBase := client.MergeFrom(version.DeepCopy())
if version.ObjectMeta.Annotations == nil {
version.ObjectMeta.Annotations = map[string]string{}
}
version.ObjectMeta.Annotations[elementalv1.ElementalManagedOSVersionNoLongerSyncedAnnotation] = elementalv1.ElementalManagedOSVersionNoLongerSyncedValue
if err := r.Patch(ctx, version, patchBase); err != nil {
logger.Error(err, "Could not patch ManagedOSVersion as no longer in sync", "name", version.Name)
return fmt.Errorf("deprecating ManagedOSVersion '%s': %w", version.Name, err)
}
if ch.Spec.DeleteNoLongerInSyncVersions {
logger.Info("Auto-deleting no longer in sync ManagedOSVersion due to channel settings", "name", version.Name)
if err := r.Delete(ctx, version); err != nil {
logger.Error(err, "Could not auto-delete no longer in sync ManagedOSVersion")
return fmt.Errorf("auto-deleting ManagedOSVersion '%s': %w", version.Name, err)
}
if err := r.deprecateVersion(ctx, *ch, version); err != nil {
return fmt.Errorf("Deprecating ManagedOSVersion %s: %w", version.Name, err)
}
}
}

return nil
}

// deprecateVersion flags a ManagedOSVersion as orphan and if needed trigger its deletion.
func (r *ManagedOSVersionChannelReconciler) deprecateVersion(ctx context.Context, channel elementalv1.ManagedOSVersionChannel, version *elementalv1.ManagedOSVersion) error {
logger := ctrl.LoggerFrom(ctx).WithValues("ManagedOSVersionChannel", channel.Name).WithValues("ManagedOSVersion", version.Name)
logger.Info("ManagedOSVersion no longer synced through this channel")
patchBase := client.MergeFrom(version.DeepCopy())
if version.ObjectMeta.Annotations == nil {
version.ObjectMeta.Annotations = map[string]string{}
}
version.ObjectMeta.Annotations[elementalv1.ElementalManagedOSVersionNoLongerSyncedAnnotation] = elementalv1.ElementalManagedOSVersionNoLongerSyncedValue
if err := r.Patch(ctx, version, patchBase); err != nil {
logger.Error(err, "Could not patch ManagedOSVersion as no longer in sync")
return fmt.Errorf("deprecating ManagedOSVersion '%s': %w", version.Name, err)
}
if channel.Spec.DeleteNoLongerInSyncVersions {
logger.Info("Auto-deleting no longer in sync ManagedOSVersion due to channel settings")
if err := r.Delete(ctx, version); err != nil {
logger.Error(err, "Could not auto-delete no longer in sync ManagedOSVersion")
return fmt.Errorf("auto-deleting ManagedOSVersion '%s': %w", version.Name, err)
}
}
return nil
}

// getAllOwnedManagedOSVersions returns a map of all ManagedOSVersions labeled with the given channel, resource name is used as the map key
func (r *ManagedOSVersionChannelReconciler) getAllOwnedManagedOSVersions(ctx context.Context, chKey client.ObjectKey) map[string]*elementalv1.ManagedOSVersion {
logger := ctrl.LoggerFrom(ctx)
Expand Down Expand Up @@ -485,6 +517,24 @@ func (r *ManagedOSVersionChannelReconciler) createSyncerPod(ctx context.Context,
return nil
}

// deleteSyncerPod deletes the syncer pod if it exists
func (r *ManagedOSVersionChannelReconciler) deleteSyncerPod(ctx context.Context, channel elementalv1.ManagedOSVersionChannel) error {
pod := &corev1.Pod{}
if err := r.Get(ctx, client.ObjectKey{
Namespace: channel.Namespace,
Name: channel.Name,
}, pod); apierrors.IsNotFound(err) {
// Pod does not exist. Nothing to do.
return nil
} else if err != nil {
return fmt.Errorf("getting pod: %w", err)
}
if err := r.Delete(ctx, pod); err != nil {
return fmt.Errorf("deleting pod: %w", err)
}
return nil
}

// filterChannelEvents is a method that filters reconcile requests events for the channels reconciler.
// ManagedOSVersionChannelReconciler watches channels and owned pods. This filter ignores pod
// create/delete/generic events and only reacts on pod phase updates. Channel update events are
Expand Down
Loading
Loading