mirror of
https://github.com/open-cluster-management-io/ocm.git
synced 2026-02-14 18:09:57 +00:00
* update vendor to add addon-framework Signed-off-by: zhujian <jiazhu@redhat.com> * Move addon manager from addon-framework to ocm repo Signed-off-by: zhujian <jiazhu@redhat.com> * add integration tests for addon manager Signed-off-by: zhujian <jiazhu@redhat.com> * push addon manager image post commit Signed-off-by: zhujian <jiazhu@redhat.com> * use library-go to refactor addon controllers Signed-off-by: zhujian <jiazhu@redhat.com> --------- Signed-off-by: zhujian <jiazhu@redhat.com>
256 lines
9.6 KiB
Go
256 lines
9.6 KiB
Go
package managementaddoninstallprogression
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
|
|
jsonpatch "github.com/evanphx/json-patch"
|
|
"github.com/openshift/library-go/pkg/controller/factory"
|
|
"github.com/openshift/library-go/pkg/operator/events"
|
|
"k8s.io/apimachinery/pkg/api/equality"
|
|
"k8s.io/apimachinery/pkg/api/errors"
|
|
"k8s.io/apimachinery/pkg/api/meta"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
"k8s.io/apimachinery/pkg/types"
|
|
"k8s.io/klog/v2"
|
|
|
|
addonv1alpha1 "open-cluster-management.io/api/addon/v1alpha1"
|
|
addonv1alpha1client "open-cluster-management.io/api/client/addon/clientset/versioned"
|
|
addoninformerv1alpha1 "open-cluster-management.io/api/client/addon/informers/externalversions/addon/v1alpha1"
|
|
addonlisterv1alpha1 "open-cluster-management.io/api/client/addon/listers/addon/v1alpha1"
|
|
)
|
|
|
|
// managementAddonInstallProgressionController reconciles instances of clustermanagementaddon the hub
|
|
// based to update related object and status condition.
|
|
type managementAddonInstallProgressionController struct {
|
|
addonClient addonv1alpha1client.Interface
|
|
managedClusterAddonLister addonlisterv1alpha1.ManagedClusterAddOnLister
|
|
clusterManagementAddonLister addonlisterv1alpha1.ClusterManagementAddOnLister
|
|
addonFilterFunc factory.EventFilterFunc
|
|
}
|
|
|
|
func NewManagementAddonInstallProgressionController(
|
|
addonClient addonv1alpha1client.Interface,
|
|
addonInformers addoninformerv1alpha1.ManagedClusterAddOnInformer,
|
|
clusterManagementAddonInformers addoninformerv1alpha1.ClusterManagementAddOnInformer,
|
|
addonFilterFunc factory.EventFilterFunc,
|
|
recorder events.Recorder,
|
|
) factory.Controller {
|
|
c := &managementAddonInstallProgressionController{
|
|
addonClient: addonClient,
|
|
managedClusterAddonLister: addonInformers.Lister(),
|
|
clusterManagementAddonLister: clusterManagementAddonInformers.Lister(),
|
|
addonFilterFunc: addonFilterFunc,
|
|
}
|
|
|
|
return factory.New().WithInformersQueueKeysFunc(
|
|
func(obj runtime.Object) []string {
|
|
accessor, _ := meta.Accessor(obj)
|
|
return []string{accessor.GetName()}
|
|
},
|
|
addonInformers.Informer(), clusterManagementAddonInformers.Informer()).
|
|
WithSync(c.sync).ToController("management-addon-status-controller", recorder)
|
|
|
|
}
|
|
|
|
func (c *managementAddonInstallProgressionController) sync(ctx context.Context, syncCtx factory.SyncContext) error {
|
|
addonName := syncCtx.QueueKey()
|
|
klog.V(4).Infof("Reconciling addon %q", addonName)
|
|
|
|
mgmtAddon, err := c.clusterManagementAddonLister.Get(addonName)
|
|
switch {
|
|
case errors.IsNotFound(err):
|
|
return nil
|
|
case err != nil:
|
|
return err
|
|
}
|
|
|
|
mgmtAddonCopy := mgmtAddon.DeepCopy()
|
|
|
|
clusterManagementAddon, err := c.clusterManagementAddonLister.Get(addonName)
|
|
if errors.IsNotFound(err) {
|
|
return nil
|
|
}
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// set default config reference
|
|
mgmtAddonCopy.Status.DefaultConfigReferences = setDefaultConfigReference(mgmtAddonCopy.Spec.SupportedConfigs, mgmtAddonCopy.Status.DefaultConfigReferences)
|
|
|
|
// update default config reference when type is manual
|
|
if mgmtAddonCopy.Spec.InstallStrategy.Type == "" || mgmtAddonCopy.Spec.InstallStrategy.Type == addonv1alpha1.AddonInstallStrategyManual {
|
|
mgmtAddonCopy.Status.InstallProgressions = []addonv1alpha1.InstallProgression{}
|
|
return c.patchMgmtAddonStatus(ctx, mgmtAddonCopy, mgmtAddon)
|
|
}
|
|
|
|
// only update default config references and skip updating install progression for self-managed addon
|
|
if !c.addonFilterFunc(clusterManagementAddon) {
|
|
return c.patchMgmtAddonStatus(ctx, mgmtAddonCopy, mgmtAddon)
|
|
}
|
|
|
|
// set install progression
|
|
mgmtAddonCopy.Status.InstallProgressions = setInstallProgression(mgmtAddonCopy.Spec.SupportedConfigs,
|
|
mgmtAddonCopy.Spec.InstallStrategy.Placements, mgmtAddonCopy.Status.InstallProgressions)
|
|
|
|
// update cma status
|
|
return c.patchMgmtAddonStatus(ctx, mgmtAddonCopy, mgmtAddon)
|
|
}
|
|
|
|
func (c *managementAddonInstallProgressionController) patchMgmtAddonStatus(ctx context.Context, new, old *addonv1alpha1.ClusterManagementAddOn) error {
|
|
if equality.Semantic.DeepEqual(new.Status, old.Status) {
|
|
return nil
|
|
}
|
|
|
|
oldData, err := json.Marshal(&addonv1alpha1.ClusterManagementAddOn{
|
|
Status: addonv1alpha1.ClusterManagementAddOnStatus{
|
|
DefaultConfigReferences: old.Status.DefaultConfigReferences,
|
|
InstallProgressions: old.Status.InstallProgressions,
|
|
},
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
newData, err := json.Marshal(&addonv1alpha1.ClusterManagementAddOn{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
UID: new.UID,
|
|
ResourceVersion: new.ResourceVersion,
|
|
},
|
|
Status: addonv1alpha1.ClusterManagementAddOnStatus{
|
|
DefaultConfigReferences: new.Status.DefaultConfigReferences,
|
|
InstallProgressions: new.Status.InstallProgressions,
|
|
},
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
patchBytes, err := jsonpatch.CreateMergePatch(oldData, newData)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create patch for addon %s: %w", new.Name, err)
|
|
}
|
|
|
|
klog.V(2).Infof("Patching clustermanagementaddon %s status with %s", new.Name, string(patchBytes))
|
|
_, err = c.addonClient.AddonV1alpha1().ClusterManagementAddOns().Patch(
|
|
ctx, new.Name, types.MergePatchType, patchBytes, metav1.PatchOptions{}, "status")
|
|
return err
|
|
}
|
|
|
|
func setDefaultConfigReference(supportedConfigs []addonv1alpha1.ConfigMeta,
|
|
existDefaultConfigReferences []addonv1alpha1.DefaultConfigReference) []addonv1alpha1.DefaultConfigReference {
|
|
newDefaultConfigReferences := []addonv1alpha1.DefaultConfigReference{}
|
|
for _, config := range supportedConfigs {
|
|
if config.DefaultConfig == nil {
|
|
continue
|
|
}
|
|
configRef := addonv1alpha1.DefaultConfigReference{
|
|
ConfigGroupResource: config.ConfigGroupResource,
|
|
DesiredConfig: &addonv1alpha1.ConfigSpecHash{
|
|
ConfigReferent: *config.DefaultConfig,
|
|
},
|
|
}
|
|
// if the config already exists in status, keep the existing spec hash
|
|
if existConfigRef, exist := findDefaultConfigReference(&configRef, existDefaultConfigReferences); exist {
|
|
configRef.DesiredConfig.SpecHash = existConfigRef.DesiredConfig.SpecHash
|
|
}
|
|
newDefaultConfigReferences = append(newDefaultConfigReferences, configRef)
|
|
}
|
|
return newDefaultConfigReferences
|
|
}
|
|
|
|
func findDefaultConfigReference(
|
|
newobj *addonv1alpha1.DefaultConfigReference,
|
|
oldobjs []addonv1alpha1.DefaultConfigReference,
|
|
) (*addonv1alpha1.DefaultConfigReference, bool) {
|
|
for _, oldconfig := range oldobjs {
|
|
if oldconfig.ConfigGroupResource == newobj.ConfigGroupResource && oldconfig.DesiredConfig.ConfigReferent == newobj.DesiredConfig.ConfigReferent {
|
|
return &oldconfig, true
|
|
}
|
|
}
|
|
return nil, false
|
|
}
|
|
|
|
func setInstallProgression(supportedConfigs []addonv1alpha1.ConfigMeta, placementStrategies []addonv1alpha1.PlacementStrategy,
|
|
existInstallProgressions []addonv1alpha1.InstallProgression) []addonv1alpha1.InstallProgression {
|
|
newInstallProgressions := []addonv1alpha1.InstallProgression{}
|
|
for _, placementStrategy := range placementStrategies {
|
|
// set placement ref
|
|
installProgression := addonv1alpha1.InstallProgression{
|
|
PlacementRef: placementStrategy.PlacementRef,
|
|
}
|
|
|
|
// set config references as default configuration
|
|
installConfigReferences := []addonv1alpha1.InstallConfigReference{}
|
|
installConfigReferencesMap := map[addonv1alpha1.ConfigGroupResource]addonv1alpha1.ConfigReferent{}
|
|
for _, config := range supportedConfigs {
|
|
if config.DefaultConfig != nil {
|
|
installConfigReferencesMap[config.ConfigGroupResource] = *config.DefaultConfig
|
|
}
|
|
}
|
|
|
|
// override the default configuration for each placement
|
|
for _, config := range placementStrategy.Configs {
|
|
installConfigReferencesMap[config.ConfigGroupResource] = config.ConfigReferent
|
|
}
|
|
|
|
// set the config references for each install progression
|
|
for k, v := range installConfigReferencesMap {
|
|
installConfigReferences = append(installConfigReferences,
|
|
addonv1alpha1.InstallConfigReference{
|
|
ConfigGroupResource: k,
|
|
DesiredConfig: &addonv1alpha1.ConfigSpecHash{
|
|
ConfigReferent: v,
|
|
},
|
|
},
|
|
)
|
|
}
|
|
installProgression.ConfigReferences = installConfigReferences
|
|
|
|
// if the config group resource already exists in status, merge the install progression
|
|
if existInstallProgression, exist := findInstallProgression(&installProgression, existInstallProgressions); exist {
|
|
mergeInstallProgression(&installProgression, existInstallProgression)
|
|
}
|
|
newInstallProgressions = append(newInstallProgressions, installProgression)
|
|
}
|
|
return newInstallProgressions
|
|
}
|
|
|
|
func findInstallProgression(newobj *addonv1alpha1.InstallProgression, oldobjs []addonv1alpha1.InstallProgression) (*addonv1alpha1.InstallProgression, bool) {
|
|
for _, oldobj := range oldobjs {
|
|
if oldobj.PlacementRef == newobj.PlacementRef {
|
|
count := 0
|
|
for _, oldconfig := range oldobj.ConfigReferences {
|
|
for _, newconfig := range newobj.ConfigReferences {
|
|
if oldconfig.ConfigGroupResource == newconfig.ConfigGroupResource {
|
|
count += 1
|
|
}
|
|
}
|
|
}
|
|
if count == len(newobj.ConfigReferences) {
|
|
return &oldobj, true
|
|
}
|
|
}
|
|
}
|
|
return nil, false
|
|
}
|
|
|
|
func mergeInstallProgression(newobj, oldobj *addonv1alpha1.InstallProgression) {
|
|
// merge config reference
|
|
for i := range newobj.ConfigReferences {
|
|
for _, oldconfig := range oldobj.ConfigReferences {
|
|
if newobj.ConfigReferences[i].ConfigGroupResource == oldconfig.ConfigGroupResource {
|
|
if newobj.ConfigReferences[i].DesiredConfig.ConfigReferent == oldconfig.DesiredConfig.ConfigReferent {
|
|
newobj.ConfigReferences[i].DesiredConfig.SpecHash = oldconfig.DesiredConfig.SpecHash
|
|
}
|
|
newobj.ConfigReferences[i].LastAppliedConfig = oldconfig.LastAppliedConfig.DeepCopy()
|
|
newobj.ConfigReferences[i].LastKnownGoodConfig = oldconfig.LastKnownGoodConfig.DeepCopy()
|
|
}
|
|
}
|
|
}
|
|
newobj.Conditions = oldobj.Conditions
|
|
}
|