@@ -591,6 +591,7 @@ func setControlPlaneSpecDefaults(spec *infrav2exp.OCIManagedControlPlaneSpec) {
591591 if spec .ClusterType == "" {
592592 spec .ClusterType = infrav2exp .BasicClusterType
593593 }
594+ spec .Addons = nil
594595 if spec .ImagePolicyConfig == nil {
595596 spec .ImagePolicyConfig = & infrav2exp.ImagePolicyConfig {
596597 IsPolicyEnabled : common .Bool (false ),
@@ -615,6 +616,7 @@ func (s *ManagedControlPlaneScope) getSpecFromActual(cluster *oke.Cluster) *infr
615616 Version : cluster .KubernetesVersion ,
616617 KmsKeyId : cluster .KmsKeyId ,
617618 ID : cluster .Id ,
619+ Addons : nil ,
618620 }
619621 if cluster .ImagePolicyConfig != nil {
620622 keys := make ([]infrav2exp.KeyDetails , 0 )
@@ -673,6 +675,183 @@ func (s *ManagedControlPlaneScope) getSpecFromActual(cluster *oke.Cluster) *infr
673675 return & spec
674676}
675677
678+ // ReconcileAddons reconciles addons which have been specified in the spec on the OKE cluster
679+ func (s * ManagedControlPlaneScope ) ReconcileAddons (ctx context.Context , okeCluster * oke.Cluster ) error {
680+ addonSpec := s .OCIManagedControlPlane .Spec .Addons
681+ // go through the list of addons present in the spec and reconcile them, reconcile can be 2 ways, either
682+ // install the addon if it has not been installed till now, or update the addon
683+ for _ , addon := range addonSpec {
684+ resp , err := s .ContainerEngineClient .GetAddon (ctx , oke.GetAddonRequest {
685+ ClusterId : okeCluster .Id ,
686+ AddonName : addon .Name ,
687+ })
688+ if err != nil {
689+ // addon is not present, hence install it
690+ if ociutil .IsNotFound (err ) {
691+ s .Info (fmt .Sprintf ("Install addon %s" , * addon .Name ))
692+ _ , err = s .ContainerEngineClient .InstallAddon (ctx , oke.InstallAddonRequest {
693+ ClusterId : okeCluster .Id ,
694+ InstallAddonDetails : oke.InstallAddonDetails {
695+ Version : addon .Version ,
696+ Configurations : getAddonConfigurations (addon .Configurations ),
697+ AddonName : addon .Name ,
698+ },
699+ })
700+ if err != nil {
701+ return err
702+ }
703+ // add it to status, details will be reconciled in next loop
704+ status := infrav2exp.AddonStatus {
705+ LifecycleState : common .String (string (oke .AddonLifecycleStateCreating )),
706+ }
707+ s .OCIManagedControlPlane .SetAddonStatus (* addon .Name , status )
708+ } else {
709+ return err
710+ }
711+ } else {
712+ s .OCIManagedControlPlane .SetAddonStatus (* addon .Name , s .getStatus (resp .Addon ))
713+ // addon present, update it
714+ err = s .handleExistingAddon (ctx , okeCluster , resp .Addon , addon )
715+ if err != nil {
716+ return err
717+ }
718+ }
719+ }
720+ // for addons which are present in the status object but not in the spec, the possibility
721+ // is that user deleted it from the spec. Hence disable the addon
722+ for k , _ := range s .OCIManagedControlPlane .Status .AddonStatus {
723+ // present in status but not in spec
724+ if getAddon (addonSpec , k ) == nil {
725+ err := s .handleDeletedAddon (ctx , okeCluster , k )
726+ if err != nil {
727+ return err
728+ }
729+ }
730+ }
731+ return nil
732+ }
733+
734+ func (s * ManagedControlPlaneScope ) getStatus (addon oke.Addon ) infrav2exp.AddonStatus {
735+ // update status of the addon
736+ status := infrav2exp.AddonStatus {
737+ LifecycleState : common .String (string (addon .LifecycleState )),
738+ CurrentlyInstalledVersion : addon .CurrentInstalledVersion ,
739+ }
740+ if addon .AddonError != nil {
741+ status .AddonError = & infrav2exp.AddonError {
742+ Status : addon .AddonError .Status ,
743+ Code : addon .AddonError .Code ,
744+ Message : addon .AddonError .Message ,
745+ }
746+ }
747+ return status
748+ }
749+
750+ func (s * ManagedControlPlaneScope ) handleExistingAddon (ctx context.Context , okeCluster * oke.Cluster , addon oke.Addon , addonInSpec infrav2exp.Addon ) error {
751+ // if the addon can be updated do so
752+ // if the addon is already in updating state, or in failed state, do not update
753+ s .Info (fmt .Sprintf ("Reconciling addon %s with lifecycle state %s" , * addon .Name , string (addon .LifecycleState )))
754+ if ! (addon .LifecycleState == oke .AddonLifecycleStateUpdating ||
755+ addon .LifecycleState == oke .AddonLifecycleStateFailed ) {
756+ addonConfigurationsActual := getActualAddonConfigurations (addon .Configurations )
757+ // if the version changed or the configuration changed, update the addon
758+ // if the lifecycle state is needs attention, try to update
759+ if addon .LifecycleState == oke .AddonLifecycleStateNeedsAttention ||
760+ ! reflect .DeepEqual (addonInSpec .Version , addon .Version ) ||
761+ ! reflect .DeepEqual (addonConfigurationsActual , addonInSpec .Configurations ) {
762+ s .Info (fmt .Sprintf ("Updating addon %s" , * addon .Name ))
763+ _ , err := s .ContainerEngineClient .UpdateAddon (ctx , oke.UpdateAddonRequest {
764+ ClusterId : okeCluster .Id ,
765+ AddonName : addon .Name ,
766+ UpdateAddonDetails : oke.UpdateAddonDetails {
767+ Version : addonInSpec .Version ,
768+ Configurations : getAddonConfigurations (addonInSpec .Configurations ),
769+ },
770+ })
771+ if err != nil {
772+ return err
773+ }
774+ }
775+ }
776+ return nil
777+ }
778+
779+ func (s * ManagedControlPlaneScope ) handleDeletedAddon (ctx context.Context , okeCluster * oke.Cluster , addonName string ) error {
780+ resp , err := s .ContainerEngineClient .GetAddon (ctx , oke.GetAddonRequest {
781+ ClusterId : okeCluster .Id ,
782+ AddonName : common .String (addonName ),
783+ })
784+ if err != nil {
785+ if ociutil .IsNotFound (err ) {
786+ s .OCIManagedControlPlane .RemoveAddonStatus (addonName )
787+ return nil
788+ } else {
789+ return err
790+ }
791+ }
792+ addonState := resp .LifecycleState
793+ switch addonState {
794+ // nothing to do if addon is in deleting state
795+ case oke .AddonLifecycleStateDeleting :
796+ s .Info (fmt .Sprintf ("Addon %s is in deleting state" , addonName ))
797+ break
798+ case oke .AddonLifecycleStateDeleted :
799+ // delete addon from status if addon has been deleted
800+ s .Info (fmt .Sprintf ("Addon %s is in deleted state" , addonName ))
801+ s .OCIManagedControlPlane .RemoveAddonStatus (addonName )
802+ break
803+ default :
804+ // else delete the addon
805+ // delete addon is called disable addon with remove flag turned on
806+ _ , err := s .ContainerEngineClient .DisableAddon (ctx , oke.DisableAddonRequest {
807+ ClusterId : okeCluster .Id ,
808+ AddonName : common .String (addonName ),
809+ IsRemoveExistingAddOn : common .Bool (true ),
810+ })
811+ if err != nil {
812+ return err
813+ }
814+ }
815+ return nil
816+ }
817+
818+ func getAddonConfigurations (configurations []infrav2exp.AddonConfiguration ) []oke.AddonConfiguration {
819+ if len (configurations ) == 0 {
820+ return nil
821+ }
822+ config := make ([]oke.AddonConfiguration , len (configurations ))
823+ for i , c := range configurations {
824+ config [i ] = oke.AddonConfiguration {
825+ Key : c .Key ,
826+ Value : c .Value ,
827+ }
828+ }
829+ return config
830+ }
831+
832+ func getActualAddonConfigurations (addonConfigurations []oke.AddonConfiguration ) []infrav2exp.AddonConfiguration {
833+ if len (addonConfigurations ) == 0 {
834+ return nil
835+ }
836+ config := make ([]infrav2exp.AddonConfiguration , len (addonConfigurations ))
837+ for i , c := range addonConfigurations {
838+ config [i ] = infrav2exp.AddonConfiguration {
839+ Key : c .Key ,
840+ Value : c .Value ,
841+ }
842+ }
843+ return config
844+ }
845+
846+ func getAddon (addons []infrav2exp.Addon , name string ) * infrav2exp.Addon {
847+ for i , addon := range addons {
848+ if * addon .Name == name {
849+ return & addons [i ]
850+ }
851+ }
852+ return nil
853+ }
854+
676855func getKubeConfigUserName (clusterName string , isUser bool ) string {
677856 if isUser {
678857 return fmt .Sprintf ("%s-user" , clusterName )
0 commit comments