...

Source file src/edge-infra.dev/pkg/edge/api/graph/mapper/mapper_terminal_bootstrap.go

Documentation: edge-infra.dev/pkg/edge/api/graph/mapper

     1  package mapper
     2  
     3  import (
     4  	"fmt"
     5  	"net"
     6  	"strings"
     7  
     8  	goVersion "github.com/hashicorp/go-version"
     9  	corev1 "k8s.io/api/core/v1"
    10  
    11  	"edge-infra.dev/pkg/edge/api/graph/model"
    12  	"edge-infra.dev/pkg/edge/api/types"
    13  	"edge-infra.dev/pkg/edge/component/build"
    14  	"edge-infra.dev/pkg/edge/constants"
    15  	"edge-infra.dev/pkg/sds/ien/k8s/controllers/nodeagent/plugins/networking/netplan"
    16  )
    17  
    18  var (
    19  	kubeadmAPIVersion        = "kubeadm.k8s.io/v1beta3"
    20  	controlPlaneEndpointPort = "6443"
    21  	caHashFilePath           = "/etc/kubernetes/pki/ca.crt"
    22  )
    23  
    24  var (
    25  	apiEnabled     = true
    26  	gatewayEnabled = false
    27  )
    28  
    29  var (
    30  	autoCompactionMode      = "periodic"
    31  	autoCompactionRetention = "5m"
    32  	listenMetricsURLs       = "http://0.0.0.0:2381"
    33  )
    34  
    35  type TerminalBootstrapPayload struct {
    36  	Terminal               *model.Terminal
    37  	EdgeInfraVersion       string
    38  	ClusterNetworkServices []*model.ClusterNetworkServiceInfo
    39  	ShadowFileLine         string
    40  	GrubFileEntry          string
    41  	FirstNode              bool
    42  	BootstrapTokenValues   []*model.KeyValues
    43  	ClusterCaHash          string
    44  	BootstrapAck           bool
    45  	Organization           string
    46  	EdgeBootstrapToken     string
    47  	Endpoint               string
    48  }
    49  
    50  func (p *TerminalBootstrapPayload) Yaml() (*types.TerminalBootstrapConfig, error) {
    51  	ienNode := TerminalToIENode(
    52  		p.Terminal,
    53  		p.ClusterNetworkServices,
    54  		make(map[string]string, 0),
    55  		p.EdgeInfraVersion,
    56  	)
    57  
    58  	tunnelSubnetCIDR, err := p.getTunnelSubnetCIDR()
    59  	if err != nil {
    60  		return &types.TerminalBootstrapConfig{}, err
    61  	}
    62  
    63  	netplan, err := netplan.GenerateTargetNetplanConfig(
    64  		&ienNode,
    65  		[]corev1.Node{},
    66  		apiEnabled,
    67  		gatewayEnabled,
    68  		tunnelSubnetCIDR,
    69  	)
    70  	if err != nil {
    71  		return &types.TerminalBootstrapConfig{}, err
    72  	}
    73  
    74  	config := types.TerminalBootstrapConfig{}
    75  	config.FirstNode = p.FirstNode
    76  	config.GrubUser = constants.GrubRecoveryUser
    77  	config.Hostname = ienNode.ObjectMeta.Name
    78  	config.VipAddress = ienNode.Spec.KubeVip
    79  	config.NtpServers = ienNode.Spec.NTPServers
    80  	config.Role = p.Terminal.Role
    81  	config.Netplan = netplan
    82  	config.Registry = build.DefaultPublicContainerRegistry
    83  	config.EdgeRegistries = types.EdgeRegistries{
    84  		Public:  []string{build.DefaultPublicContainerRegistry},
    85  		Private: []string{build.EdgeWorkloadsRegistry, build.WorkloadsDirectory, build.WarehouseRegistry},
    86  	}
    87  	config.GrubHash = p.GrubFileEntry
    88  	config.BreakGlassHash = p.ShadowFileLine
    89  	config.BootstrapAck = p.BootstrapAck
    90  	config.Swap.Enabled = p.Terminal.SwapEnabled
    91  	podSubnet := p.getClusterNetworkServiceIP(constants.ServiceTypePodNetworkCIDR)
    92  	serviceSubnet := p.getClusterNetworkServiceIP(constants.ServiceTypeServiceNetworkCIDR)
    93  	config.PodSubnet = podSubnet
    94  	config.ServiceSubnet = serviceSubnet
    95  	config.KubeadmConfig.KubeadmClusterConfig = *p.generateKubeadmClusterConfig(&config) //nolint:staticcheck // Allow existing usage of deprecated field
    96  
    97  	// Deprecated: in 0.17: network fields will be returned as part of cluster config
    98  	config.Network = types.Network{ //nolint:staticcheck // Allow existing usage of deprecated type
    99  		ServiceSubnetCIDR: serviceSubnet,
   100  		PodSubnetCIDR:     podSubnet,
   101  	}
   102  
   103  	if config.FirstNode {
   104  		config.NcrEdge = types.NcrEdge{
   105  			EdgeInfraVersion: p.EdgeInfraVersion,
   106  			NcrEdgeParams: types.NcrEdgeParams{
   107  				ClusterEdgeID: p.Terminal.ClusterEdgeID,
   108  				Organization:  p.Organization,
   109  				UploadCaHash:  caHashFilePath,
   110  				APIEndpoint:   p.Endpoint,
   111  				Token:         p.EdgeBootstrapToken,
   112  			},
   113  			BootstrapPayload: types.BootstrapPayload{
   114  				ClusterEdgeID: p.Terminal.ClusterEdgeID,
   115  			},
   116  		}
   117  		config.KubeadmConfig.KubeadmInitConfig = p.generateKubeadmInitConfig() //nolint:staticcheck // Allow existing usage of deprecated field
   118  	} else {
   119  		config.BootstrapInfo = types.BootstrapInfo{
   120  			ClusterCaHash: p.ClusterCaHash,
   121  			JoinToken:     p.BootstrapTokenValues[0].Value + "." + p.BootstrapTokenValues[1].Value,
   122  		}
   123  		config.KubeadmConfig.KubeadmJoinConfig = p.generateKubeadmJoinConfig(&config) //nolint:staticcheck // Allow existing usage of deprecated field
   124  	}
   125  
   126  	if p.Terminal.Disks != nil {
   127  		disks := []types.Disks{}
   128  		for _, disk := range p.Terminal.Disks {
   129  			disks = append(disks, types.Disks{
   130  				DevicePath:  disk.DevicePath,
   131  				ExpectEmpty: disk.ExpectEmpty,
   132  				IncludeDisk: disk.IncludeDisk,
   133  				UsePart:     disk.UsePart,
   134  			})
   135  		}
   136  
   137  		if p.Terminal.DiscoverDisks == nil {
   138  			return &types.TerminalBootstrapConfig{}, fmt.Errorf(
   139  				"unable to get terminal bootstrap as discover disks not set",
   140  			)
   141  		}
   142  
   143  		if p.Terminal.BootDisk != nil && *p.Terminal.BootDisk == "" {
   144  			p.Terminal.BootDisk = nil
   145  		}
   146  		config.DiskOptions = types.DiskOptions{
   147  			PersistentVolume: types.PersistentVolume{
   148  				Disks:           disks,
   149  				DiscoverDisks:   *p.Terminal.DiscoverDisks,
   150  				BootDisk:        p.Terminal.BootDisk,
   151  				ExistingEfiPart: p.Terminal.ExistingEfiPart,
   152  			},
   153  		}
   154  	}
   155  
   156  	minimumVersion, _ := goVersion.NewVersion("v0.17.0")
   157  	currentVersion, _ := goVersion.NewVersion(p.EdgeInfraVersion)
   158  	if currentVersion.LessThan(minimumVersion) {
   159  		return &config, nil
   160  	}
   161  	if p.Terminal.PrimaryInterface == nil {
   162  		return &types.TerminalBootstrapConfig{}, fmt.Errorf("unable to get terminal bootstrap as primary interface not set")
   163  	}
   164  	iface, err := p.getPrimaryTerminalInterface()
   165  	if err != nil {
   166  		return &types.TerminalBootstrapConfig{}, err
   167  	}
   168  	config.PrimaryInterface = types.PrimaryInterface{
   169  		ID:   *p.Terminal.PrimaryInterface,
   170  		Macs: []string{iface.MacAddress},
   171  	}
   172  	return &config, nil
   173  }
   174  
   175  // Deprecated: DEPRECATED in 0.19
   176  func (p *TerminalBootstrapPayload) generateKubeadmClusterConfig(
   177  	config *types.TerminalBootstrapConfig,
   178  ) *types.KubeadmClusterConfig {
   179  	return &types.KubeadmClusterConfig{
   180  		APIVersion:           kubeadmAPIVersion,
   181  		ImageRepository:      "IMAGE_REPOSITORY",
   182  		Kind:                 "ClusterConfiguration",
   183  		KubernetesVersion:    "KUBE_VERSION",
   184  		ControlPlaneEndpoint: config.VipAddress + ":" + controlPlaneEndpointPort,
   185  		Networking: types.KubeadmNetworkingConfig{
   186  			ServiceSubnet: p.getClusterNetworkServiceIP(constants.ServiceTypeServiceNetworkCIDR),
   187  			PodSubnet:     p.getClusterNetworkServiceIP(constants.ServiceTypePodNetworkCIDR),
   188  		},
   189  		Etcd: p.getClusterEtcdConfig(),
   190  	}
   191  }
   192  
   193  func (p *TerminalBootstrapPayload) getClusterEtcdConfig() types.KubeadmEtcdConfig {
   194  	return types.KubeadmEtcdConfig{
   195  		Local: types.KubeadmEtcdLocal{
   196  			ExtraArgs: types.KubeadmEtcdExtraArgs{
   197  				AutoCompactionMode:      autoCompactionMode,
   198  				AutoCompactionRetention: autoCompactionRetention,
   199  				ListenMetricsURLs:       listenMetricsURLs,
   200  			},
   201  		},
   202  	}
   203  }
   204  
   205  func (p *TerminalBootstrapPayload) generateKubeadmInitConfig() *types.KubeadmInitConfig {
   206  	return &types.KubeadmInitConfig{
   207  		APIVersion: kubeadmAPIVersion,
   208  		Kind:       "InitConfiguration",
   209  		NodeRegistration: types.KubeadmNodeRegistration{
   210  			Taints: []string{},
   211  		},
   212  	}
   213  }
   214  
   215  func (p *TerminalBootstrapPayload) generateKubeadmJoinConfig(
   216  	config *types.TerminalBootstrapConfig,
   217  ) *types.KubeadmJoinConfig {
   218  	clusterCaHash := p.ClusterCaHash
   219  	if !strings.HasPrefix(p.ClusterCaHash, "sha256:") {
   220  		clusterCaHash = "sha256:" + p.ClusterCaHash
   221  	}
   222  	return &types.KubeadmJoinConfig{
   223  		APIVersion: kubeadmAPIVersion,
   224  		Kind:       "JoinConfiguration",
   225  		Discovery: types.KubeadmDiscovery{
   226  			BootstrapToken: types.KubeadmBootstrapToken{
   227  				Token:             p.BootstrapTokenValues[0].Value + "." + p.BootstrapTokenValues[1].Value,
   228  				APIServerEndpoint: config.VipAddress + ":" + controlPlaneEndpointPort,
   229  				CaCertHashes:      []string{clusterCaHash},
   230  			},
   231  		},
   232  		NodeRegistration: types.KubeadmNodeRegistration{
   233  			Taints: []string{},
   234  		},
   235  	}
   236  }
   237  
   238  func (p *TerminalBootstrapPayload) getTunnelSubnetCIDR() (*net.IPNet, error) {
   239  	tunnelSubnetCIDR := p.getClusterNetworkServiceIP(constants.ServiceTypeEgressTunnelsCIDR)
   240  	_, parsedTunnelCIDR, err := net.ParseCIDR(tunnelSubnetCIDR)
   241  
   242  	if err != nil {
   243  		return nil, err
   244  	}
   245  	return parsedTunnelCIDR, nil
   246  }
   247  
   248  func (p *TerminalBootstrapPayload) getClusterNetworkServiceIP(serviceType string) string {
   249  	for _, service := range p.ClusterNetworkServices {
   250  		if service.ServiceType == serviceType {
   251  			return service.IP
   252  		}
   253  	}
   254  	return constants.ClusterNetworkServiceDefaults[serviceType]
   255  }
   256  
   257  func (p *TerminalBootstrapPayload) getPrimaryTerminalInterface() (*model.TerminalInterface, error) {
   258  	for _, iface := range p.Terminal.Interfaces {
   259  		if iface.TerminalInterfaceID == *p.Terminal.PrimaryInterface {
   260  			return iface, nil
   261  		}
   262  	}
   263  	return nil, fmt.Errorf(
   264  		"no interface with configured PrimaryInterface ID %s",
   265  		*p.Terminal.PrimaryInterface,
   266  	)
   267  }
   268  

View as plain text