Skip to content

Commit

Permalink
Implement Enabled field on ManagedOSVersionChannels (#800)
Browse files Browse the repository at this point in the history
* Implement channel Enabled flag

Signed-off-by: Andrea Mazzotti <[email protected]>
  • Loading branch information
anmazzotti authored Jul 30, 2024
1 parent b866419 commit d280e61
Show file tree
Hide file tree
Showing 11 changed files with 256 additions and 37 deletions.
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:
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

0 comments on commit d280e61

Please sign in to comment.