Skip to content

Commit

Permalink
Add Calico CNI (#469)
Browse files Browse the repository at this point in the history
  • Loading branch information
bschimke95 authored Jun 6, 2024
1 parent b7859b0 commit 15f64ea
Show file tree
Hide file tree
Showing 10 changed files with 256 additions and 27 deletions.
8 changes: 4 additions & 4 deletions build-scripts/patches/moonray/0001-Moonray.patch
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
From 1a16f5952ebda874a37556cfdca410a8e7e00ae6 Mon Sep 17 00:00:00 2001
From: Benjamin Schimke <benjamin.schimke@canonical.com>
Date: Wed, 5 Jun 2024 09:48:57 +0200
From 4f26e12832ef73b5c76a59cf3209d4f1f5b0aa3b Mon Sep 17 00:00:00 2001
From: k8s-bot <k8s-bot@canonical.com>
Date: Thu, 6 Jun 2024 10:34:52 +0200
Subject: [PATCH] Moonray

---
Expand All @@ -19,7 +19,7 @@ index aeb1729..184f2ce 100644

import (
diff --git a/src/k8s/pkg/k8sd/features/implementation_moonray.go b/src/k8s/pkg/k8sd/features/implementation_moonray.go
index f8abf25..cdf39fa 100644
index dece19d..5933221 100644
--- a/src/k8s/pkg/k8sd/features/implementation_moonray.go
+++ b/src/k8s/pkg/k8sd/features/implementation_moonray.go
@@ -1,5 +1,3 @@
Expand Down
Binary file added k8s/manifests/charts/tigera-operator-v3.28.0.tgz
Binary file not shown.
9 changes: 9 additions & 0 deletions src/k8s/pkg/client/kubernetes/pods.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,12 @@ func (c *Client) IsPodReady(ctx context.Context, name, namespace string, listOpt

return false, nil
}

// ListPods lists all pods in a namespace.
func (c *Client) ListPods(ctx context.Context, namespace string, listOptions metav1.ListOptions) ([]corev1.Pod, error) {
pods, err := c.CoreV1().Pods(namespace).List(ctx, listOptions)
if err != nil {
return nil, fmt.Errorf("failed to list pods: %w", err)
}
return pods.Items, nil
}
33 changes: 33 additions & 0 deletions src/k8s/pkg/k8sd/features/calico/chart.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package calico

import (
"path"

"github.com/canonical/k8s/pkg/client/helm"
)

var (
// chartCalico represents manifests to deploy Calico.
chartCalico = helm.InstallableChart{
Name: "ck-network",
Namespace: "tigera-operator",
ManifestPath: path.Join("charts", "tigera-operator-v3.28.0.tgz"),
}

// tigeraOperatorRepo represents the repo to fetch the tigera-operator image for calico.
// Note: Tigera is the company behind Calico and the tigera-operator is the operator for Calico.
// TODO: use ROCKs instead of upstream
tigeraOperatorRegistry = "quay.io"

// tigeraOperatorImage represents the image to fetch for calico.
tigeraOperatorImage = "tigera/operator"

// tigeraOperatorVersion is the version to use for the tigera-operator image.
tigeraOperatorVersion = "v1.34.0"

// calicoCtlImage represents the image to fetch for calicoctl.
// TODO: use ROCKs instead of upstream
calicoCtlImage = "docker.io/calico/ctl"
// calicoCtlTag represents the tag to use for the calicoctl image.
calicoCtlTag = "v3.28.0"
)
79 changes: 79 additions & 0 deletions src/k8s/pkg/k8sd/features/calico/network.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package calico

import (
"context"
"fmt"

"github.com/canonical/k8s/pkg/client/helm"
"github.com/canonical/k8s/pkg/k8sd/types"
"github.com/canonical/k8s/pkg/snap"
"github.com/canonical/k8s/pkg/utils"
)

// ApplyNetwork will deploy Calico when cfg.Enabled is true.
// ApplyNetwork will remove Calico when cfg.Enabled is false.
// ApplyNetwork returns an error if anything fails.
func ApplyNetwork(ctx context.Context, snap snap.Snap, cfg types.Network, _ types.Annotations) error {
m := snap.HelmClient()

if !cfg.GetEnabled() {
if _, err := m.Apply(ctx, chartCalico, helm.StateDeleted, nil); err != nil {
return fmt.Errorf("failed to uninstall network: %w", err)
}
return nil
}

podIpPools := []map[string]any{}
ipv4PodCIDR, ipv6PodCIDR, err := utils.ParseCIDRs(cfg.GetPodCIDR())
if err != nil {
return fmt.Errorf("invalid pod cidr: %v", err)
}
if ipv4PodCIDR != "" {
podIpPools = append(podIpPools, map[string]any{
"name": "ipv4-ippool",
"cidr": ipv4PodCIDR,
})
}
if ipv6PodCIDR != "" {
podIpPools = append(podIpPools, map[string]any{
"name": "ipv6-ippool",
"cidr": ipv6PodCIDR,
})
}

serviceCIDRs := []string{}
ipv4ServiceCIDR, ipv6ServiceCIDR, err := utils.ParseCIDRs(cfg.GetPodCIDR())
if err != nil {
return fmt.Errorf("invalid service cidr: %v", err)
}
if ipv4ServiceCIDR != "" {
serviceCIDRs = append(serviceCIDRs, ipv4ServiceCIDR)
}
if ipv6ServiceCIDR != "" {
serviceCIDRs = append(serviceCIDRs, ipv6ServiceCIDR)
}

values := map[string]any{
"tigeraOperator": map[string]any{
"registry": tigeraOperatorRegistry,
"image": tigeraOperatorImage,
"version": tigeraOperatorVersion,
},
"calicoctl": map[string]any{
"image": calicoCtlImage,
"tag": calicoCtlTag,
},
"installation": map[string]any{
"calicoNetwork": map[string]any{
"ipPools": podIpPools,
},
},
"serviceCIDRs": serviceCIDRs,
}

if _, err := m.Apply(ctx, chartCalico, helm.StatePresent, values); err != nil {
return fmt.Errorf("failed to enable network: %w", err)
}

return nil
}
49 changes: 49 additions & 0 deletions src/k8s/pkg/k8sd/features/calico/status.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package calico

import (
"context"
"fmt"

"github.com/canonical/k8s/pkg/snap"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// CheckNetwork checks the status of the Calico pods in the Kubernetes cluster.
// It verifies if all the Calico pods in the "tigera-operator" namespace are ready.
// If any pod is not ready, it returns false. Otherwise, it returns true.
func CheckNetwork(ctx context.Context, snap snap.Snap) (bool, error) {
client, err := snap.KubernetesClient("calico-system")
if err != nil {
return false, fmt.Errorf("failed to create kubernetes client: %w", err)
}

operatorReady, err := client.IsPodReady(ctx, "kube-system", "tigera-operator", metav1.ListOptions{})
if err != nil {
return false, fmt.Errorf("failed to get calico pods: %w", err)
}
if !operatorReady {
return false, nil
}

calicoPods, err := client.ListPods(ctx, "calico-system", metav1.ListOptions{})
if err != nil {
return false, fmt.Errorf("failed to get calico pods: %w", err)
}
calicoApiserverPods, err := client.ListPods(ctx, "calico-apiserver", metav1.ListOptions{})
if err != nil {
return false, fmt.Errorf("failed to get calico-apiserver pods: %w", err)
}

for _, pod := range append(calicoPods, calicoApiserverPods...) {
isReady, err := client.IsPodReady(ctx, pod.Name, "calico-system", metav1.ListOptions{})
if err != nil {
return false, fmt.Errorf("failed to check if pod %q is ready: %w", pod.Name, err)
}
if !isReady {
return false, nil
}
}

return true, nil
}
24 changes: 3 additions & 21 deletions src/k8s/pkg/k8sd/features/cilium/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import (
"context"
"fmt"
"log"
"net"
"strings"

"github.com/canonical/k8s/pkg/client/helm"
"github.com/canonical/k8s/pkg/k8sd/types"
Expand All @@ -29,25 +27,9 @@ func ApplyNetwork(ctx context.Context, snap snap.Snap, cfg types.Network, _ type
return nil
}

clusterCIDRs := strings.Split(cfg.GetPodCIDR(), ",")
if v := len(clusterCIDRs); v != 1 && v != 2 {
return fmt.Errorf("invalid kube-proxy --cluster-cidr value: %v", clusterCIDRs)
}

var (
ipv4CIDR string
ipv6CIDR string
)
for _, cidr := range clusterCIDRs {
_, parsed, err := net.ParseCIDR(cidr)
switch {
case err != nil:
return fmt.Errorf("failed to parse cidr: %w", err)
case parsed.IP.To4() != nil:
ipv4CIDR = cidr
default:
ipv6CIDR = cidr
}
ipv4CIDR, ipv6CIDR, err := utils.ParseCIDRs(cfg.GetPodCIDR())
if err != nil {
return fmt.Errorf("invalid kube-proxy --cluster-cidr value: %v", err)
}

values := map[string]any{
Expand Down
5 changes: 3 additions & 2 deletions src/k8s/pkg/k8sd/features/implementation_moonray.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
package features

import (
"github.com/canonical/k8s/pkg/k8sd/features/calico"
"github.com/canonical/k8s/pkg/k8sd/features/cilium"
"github.com/canonical/k8s/pkg/k8sd/features/coredns"
"github.com/canonical/k8s/pkg/k8sd/features/localpv"
Expand All @@ -13,7 +14,7 @@ import (
// TODO: Replace default by moonray.
var Implementation Interface = &implementation{
applyDNS: coredns.ApplyDNS,
applyNetwork: cilium.ApplyNetwork,
applyNetwork: calico.ApplyNetwork,
applyLoadBalancer: cilium.ApplyLoadBalancer,
applyIngress: cilium.ApplyIngress,
applyGateway: cilium.ApplyGateway,
Expand All @@ -24,6 +25,6 @@ var Implementation Interface = &implementation{
// StatusChecks implements the Canonical Kubernetes moonray feature status checks.
// TODO: Replace default by moonray.
var StatusChecks StatusInterface = &statusChecks{
checkNetwork: cilium.CheckNetwork,
checkNetwork: calico.CheckNetwork,
checkDNS: coredns.CheckDNS,
}
24 changes: 24 additions & 0 deletions src/k8s/pkg/utils/cidr.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,5 +99,29 @@ func ParseAddressString(address string, port int64) (string, error) {
}

return util.CanonicalNetworkAddress(address, port), nil
}

// ParseCIDRs parses the given CIDR string and returns the respective IPv4 and IPv6 CIDRs.
func ParseCIDRs(CIDRstring string) (string, string, error) {
clusterCIDRs := strings.Split(CIDRstring, ",")
if v := len(clusterCIDRs); v != 1 && v != 2 {
return "", "", fmt.Errorf("invalid CIDR list: %v", clusterCIDRs)
}

var (
ipv4CIDR string
ipv6CIDR string
)
for _, cidr := range clusterCIDRs {
_, parsed, err := net.ParseCIDR(cidr)
switch {
case err != nil:
return "", "", fmt.Errorf("failed to parse cidr: %w", err)
case parsed.IP.To4() != nil:
ipv4CIDR = cidr
default:
ipv6CIDR = cidr
}
}
return ipv4CIDR, ipv6CIDR, nil
}
52 changes: 52 additions & 0 deletions src/k8s/pkg/utils/cidr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,3 +112,55 @@ func TestParseAddressString(t *testing.T) {
})
}
}

func TestParseCIDRs(t *testing.T) {
RegisterTestingT(t)

testCases := []struct {
input string
expectedIPv4 string
expectedIPv6 string
expectedErr bool
}{
{
input: "192.168.1.0/24",
expectedIPv4: "192.168.1.0/24",
expectedIPv6: "",
},
{
input: "2001:db8::/32",
expectedIPv4: "",
expectedIPv6: "2001:db8::/32",
},
{
input: "192.168.1.0/24,2001:db8::/32",
expectedIPv4: "192.168.1.0/24",
expectedIPv6: "2001:db8::/32",
},
{
input: "192.168.1.0/24,invalidCIDR",
expectedIPv4: "",
expectedIPv6: "",
expectedErr: true,
},
{
input: "192.168.1.0/24,2001:db8::/32,10.0.0.0/8",
expectedIPv4: "",
expectedIPv6: "",
expectedErr: true,
},
}

for _, tc := range testCases {
t.Run(tc.input, func(t *testing.T) {
ipv4CIDR, ipv6CIDR, err := utils.ParseCIDRs(tc.input)
if tc.expectedErr {
Expect(err).To(HaveOccurred())
} else {
Expect(err).To(BeNil())
Expect(ipv4CIDR).To(Equal(tc.expectedIPv4))
Expect(ipv6CIDR).To(Equal(tc.expectedIPv6))
}
})
}
}

0 comments on commit 15f64ea

Please sign in to comment.