Skip to content

Commit

Permalink
cherry pick 2.6.0
Browse files Browse the repository at this point in the history
  • Loading branch information
winston0410 committed Jul 18, 2024
1 parent 6e4778c commit e328510
Show file tree
Hide file tree
Showing 4 changed files with 198 additions and 30 deletions.
4 changes: 2 additions & 2 deletions config/crd/bases/vpn.wireguard-operator.io_wireguards.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -196,8 +196,8 @@ spec:
type: object
serviceType:
description: A field that specifies the type of Kubernetes service
that should be used for the Wireguard VPN. This could be NodePort
or LoadBalancer, depending on the needs of the deployment.
that should be used for the Wireguard VPN. This could be ClusterIP,
NodePort or LoadBalancer, depending on the needs of the deployment.
type: string
useWgUserspaceImplementation:
description: A boolean field that specifies whether to use the userspace
Expand Down
2 changes: 1 addition & 1 deletion pkg/api/v1alpha1/wireguard_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ type WireguardSpec struct {
Address string `json:"address,omitempty"`
// A string field that specifies the DNS server(s) to be used by the peers.
Dns string `json:"dns,omitempty"`
// A field that specifies the type of Kubernetes service that should be used for the Wireguard VPN. This could be NodePort or LoadBalancer, depending on the needs of the deployment.
// A field that specifies the type of Kubernetes service that should be used for the Wireguard VPN. This could be ClusterIP, NodePort or LoadBalancer, depending on the needs of the deployment.
ServiceType corev1.ServiceType `json:"serviceType,omitempty"`
// A field that specifies the value to use for a nodePort ServiceType
NodePort int32 `json:"port,omitempty"`
Expand Down
11 changes: 11 additions & 0 deletions pkg/controllers/wireguard_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,17 @@ func (r *WireguardReconciler) Reconcile(ctx context.Context, req ctrl.Request) (

}

if serviceType == corev1.ServiceTypeClusterIP {
if len(svcFound.Spec.Ports) == 0 {
err = r.updateStatus(ctx, req, wireguard, v1alpha1.WgStatusReport{Status: v1alpha1.Pending, Message: "Waiting for service with type ClusterIP to be ready"})
if err != nil {
return ctrl.Result{}, err
}

return ctrl.Result{}, nil
}
}

if wireguard.Status.Address != address || port != wireguard.Status.Port || dnsAddress != wireguard.Status.Dns {
updateWireguard := wireguard.DeepCopy()
updateWireguard.Status.Address = address
Expand Down
211 changes: 184 additions & 27 deletions pkg/controllers/wireguard_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ import (
"time"

"github.com/jodevsa/wireguard-operator/pkg/api/v1alpha1"
"sigs.k8s.io/controller-runtime/pkg/client"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/intstr"
"sigs.k8s.io/controller-runtime/pkg/client"
)

// test helpers
Expand Down Expand Up @@ -43,18 +43,20 @@ func createNode(address string) error {
func reconcileServiceWithTypeNodePort(svcKey client.ObjectKey, nodePort string, port int32) error {
// update NodePort service port
svc := &corev1.Service{}
k8sClient.Get(context.Background(), svcKey, svc)
Expect(k8sClient.Get(context.Background(), svcKey, svc)).Should(Succeed())
if svc.Spec.Type != corev1.ServiceTypeNodePort {
return fmt.Errorf("ReconcileServiceWithTypeNodePort only reconsiles NodePort services")
}

nodePortInteger, _ := strconv.ParseInt(nodePort, 10, 32)
nodePortInteger, err := strconv.ParseInt(nodePort, 10, 32)
Expect(err).ToNot(HaveOccurred())

svc.Spec.Ports = []corev1.ServicePort{{NodePort: int32(nodePortInteger), Port: port}}
return k8sClient.Update(context.Background(), svc)
}
func reconcileServiceWithTypeLoadBalancer(svcKey client.ObjectKey, hostname string) error {
svc := &corev1.Service{}
k8sClient.Get(context.Background(), svcKey, svc)
Expect(k8sClient.Get(context.Background(), svcKey, svc)).Should(Succeed())
if svc.Spec.Type != corev1.ServiceTypeLoadBalancer {
return fmt.Errorf("ReconcileServiceWithTypeLoadBalancer only reconsiles LoadBalancer services")
}
Expand All @@ -63,6 +65,21 @@ func reconcileServiceWithTypeLoadBalancer(svcKey client.ObjectKey, hostname stri
return k8sClient.Status().Update(context.Background(), svc)
}

func reconcileServiceWithClusterIP(svcKey client.ObjectKey, port int32) error {
svc := &corev1.Service{}
Expect(k8sClient.Get(context.Background(), svcKey, svc)).Should(Succeed())

if svc.Spec.Type != corev1.ServiceTypeClusterIP {
return fmt.Errorf("ReconcileServiceWithClusterIP only reconsiles ClusterIP services")
}

svc.Spec.Ports = []corev1.ServicePort{{
Port: port,
TargetPort: intstr.FromInt32(port),
}}
return k8sClient.Status().Update(context.Background(), svc)
}

var _ = Describe("wireguard controller", func() {

// Define utility constants for object names and testing timeouts/durations and intervals.
Expand All @@ -84,43 +101,43 @@ var _ = Describe("wireguard controller", func() {

// delete all wg resources
wgList := &v1alpha1.WireguardList{}
k8sClient.List(context.Background(), wgList, listOpts...)
Expect(k8sClient.List(context.Background(), wgList, listOpts...)).Should(Succeed())
for _, wg := range wgList.Items {
k8sClient.Delete(context.Background(), &wg)
Expect(k8sClient.Delete(context.Background(), &wg)).Should(Succeed())
}
// delete all wg-peer resources
peerList := &v1alpha1.WireguardPeerList{}
k8sClient.List(context.Background(), peerList, listOpts...)
Expect(k8sClient.List(context.Background(), peerList, listOpts...)).Should(Succeed())
for _, peer := range peerList.Items {
k8sClient.Delete(context.Background(), &peer)
Expect(k8sClient.Delete(context.Background(), &peer)).Should(Succeed())
}

// delete all wg-peer services
svcList := &corev1.ServiceList{}
k8sClient.List(context.Background(), svcList, listOpts...)
Expect(k8sClient.List(context.Background(), svcList, listOpts...)).Should(Succeed())
for _, svc := range svcList.Items {
k8sClient.Delete(context.Background(), &svc)
Expect(k8sClient.Delete(context.Background(), &svc)).Should(Succeed())
}

// delete all nodes
nodeList := &corev1.NodeList{}
k8sClient.List(context.Background(), nodeList, listOpts...)
Expect(k8sClient.List(context.Background(), nodeList, listOpts...)).Should(Succeed())
for _, node := range nodeList.Items {
k8sClient.Delete(context.Background(), &node)
Expect(k8sClient.Delete(context.Background(), &node)).Should(Succeed())
}

// delete all secrets
secretList := &corev1.SecretList{}
k8sClient.List(context.Background(), secretList, listOpts...)
Expect(k8sClient.List(context.Background(), secretList, listOpts...)).Should(Succeed())
for _, secret := range secretList.Items {
k8sClient.Delete(context.Background(), &secret)
Expect(k8sClient.Delete(context.Background(), &secret)).Should(Succeed())
}

// delete all configmaps
cList := &corev1.ConfigMapList{}
k8sClient.List(context.Background(), cList, listOpts...)
Expect(k8sClient.List(context.Background(), cList, listOpts...)).Should(Succeed())
for _, c := range cList.Items {
k8sClient.Delete(context.Background(), &c)
Expect(k8sClient.Delete(context.Background(), &c)).Should(Succeed())
}

// create kube-dns service
Expand Down Expand Up @@ -180,15 +197,15 @@ var _ = Describe("wireguard controller", func() {
// match labels
Eventually(func() map[string]string {
svc := &corev1.Service{}
k8sClient.Get(context.Background(), serviceKey, svc)
Expect(k8sClient.Get(context.Background(), serviceKey, svc)).Should(Succeed())
return svc.Spec.Selector
}, Timeout, Interval).Should(BeEquivalentTo(expectedLabels))

Expect(reconcileServiceWithTypeNodePort(serviceKey, expectedPort, 51820)).Should(Succeed())

Eventually(func() string {
wgPeer := &v1alpha1.WireguardPeer{}
k8sClient.Get(context.Background(), wgPeerKey, wgPeer)
Expect(k8sClient.Get(context.Background(), wgPeerKey, wgPeer)).Should(Succeed())
for _, line := range strings.Split(wgPeer.Status.Config, "\n") {
if strings.Contains(line, "Endpoint") {
return line
Expand Down Expand Up @@ -239,15 +256,15 @@ var _ = Describe("wireguard controller", func() {
// match labels
Eventually(func() map[string]string {
svc := &corev1.Service{}
k8sClient.Get(context.Background(), serviceKey, svc)
Expect(k8sClient.Get(context.Background(), serviceKey, svc)).Should(Succeed())
return svc.Spec.Selector
}, Timeout, Interval).Should(BeEquivalentTo(expectedLabels))

Expect(reconcileServiceWithTypeLoadBalancer(serviceKey, "test-address")).Should(Succeed())

Eventually(func() string {
wgPeer := &v1alpha1.WireguardPeer{}
k8sClient.Get(context.Background(), wgPeerKey, wgPeer)
Expect(k8sClient.Get(context.Background(), wgPeerKey, wgPeer)).Should(Succeed())
for _, line := range strings.Split(wgPeer.Status.Config, "\n") {
if strings.Contains(line, "DNS") {
return line
Expand Down Expand Up @@ -289,14 +306,14 @@ var _ = Describe("wireguard controller", func() {
// match labels
Eventually(func() map[string]string {
svc := &corev1.Service{}
k8sClient.Get(context.Background(), serviceKey, svc)
Expect(k8sClient.Get(context.Background(), serviceKey, svc)).Should(Succeed())
return svc.Spec.Selector
}, Timeout, Interval).Should(BeEquivalentTo(expectedLabels))

// match service type
Eventually(func() corev1.ServiceType {
svc := &corev1.Service{}
k8sClient.Get(context.Background(), serviceKey, svc)
Expect(k8sClient.Get(context.Background(), serviceKey, svc)).Should(Succeed())
return svc.Spec.Type
}, Timeout, Interval).Should(Equal(corev1.ServiceTypeNodePort))

Expand Down Expand Up @@ -406,20 +423,20 @@ Endpoint = %s:%s"`, peerKey.Name, peer.Spec.AllowedIPs, peer.Spec.Address, dnsSe
// match labels
Eventually(func() map[string]string {
svc := &corev1.Service{}
k8sClient.Get(context.Background(), serviceKey, svc)
Expect(k8sClient.Get(context.Background(), serviceKey, svc)).Should(Succeed())
return svc.Spec.Selector
}, Timeout, Interval).Should(BeEquivalentTo(expectedLabels))

// match service type
Eventually(func() corev1.ServiceType {
svc := &corev1.Service{}
k8sClient.Get(context.Background(), serviceKey, svc)
Expect(k8sClient.Get(context.Background(), serviceKey, svc)).Should(Succeed())
return svc.Spec.Type
}, Timeout, Interval).Should(Equal(corev1.ServiceTypeLoadBalancer))

Eventually(func() v1alpha1.WireguardStatus {
wg := &v1alpha1.Wireguard{}
k8sClient.Get(context.Background(), wgKey, wg)
Expect(k8sClient.Get(context.Background(), wgKey, wg)).Should(Succeed())
return wg.Status
}, Timeout, Interval).Should(Equal(v1alpha1.WireguardStatus{
Address: "",
Expand Down Expand Up @@ -513,6 +530,146 @@ Endpoint = %s:%s"`, peerKey.Name, peer.Spec.AllowedIPs, peer.Spec.Address, dnsSe

})

It("Should create a WG with ServiceType ClusterIP and WG peer successfully", func() {
expectedAddress := "test-address"

wgKey := types.NamespacedName{
Name: wgName,
Namespace: wgNamespace,
}
created := &v1alpha1.Wireguard{
ObjectMeta: metav1.ObjectMeta{
Name: wgKey.Name,
Namespace: wgKey.Namespace,
},
Spec: v1alpha1.WireguardSpec{
ServiceType: corev1.ServiceTypeClusterIP,
Address: expectedAddress,
},
}
expectedLabels := map[string]string{"app": "wireguard", "instance": wgKey.Name}

Expect(k8sClient.Create(context.Background(), created)).Should(Succeed())

// service created
serviceName := wgKey.Name + "-svc"
serviceKey := types.NamespacedName{
Namespace: wgKey.Namespace,
Name: serviceName,
}

// match labels
Eventually(func() map[string]string {
svc := &corev1.Service{}
//nolint:errcheck
k8sClient.Get(context.Background(), serviceKey, svc)
return svc.Spec.Selector
}, Timeout, Interval).Should(BeEquivalentTo(expectedLabels))

// match service type
Eventually(func() corev1.ServiceType {
svc := &corev1.Service{}
//nolint:errcheck
k8sClient.Get(context.Background(), serviceKey, svc)
return svc.Spec.Type
}, Timeout, Interval).Should(Equal(corev1.ServiceTypeClusterIP))

Eventually(func() v1alpha1.WireguardStatus {
wg := &v1alpha1.Wireguard{}
//nolint:errcheck
k8sClient.Get(context.Background(), wgKey, wg)
return wg.Status
}, Timeout, Interval).Should(Equal(v1alpha1.WireguardStatus{
Address: "",
Status: "pending",
Message: "Waiting for service to be ready",
}))

Expect(reconcileServiceWithClusterIP(serviceKey, 51820)).Should(Succeed())

// check that wireguard resource got the right status after the service is ready
wg := &v1alpha1.Wireguard{}
Eventually(func() v1alpha1.WireguardStatus {
Expect(k8sClient.Get(context.Background(), wgKey, wg)).Should(Succeed())
return wg.Status
}, Timeout, Interval).Should(Equal(v1alpha1.WireguardStatus{
Address: expectedAddress,
Port: "51820",
Status: "ready",
Dns: dnsServiceIp,
Message: "VPN is active!",
}))

Eventually(func() string {
deploymentKey := types.NamespacedName{
Name: wgName + "-dep",
Namespace: wgNamespace,
}
deployment := &appsv1.Deployment{}
Expect(k8sClient.Get(context.Background(), deploymentKey, deployment)).Should(Succeed())
Expect(len(deployment.Spec.Template.Spec.Containers)).Should(Equal(2))
Expect(deployment.Spec.Template.Spec.Containers[0].Image).Should(Equal(deployment.Spec.Template.Spec.Containers[1].Image))
return deployment.Spec.Template.Spec.Containers[0].Image
}, Timeout, Interval).Should(Equal(wgTestImage))

// create peer
peerKey := types.NamespacedName{
Name: wgKey.Name + "peer",
Namespace: wgKey.Namespace,
}
peer := &v1alpha1.WireguardPeer{
ObjectMeta: metav1.ObjectMeta{
Name: peerKey.Name,
Namespace: peerKey.Namespace,
},
Spec: v1alpha1.WireguardPeerSpec{
WireguardRef: wgKey.Name,
},
}
Expect(k8sClient.Create(context.Background(), peer)).Should(Succeed())

//get peer secret
wgSecretKeyName := types.NamespacedName{
Name: wgKey.Name,
Namespace: wgKey.Namespace,
}
wgSecret := &corev1.Secret{}
Eventually(func() error {
return k8sClient.Get(context.Background(), wgSecretKeyName, wgSecret)
}, Timeout, Interval).Should(Succeed())
wgPublicKey := string(wgSecret.Data["publicKey"])

Eventually(func() string {
Expect(k8sClient.Get(context.Background(), peerKey, peer)).Should(Succeed())
print(peer.Status.Message)
return peer.Spec.Address
}, Timeout, Interval).Should(Equal("10.8.0.2"))

Eventually(func() v1alpha1.WireguardPeerStatus {
Expect(k8sClient.Get(context.Background(), peerKey, peer)).Should(Succeed())
return peer.Status
}, Timeout, Interval).Should(Equal(v1alpha1.WireguardPeerStatus{
Config: fmt.Sprintf(`
echo "
[Interface]
PrivateKey = $(kubectl get secret %s-peer --template={{.data.privateKey}} -n default | base64 -d)
Address = %s
DNS = %s, %s.svc.cluster.local
[Peer]
PublicKey = %s
AllowedIPs = 0.0.0.0/0
Endpoint = %s:%s"`, peerKey.Name, peer.Spec.Address, dnsServiceIp, peer.Namespace, wgPublicKey, expectedAddress, wg.Status.Port),
Status: "ready",
Message: "Peer configured",
}))

Eventually(func() error {
return k8sClient.Get(context.Background(), wgSecretKeyName, wgSecret)
}, Timeout, Interval).Should(Succeed())

})

for _, useWgUserspace := range []bool{true, false} {
testTextPrefix := "uses"
if !useWgUserspace {
Expand Down Expand Up @@ -547,7 +704,7 @@ Endpoint = %s:%s"`, peerKey.Name, peer.Spec.AllowedIPs, peer.Spec.Address, dnsSe

Eventually(func() []string {
dep := &appsv1.Deployment{}
k8sClient.Get(context.Background(), depKey, dep)
Expect(k8sClient.Get(context.Background(), depKey, dep)).Should(Succeed())
fmt.Println(dep)
for _, c := range dep.Spec.Template.Spec.Containers {
if c.Name == "agent" {
Expand Down

0 comments on commit e328510

Please sign in to comment.