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)
96
97
98 config.Network = types.Network{
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()
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)
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
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