talos/pkg/flannel/gen.go
Andrey Smirnov 02b0ff35ee
feat: generate Flannel CNI manifest from upstream
Fixes #6730

`go generate`-based step downloads the upstream manifest, transforms it
to match our requirements, and it is compiled in as the Flannel
manifest.

Signed-off-by: Andrey Smirnov <andrey.smirnov@talos-systems.com>
2023-03-13 20:00:35 +04:00

238 lines
5.5 KiB
Go

// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//go:build generate
package main
import (
"fmt"
"io"
"log"
"net/http"
"os"
"regexp"
"strings"
"github.com/siderolabs/gen/slices"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/kubernetes/scheme"
"sigs.k8s.io/yaml"
"github.com/siderolabs/talos/pkg/machinery/constants"
)
const sourceURL = "https://raw.githubusercontent.com/flannel-io/flannel/%s/Documentation/kube-flannel.yml"
const configMap = `apiVersion: v1
kind: ConfigMap
metadata:
name: kube-flannel-cfg
namespace: kube-system
labels:
tier: node
k8s-app: flannel
data:
cni-conf.json: |
{
"name": "cbr0",
"cniVersion": "1.0.0",
"plugins": [
{
"type": "flannel",
"delegate": {
"hairpinMode": true,
"isDefaultGateway": true
}
},
{
"type": "portmap",
"capabilities": {
"portMappings": true
}
}
]
}
net-conf.json: |
{
{{- $hasIPv4 := false }}
{{- range $cidr := .PodCIDRs }}
{{- if contains $cidr "." }}
{{- $hasIPv4 = true }}
"Network": "{{ $cidr }}",
{{- else }}
"IPv6Network": "{{ $cidr }}",
"EnableIPv6": true,
{{- end }}
{{- end }}
{{- if not $hasIPv4 }}
"EnableIPv4": false,
{{- end }}
"Backend": {
"Type": "vxlan",
"Port": 4789
}
}
---
`
func marshal(out io.Writer, obj runtime.Object) {
m, err := yaml.Marshal(obj)
if err != nil {
log.Fatal(err)
}
m = regexp.MustCompile(` +creationTimestamp: null\n`).ReplaceAll(m, nil)
m = regexp.MustCompile(`status:\n( .+\n)+`).ReplaceAll(m, nil)
fmt.Fprintf(out, "%s---\n", string(m))
}
func main() {
url := fmt.Sprintf(sourceURL, constants.FlannelVersion)
resp, err := http.Get(url)
if err != nil {
log.Fatal(err)
}
if resp.StatusCode != http.StatusOK {
log.Fatalf("unexpected status code: %d", resp.StatusCode)
}
manifest, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
out, err := os.Create("template.go")
if err != nil {
log.Fatal(err)
}
defer out.Close()
fmt.Fprintf(out, `// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Code generated from the manifest %s DO NOT EDIT
package flannel
// Template is a flannel manifest template.
var Template = []byte(`+"`", url)
decoder := scheme.Codecs.UniversalDeserializer()
for _, resourceYAML := range strings.Split(string(manifest), "---") {
if len(resourceYAML) == 0 {
continue
}
obj, groupVersionKind, err := decoder.Decode(
[]byte(resourceYAML),
nil,
nil)
if err != nil {
log.Fatal(err)
}
switch groupVersionKind.Kind {
case "Namespace":
continue
case "ClusterRole":
marshal(out, obj)
case "ClusterRoleBinding":
crb := obj.(*rbacv1.ClusterRoleBinding)
crb.Subjects[0].Namespace = "kube-system"
crb.CreationTimestamp = metav1.Time{}
marshal(out, obj)
case "ServiceAccount":
sa := obj.(*corev1.ServiceAccount)
sa.Namespace = "kube-system"
marshal(out, obj)
case "ConfigMap":
fmt.Fprint(out, configMap)
case "DaemonSet":
ds := obj.(*appsv1.DaemonSet)
ds.Namespace = "kube-system"
ds.Name = "kube-flannel"
ds.Status = appsv1.DaemonSetStatus{}
ds.Labels["k8s-app"] = "flannel"
delete(ds.Labels, "app")
ds.Spec.Template.Labels["k8s-app"] = "flannel"
delete(ds.Spec.Template.Labels, "app")
ds.Spec.Template.Spec.Tolerations = append(ds.Spec.Template.Spec.Tolerations,
corev1.Toleration{
Effect: "NoExecute",
Operator: "Exists",
})
ds.Spec.Selector.MatchLabels["k8s-app"] = "flannel"
ds.Spec.Selector.MatchLabels["tier"] = "node"
delete(ds.Spec.Selector.MatchLabels, "app")
ds.Spec.Template.Spec.Containers[0].Image = "{{ .FlannelImage }}"
ds.Spec.Template.Spec.Volumes = slices.FilterInPlace(ds.Spec.Template.Spec.Volumes, func(v corev1.Volume) bool {
return v.Name != "xtables-lock"
})
ds.Spec.Template.Spec.Containers[0].VolumeMounts = slices.FilterInPlace(
ds.Spec.Template.Spec.Containers[0].VolumeMounts, func(v corev1.VolumeMount) bool {
return v.Name != "xtables-lock"
})
ds.Spec.Template.Spec.InitContainers = []corev1.Container{
{
Name: "install-config",
Image: "{{ .FlannelImage }}",
Command: []string{"cp"},
Args: []string{
"-f",
"/etc/kube-flannel/cni-conf.json",
"/etc/cni/net.d/10-flannel.conflist",
},
VolumeMounts: []corev1.VolumeMount{
{
Name: "cni",
MountPath: "/etc/cni/net.d",
},
{
Name: "flannel-cfg",
MountPath: "/etc/kube-flannel/",
},
},
},
{
Name: "install-cni",
Image: "{{ .FlannelCNIImage }}",
Command: []string{"/install-cni.sh"},
VolumeMounts: []corev1.VolumeMount{
{
Name: "cni-plugin",
MountPath: "/host/opt/cni/bin/",
},
},
},
}
marshal(out, obj)
default:
log.Fatalf("unknown resource kind: %q", groupVersionKind.Kind)
}
}
fmt.Fprint(out, "`)\n")
}