@@ -248,6 +248,8 @@ func ResourceContainerCluster() *schema.Resource {
248248 containerClusterEnableK8sBetaApisCustomizeDiff ,
249249 containerClusterNodeVersionCustomizeDiff ,
250250 tpgresource .SetDiffForLabelsWithCustomizedName ("resource_labels" ),
251+
252+ clusterAcceleratorNetworkProfileCustomizeDiff ,
251253 ),
252254
253255 Timeouts : & schema.ResourceTimeout {
@@ -8417,3 +8419,156 @@ func containerClusterNodeVersionCustomizeDiffFunc(diff tpgresource.TerraformReso
84178419
84188420 return nil
84198421}
8422+
8423+ func clusterAcceleratorNetworkProfileCustomizeDiff (_ context.Context , diff * schema.ResourceDiff , meta any ) error {
8424+ // 1. SKIP ON CREATE
8425+ if diff .Id () == "" {
8426+ return nil
8427+ }
8428+
8429+ // 2. PREPARE TO UPDATE THE FULL LIST
8430+ oldNodePools := diff .Get ("node_pool" ).([]interface {})
8431+ newNodePools := make ([]interface {}, len (oldNodePools ))
8432+ listChanged := false
8433+
8434+ // We need Raw Config to check what the user actually wrote
8435+ rawConfig := diff .GetRawConfig ()
8436+ rawNodePools := rawConfig .GetAttr ("node_pool" )
8437+
8438+ // 3. ITERATE OVER ALL POOLS IN STATE
8439+ for i , np := range oldNodePools {
8440+ // Deep copy the node pool map
8441+ npMap := np .(map [string ]interface {})
8442+ newNpMap := make (map [string ]interface {})
8443+ for k , v := range npMap {
8444+ newNpMap [k ] = v
8445+ }
8446+
8447+ // Check if this specific node pool is actually defined in the Raw Config (Inline).
8448+ // If it is not in Raw Config, it is a Standalone resource (or API generated).
8449+ // We must not touch Standalone resources from the Cluster resource.
8450+ isInline := false
8451+ currentName := npMap ["name" ].(string )
8452+
8453+ // Iterate over Raw Config to find a match by name
8454+ if ! rawNodePools .IsNull () && rawNodePools .Type ().IsCollectionType () {
8455+ it := rawNodePools .ElementIterator ()
8456+ for it .Next () {
8457+ _ , val := it .Element ()
8458+ rawNameVal := val .GetAttr ("name" )
8459+ if ! rawNameVal .IsNull () && rawNameVal .AsString () == currentName {
8460+ isInline = true
8461+ break
8462+ }
8463+ }
8464+ }
8465+
8466+ // If this is NOT an inline pool, copy it as-is and skip logic.
8467+ if ! isInline {
8468+ newNodePools [i ] = newNpMap
8469+ continue
8470+ }
8471+
8472+ // A. DETECT USER CONFIG (Raw Config Check for this specific pool)
8473+ userHasAdditionalConfigs := false
8474+
8475+ // Re-find the specific raw block for logic checking
8476+ if ! rawNodePools .IsNull () {
8477+ it := rawNodePools .ElementIterator ()
8478+ for it .Next () {
8479+ _ , val := it .Element ()
8480+ rawNameVal := val .GetAttr ("name" )
8481+ if ! rawNameVal .IsNull () && rawNameVal .AsString () == currentName {
8482+ // We found the matching raw block, now check its network config
8483+ rawNc := val .GetAttr ("network_config" )
8484+ if ! rawNc .IsNull () && rawNc .Type ().IsCollectionType () {
8485+ ncIt := rawNc .ElementIterator ()
8486+ for ncIt .Next () {
8487+ _ , ncVal := ncIt .Element ()
8488+ userConfig := ncVal .GetAttr ("additional_node_network_configs" )
8489+ if ! userConfig .IsNull () && userConfig .LengthInt () > 0 {
8490+ userHasAdditionalConfigs = true
8491+ }
8492+ }
8493+ }
8494+ break
8495+ }
8496+ }
8497+ }
8498+
8499+ // B. CHECK TRANSITION LOGIC
8500+ shouldClear := false
8501+ basePath := fmt .Sprintf ("node_pool.%d" , i )
8502+ networkConfigPath := basePath + ".network_config.0"
8503+
8504+ oldProfile , newProfile := diff .GetChange (networkConfigPath + ".accelerator_network_profile" )
8505+
8506+ newProfileStr := ""
8507+ if newProfile != nil {
8508+ newProfileStr = newProfile .(string )
8509+ }
8510+ oldProfileStr := ""
8511+ if oldProfile != nil {
8512+ oldProfileStr = oldProfile .(string )
8513+ }
8514+
8515+ anpIsActive := newProfileStr != ""
8516+ anpIsChanging := oldProfileStr != newProfileStr
8517+
8518+ if ! userHasAdditionalConfigs {
8519+ if anpIsActive && anpIsChanging {
8520+ shouldClear = true
8521+ }
8522+ if ! anpIsActive {
8523+ shouldClear = true
8524+ }
8525+ }
8526+
8527+ // Check if additional configs currently exist to avoid no-op
8528+ currentCount := 0
8529+ if c , ok := diff .Get (networkConfigPath + ".additional_node_network_configs.#" ).(int ); ok {
8530+ currentCount = c
8531+ }
8532+ if shouldClear && currentCount == 0 {
8533+ shouldClear = false
8534+ }
8535+
8536+ // C. APPLY FIX TO THE MAP
8537+ if shouldClear {
8538+ log .Printf ("[DEBUG] Cluster ANP CustomizeDiff: Clearing additional configs for INLINE pool %s" , currentName )
8539+
8540+ var newConfigMap map [string ]interface {}
8541+
8542+ if ncList , ok := newNpMap ["network_config" ].([]interface {}); ok && len (ncList ) > 0 {
8543+ if existingMap , ok := ncList [0 ].(map [string ]interface {}); ok {
8544+ newConfigMap = make (map [string ]interface {})
8545+ for k , v := range existingMap {
8546+ newConfigMap [k ] = v
8547+ }
8548+ }
8549+ }
8550+
8551+ if newConfigMap == nil {
8552+ newConfigMap = make (map [string ]interface {})
8553+ }
8554+
8555+ newConfigMap ["additional_node_network_configs" ] = []interface {}{}
8556+
8557+ if ! anpIsActive {
8558+ newConfigMap ["accelerator_network_profile" ] = ""
8559+ }
8560+
8561+ newNpMap ["network_config" ] = []interface {}{newConfigMap }
8562+ listChanged = true
8563+ }
8564+
8565+ newNodePools [i ] = newNpMap
8566+ }
8567+
8568+ // 4. WRITE THE FULL LIST BACK
8569+ if listChanged {
8570+ return diff .SetNew ("node_pool" , newNodePools )
8571+ }
8572+
8573+ return nil
8574+ }
0 commit comments