174 lines
4.2 KiB
Go
174 lines
4.2 KiB
Go
package internal
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"html/template"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
"k8s.io/client-go/kubernetes/scheme"
|
|
"k8s.io/client-go/rest"
|
|
)
|
|
|
|
var CILIUM_GROUP_VERSION = schema.GroupVersion{
|
|
Group: "cilium.io",
|
|
Version: "v2alpha1",
|
|
}
|
|
|
|
var IP_POOL_TEMPLATE = `
|
|
{
|
|
"apiVersion": "cilium.io/v2alpha1",
|
|
"kind": "CiliumLoadBalancerIPPool",
|
|
"metadata": {
|
|
"name": "{{ .Name }}",
|
|
"annotations": {
|
|
"argocd.argoproj.io/tracking-id": "cilium-lb:cilium.io/CiliumLoadBalancerIPPool:kube-system/covidnetes-pool",
|
|
"managed-by": "canada-kaktus"
|
|
}
|
|
},
|
|
"spec": {
|
|
"blocks": [
|
|
{{- range $i, $ip := .IPs }}
|
|
{{- if $i}},{{ end }}
|
|
{
|
|
"cidr": "{{ $ip }}/32"
|
|
}
|
|
{{- end }}
|
|
],
|
|
"disabled": false
|
|
}
|
|
}
|
|
`
|
|
|
|
type CrdConfig struct {
|
|
Name string
|
|
IPs []string
|
|
}
|
|
|
|
func RecreateIPPoolCrd(cfg *Config, name string, ips []string) error {
|
|
|
|
if len(ips) == 0 {
|
|
return fmt.Errorf("no IPs provided to create IP Pool CRD")
|
|
}
|
|
|
|
routeclient, err := createRestClient()
|
|
if err != nil {
|
|
return fmt.Errorf("error creating REST Client: %v", err.Error())
|
|
}
|
|
|
|
resourceVersion, err := getResourceVersion(routeclient, name)
|
|
if err != nil {
|
|
return fmt.Errorf("error getting resourceVersion: %v", err.Error())
|
|
}
|
|
|
|
body, err := generateIpPool(name, ips)
|
|
if err != nil {
|
|
return fmt.Errorf("error generating CRD: %v", err.Error())
|
|
}
|
|
|
|
// Inject resourceVersion into the JSON
|
|
var obj map[string]interface{}
|
|
if err := json.Unmarshal([]byte(body), &obj); err != nil {
|
|
return fmt.Errorf("could not unmarshal generated CRD: %v", err)
|
|
}
|
|
if meta, ok := obj["metadata"].(map[string]interface{}); ok {
|
|
meta["resourceVersion"] = resourceVersion
|
|
}
|
|
finalBody, err := json.Marshal(obj)
|
|
if err != nil {
|
|
return fmt.Errorf("could not marshal final CRD: %v", err)
|
|
}
|
|
|
|
res := routeclient.Put().
|
|
Resource("ciliumloadbalancerippools").
|
|
Name(name).
|
|
Body(finalBody).
|
|
Do(context.TODO())
|
|
|
|
var status int
|
|
res.StatusCode(&status)
|
|
|
|
raw, rawErr := res.Raw()
|
|
|
|
if status < 200 || status >= 400 {
|
|
log.WithFields(log.Fields{
|
|
"Caller": "RecreateIPPoolCrd",
|
|
}).Warnf("Response from k8s api server: %s", string(raw))
|
|
return fmt.Errorf("failed to post CRD to kube api: %v", res.Error())
|
|
}
|
|
|
|
log.WithFields(log.Fields{
|
|
"Caller": "RecreateIPPoolCrd",
|
|
}).Debugf("Response from k8s api server: %s", string(raw))
|
|
|
|
if rawErr != nil {
|
|
log.WithFields(log.Fields{
|
|
"Caller": "RecreateIPPoolCrd",
|
|
}).Warnf("Could not get raw response from k8s api server: %v", rawErr)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
func createRestClient() (*rest.RESTClient, error) {
|
|
k8s_config, err := rest.InClusterConfig()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("could not create in cluster k8s config: %v", err)
|
|
}
|
|
|
|
k8s_config.APIPath = "/apis"
|
|
k8s_config.NegotiatedSerializer = scheme.Codecs.WithoutConversion()
|
|
k8s_config.GroupVersion = &CILIUM_GROUP_VERSION
|
|
|
|
routeclient, err := rest.RESTClientFor(k8s_config)
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf("could not create k8s client: %v", err)
|
|
}
|
|
return routeclient, nil
|
|
|
|
}
|
|
|
|
func generateIpPool(name string, ips []string) (string, error) {
|
|
config := CrdConfig{
|
|
Name: name,
|
|
IPs: ips,
|
|
}
|
|
tmpl, err := template.New("ippool").Parse(IP_POOL_TEMPLATE)
|
|
if err != nil {
|
|
return "", fmt.Errorf("errors in ippool template: %s", err.Error())
|
|
}
|
|
var buf bytes.Buffer
|
|
err = tmpl.Execute(&buf, &config)
|
|
if err != nil {
|
|
return "", fmt.Errorf("could not render ippool template: %s", err.Error())
|
|
}
|
|
return buf.String(), nil
|
|
}
|
|
|
|
func getResourceVersion(client *rest.RESTClient, name string) (string, error) {
|
|
res := client.Get().
|
|
Resource("ciliumloadbalancerippools").
|
|
Name(name).
|
|
Do(context.TODO())
|
|
raw, err := res.Raw()
|
|
if err != nil {
|
|
return "", fmt.Errorf("could not fetch CRD: %v", err)
|
|
}
|
|
var obj map[string]interface{}
|
|
if err := json.Unmarshal(raw, &obj); err != nil {
|
|
return "", fmt.Errorf("could not unmarshal CRD: %v", err)
|
|
}
|
|
meta, ok := obj["metadata"].(map[string]interface{})
|
|
if !ok {
|
|
return "", fmt.Errorf("metadata missing in CRD")
|
|
}
|
|
rv, ok := meta["resourceVersion"].(string)
|
|
if !ok {
|
|
return "", fmt.Errorf("resourceVersion missing in metadata")
|
|
}
|
|
return rv, nil
|
|
}
|