github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/state/containernetworking.go (about)

     1  // Copyright 2017 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package state
     5  
     6  import (
     7  	"fmt"
     8  	"net"
     9  	"strings"
    10  
    11  	"github.com/juju/errors"
    12  
    13  	"github.com/juju/juju/environs"
    14  	"github.com/juju/juju/environs/config"
    15  	"github.com/juju/juju/environs/context"
    16  )
    17  
    18  // AutoConfigureContainerNetworking tries to set up best container networking available
    19  // for the specific model if user hasn't set anything.
    20  func (m *Model) AutoConfigureContainerNetworking(environ environs.BootstrapEnviron) error {
    21  	updateAttrs := make(map[string]interface{})
    22  	modelConfig, err := m.ModelConfig()
    23  	if err != nil {
    24  		return err
    25  	}
    26  	fanConfigured, err := m.discoverFan(environ, modelConfig, updateAttrs)
    27  	if err != nil {
    28  		return err
    29  	}
    30  
    31  	if modelConfig.ContainerNetworkingMethod() != "" {
    32  		// Do nothing, user has decided what to do
    33  	} else if environs.SupportsContainerAddresses(context.CallContext(m.st), environ) {
    34  		updateAttrs["container-networking-method"] = "provider"
    35  	} else if fanConfigured {
    36  		updateAttrs["container-networking-method"] = "fan"
    37  	} else {
    38  		updateAttrs["container-networking-method"] = "local"
    39  	}
    40  	err = m.UpdateModelConfig(updateAttrs, nil)
    41  	return err
    42  }
    43  
    44  func (m *Model) discoverFan(environ environs.BootstrapEnviron, modelConfig *config.Config, updateAttrs map[string]interface{}) (bool, error) {
    45  	netEnviron, ok := environs.SupportsNetworking(environ)
    46  	if !ok {
    47  		// Not a networking environ, nothing to do here
    48  		return false, nil
    49  	}
    50  	fanConfig, err := modelConfig.FanConfig()
    51  	if err != nil {
    52  		return false, err
    53  	}
    54  	if len(fanConfig) != 0 {
    55  		logger.Debugf("Not trying to autoconfigure FAN - configured already")
    56  		return false, nil
    57  	}
    58  	subnets, err := netEnviron.SuperSubnets(context.CallContext(m.st))
    59  	if errors.IsNotSupported(err) || (err == nil && len(subnets) == 0) {
    60  		logger.Debugf("Not trying to autoconfigure FAN - SuperSubnets not supported or empty")
    61  		return false, nil
    62  	}
    63  	if err != nil {
    64  		return false, err
    65  	}
    66  	var outputTable []string
    67  
    68  	fanOverlays := []string{"252.0.0.0/8", "253.0.0.0/8", "254.0.0.0/8", "250.0.0.0/8", "251.0.0.0/8"}
    69  	fanOverlayForUnderlay := func(underlay string) string {
    70  		_, ipNet, err := net.ParseCIDR(underlay)
    71  		if err != nil {
    72  			return ""
    73  		}
    74  		// We don't create FAN networks for IPv6 networks
    75  		if ipNet.IP.To4() == nil {
    76  			return ""
    77  		}
    78  		if ones, _ := ipNet.Mask.Size(); ones <= 8 {
    79  			return ""
    80  		}
    81  		if len(fanOverlays) == 0 {
    82  			return ""
    83  		}
    84  		var overlay string
    85  		overlay, fanOverlays = fanOverlays[0], fanOverlays[1:]
    86  		return overlay
    87  	}
    88  	for _, subnet := range subnets {
    89  		overlay := fanOverlayForUnderlay(subnet)
    90  		if overlay != "" {
    91  			outputTable = append(outputTable, fmt.Sprintf("%s=%s", subnet, overlay))
    92  		}
    93  	}
    94  	if len(outputTable) > 0 {
    95  		updateAttrs["fan-config"] = strings.Join(outputTable, " ")
    96  		return true, nil
    97  	}
    98  	return false, nil
    99  }