@@ -652,6 +652,9 @@ def update_package_host_configuration(self, env_name: str, package_name: str,
652652 hostname : str , server_config : dict ) -> bool :
653653 """Update package metadata with host configuration tracking.
654654
655+ Enforces constraint: Only one environment can control a package-host combination.
656+ Automatically cleans up conflicting configurations from other environments.
657+
655658 Args:
656659 env_name (str): Environment name
657660 package_name (str): Package name
@@ -666,37 +669,118 @@ def update_package_host_configuration(self, env_name: str, package_name: str,
666669 self .logger .error (f"Environment { env_name } does not exist" )
667670 return False
668671
669- # Find the package in the environment
670- packages = self ._environments [env_name ].get ("packages" , [])
671- for i , pkg in enumerate (packages ):
672- if pkg .get ("name" ) == package_name :
673- # Initialize configured_hosts if it doesn't exist
674- if "configured_hosts" not in pkg :
675- pkg ["configured_hosts" ] = {}
676-
677- # Add or update host configuration
678- from datetime import datetime
679- pkg ["configured_hosts" ][hostname ] = {
680- "config_path" : self ._get_host_config_path (hostname ),
681- "configured_at" : datetime .now ().isoformat (),
682- "last_synced" : datetime .now ().isoformat (),
683- "server_config" : server_config
684- }
672+ # Step 1: Clean up conflicting configurations from other environments
673+ conflicts_removed = self ._cleanup_package_host_conflicts (
674+ target_env = env_name ,
675+ package_name = package_name ,
676+ hostname = hostname
677+ )
685678
686- # Update the package in the environment
687- self ._environments [env_name ]["packages" ][i ] = pkg
688- self ._save_environments ()
679+ # Step 2: Update target environment configuration
680+ success = self ._update_target_environment_configuration (
681+ env_name , package_name , hostname , server_config
682+ )
689683
690- self .logger .info (f"Updated host configuration for package { package_name } on { hostname } " )
691- return True
684+ # Step 3: User notification for conflict resolution
685+ if conflicts_removed > 0 and success :
686+ self .logger .warning (
687+ f"Package '{ package_name } ' host configuration for '{ hostname } ' "
688+ f"transferred from { conflicts_removed } other environment(s) to '{ env_name } '"
689+ )
692690
693- self .logger .error (f"Package { package_name } not found in environment { env_name } " )
694- return False
691+ return success
695692
696693 except Exception as e :
697694 self .logger .error (f"Failed to update package host configuration: { e } " )
698695 return False
699696
697+ def _cleanup_package_host_conflicts (self , target_env : str , package_name : str , hostname : str ) -> int :
698+ """Remove conflicting package-host configurations from other environments.
699+
700+ This method enforces the constraint that only one environment can control
701+ a package-host combination by removing conflicting configurations from
702+ all environments except the target environment.
703+
704+ Args:
705+ target_env (str): Environment that should control the configuration
706+ package_name (str): Package name
707+ hostname (str): Host identifier
708+
709+ Returns:
710+ int: Number of conflicting configurations removed
711+ """
712+ conflicts_removed = 0
713+
714+ for env_name , env_data in self ._environments .items ():
715+ if env_name == target_env :
716+ continue # Skip target environment
717+
718+ packages = env_data .get ("packages" , [])
719+ for i , pkg in enumerate (packages ):
720+ if pkg .get ("name" ) == package_name :
721+ configured_hosts = pkg .get ("configured_hosts" , {})
722+ if hostname in configured_hosts :
723+ # Remove the conflicting host configuration
724+ del configured_hosts [hostname ]
725+ conflicts_removed += 1
726+
727+ # Update package metadata
728+ pkg ["configured_hosts" ] = configured_hosts
729+ self ._environments [env_name ]["packages" ][i ] = pkg
730+
731+ self .logger .info (
732+ f"Removed conflicting '{ hostname } ' configuration for package '{ package_name } ' "
733+ f"from environment '{ env_name } '"
734+ )
735+
736+ if conflicts_removed > 0 :
737+ self ._save_environments ()
738+
739+ return conflicts_removed
740+
741+ def _update_target_environment_configuration (self , env_name : str , package_name : str ,
742+ hostname : str , server_config : dict ) -> bool :
743+ """Update the target environment's package host configuration.
744+
745+ This method handles the actual configuration update for the target environment
746+ after conflicts have been cleaned up.
747+
748+ Args:
749+ env_name (str): Environment name
750+ package_name (str): Package name
751+ hostname (str): Host identifier
752+ server_config (dict): Server configuration data
753+
754+ Returns:
755+ bool: True if update successful, False otherwise
756+ """
757+ # Find the package in the environment
758+ packages = self ._environments [env_name ].get ("packages" , [])
759+ for i , pkg in enumerate (packages ):
760+ if pkg .get ("name" ) == package_name :
761+ # Initialize configured_hosts if it doesn't exist
762+ if "configured_hosts" not in pkg :
763+ pkg ["configured_hosts" ] = {}
764+
765+ # Add or update host configuration
766+ from datetime import datetime
767+ pkg ["configured_hosts" ][hostname ] = {
768+ "config_path" : self ._get_host_config_path (hostname ),
769+ "configured_at" : datetime .now ().isoformat (),
770+ "last_synced" : datetime .now ().isoformat (),
771+ "server_config" : server_config
772+ }
773+
774+ # Update the package in the environment
775+ self ._environments [env_name ]["packages" ][i ] = pkg
776+ self ._save_environments ()
777+
778+ self .logger .info (f"Updated host configuration for package { package_name } on { hostname } " )
779+ return True
780+
781+ self .logger .error (f"Package { package_name } not found in environment { env_name } " )
782+ return False
783+
700784 def remove_package_host_configuration (self , env_name : str , package_name : str , hostname : str ) -> bool :
701785 """Remove host configuration tracking for a specific package.
702786
0 commit comments