Skip to content

Commit

Permalink
add ports logic
Browse files Browse the repository at this point in the history
  • Loading branch information
templarfelix committed Apr 11, 2024
1 parent 60e1dcc commit 812fc2e
Show file tree
Hide file tree
Showing 6 changed files with 217 additions and 45 deletions.
12 changes: 12 additions & 0 deletions api/v1alpha1/base.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package v1alpha1

import corev1 "k8s.io/api/core/v1"

// Base values need
type Base struct {

//+kubebuilder:default="10G"
Storage string `json:"storage,omitempty"`

Ports []corev1.ServicePort `json:"ports"`
}
3 changes: 1 addition & 2 deletions api/v1alpha1/dayz_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@ type DayzSpec struct {
//+kubebuilder:default="gameservermanagers/gameserver:dayz"
Image string `json:"image"`

//+kubebuilder:default="10G"
Storage string `json:"storage,omitempty"`
Base `json:",inline"`

Config DayzConfig `json:"config,omitempty"`
}
Expand Down
65 changes: 65 additions & 0 deletions config/crd/bases/gameserver.templarfelix.com_dayzs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,76 @@ spec:
image:
default: gameservermanagers/gameserver:dayz
type: string
ports:
items:
description: ServicePort contains information on service's port.
properties:
appProtocol:
description: "The application protocol for this port. This is
used as a hint for implementations to offer richer behavior
for protocols that they understand. This field follows standard
Kubernetes label syntax. Valid values are either: \n * Un-prefixed
protocol names - reserved for IANA standard service names
(as per RFC-6335 and https://www.iana.org/assignments/service-names).
\n * Kubernetes-defined prefixed names: * 'kubernetes.io/h2c'
- HTTP/2 over cleartext as described in https://www.rfc-editor.org/rfc/rfc7540
* 'kubernetes.io/ws' - WebSocket over cleartext as described
in https://www.rfc-editor.org/rfc/rfc6455 * 'kubernetes.io/wss'
- WebSocket over TLS as described in https://www.rfc-editor.org/rfc/rfc6455
\n * Other protocols should use implementation-defined prefixed
names such as mycompany.com/my-custom-protocol."
type: string
name:
description: The name of this port within the service. This
must be a DNS_LABEL. All ports within a ServiceSpec must have
unique names. When considering the endpoints for a Service,
this must match the 'name' field in the EndpointPort. Optional
if only one ServicePort is defined on this service.
type: string
nodePort:
description: 'The port on each node on which this service is
exposed when type is NodePort or LoadBalancer. Usually assigned
by the system. If a value is specified, in-range, and not
in use it will be used, otherwise the operation will fail. If
not specified, a port will be allocated if this Service requires
one. If this field is specified when creating a Service which
does not need it, creation will fail. This field will be wiped
when updating a Service to no longer need it (e.g. changing
type from NodePort to ClusterIP). More info: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport'
format: int32
type: integer
port:
description: The port that will be exposed by this service.
format: int32
type: integer
protocol:
default: TCP
description: The IP protocol for this port. Supports "TCP",
"UDP", and "SCTP". Default is TCP.
type: string
targetPort:
anyOf:
- type: integer
- type: string
description: 'Number or name of the port to access on the pods
targeted by the service. Number must be in the range 1 to
65535. Name must be an IANA_SVC_NAME. If this is a string,
it will be looked up as a named port in the target Pod''s
container ports. If this is not specified, the value of the
''port'' field is used (an identity map). This field is ignored
for services with clusterIP=None, and should be omitted or
set equal to the ''port'' field. More info: https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service'
x-kubernetes-int-or-string: true
required:
- port
type: object
type: array
storage:
default: 10G
type: string
required:
- image
- ports
type: object
status:
description: DayzStatus defines the observed state of Dayz
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ go 1.20
require (
github.com/onsi/ginkgo/v2 v2.11.0
github.com/onsi/gomega v1.27.10
golang.org/x/net v0.17.0
k8s.io/api v0.28.3
k8s.io/apimachinery v0.28.3
k8s.io/client-go v0.28.3
sigs.k8s.io/controller-runtime v0.16.3
Expand Down Expand Up @@ -48,7 +50,6 @@ require (
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.25.0 // indirect
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect
golang.org/x/net v0.17.0 // indirect
golang.org/x/oauth2 v0.8.0 // indirect
golang.org/x/sys v0.13.0 // indirect
golang.org/x/term v0.13.0 // indirect
Expand All @@ -61,7 +62,6 @@ require (
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/api v0.28.3 // indirect
k8s.io/apiextensions-apiserver v0.28.3 // indirect
k8s.io/component-base v0.28.3 // indirect
k8s.io/klog/v2 v2.100.1 // indirect
Expand Down
132 changes: 132 additions & 0 deletions internal/controller/base_controller.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package controller

import (
"golang.org/x/net/context"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"sigs.k8s.io/controller-runtime/pkg/log"
)

func ReconcilePVC(ctx context.Context, k8sClient client.Client, owner metav1.Object, storage string) error {
logger := log.FromContext(ctx)
pvcName := owner.GetName() + "-pvc"

// Define o PVC desejado
desired := &corev1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: pvcName,
Namespace: owner.GetNamespace(),
},
Spec: corev1.PersistentVolumeClaimSpec{
AccessModes: []corev1.PersistentVolumeAccessMode{corev1.ReadWriteOnce},
Resources: corev1.ResourceRequirements{
Requests: corev1.ResourceList{
corev1.ResourceStorage: resource.MustParse(storage),
},
},
},
}

// Define o objeto como proprietário do PVC
if err := controllerutil.SetControllerReference(owner, desired, k8sClient.Scheme()); err != nil {
return err
}

// Verifica se o PVC já existe
found := &corev1.PersistentVolumeClaim{}
err := k8sClient.Get(ctx, types.NamespacedName{Name: pvcName, Namespace: owner.GetNamespace()}, found)
if err != nil && errors.IsNotFound(err) {
logger.Info("Creating a new PVC", "Namespace", owner.GetNamespace(), "Name", pvcName)
return k8sClient.Create(ctx, desired)
} else if err != nil {
return err
}

// Se o PVC já existir, loga que está pulando a criação e retorna nil
logger.Info("Skip reconcile: PVC already exists", "Namespace", found.Namespace, "Name", found.Name)
return nil
}

func ReconcileServices(ctx context.Context, k8sClient client.Client, owner metav1.Object, ports []corev1.ServicePort) error {
logger := log.FromContext(ctx)
serviceNameTCP := owner.GetName() + "-tcp"
serviceNameUDP := owner.GetName() + "-tcp"

tcpPorts, udpPorts := separatePortsByProtocol(ports)

// TCP
desiredTCP := &corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: serviceNameTCP,
Namespace: owner.GetNamespace(),
},
Spec: corev1.ServiceSpec{
Selector: map[string]string{
"app": owner.GetName(),
},
Ports: tcpPorts,
},
}

// TCP
desiredUDP := &corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: serviceNameUDP,
Namespace: owner.GetNamespace(),
},
Spec: corev1.ServiceSpec{
Selector: map[string]string{
"app": owner.GetName(),
},
Ports: udpPorts,
},
}

// TCP
if err := controllerutil.SetControllerReference(owner, desiredTCP, k8sClient.Scheme()); err != nil {
return err
}

foundTCP := &corev1.PersistentVolumeClaim{}
errTCP := k8sClient.Get(ctx, types.NamespacedName{Name: serviceNameTCP, Namespace: owner.GetNamespace()}, foundTCP)
if errTCP != nil && errors.IsNotFound(errTCP) {
logger.Info("Creating a new Service", "Namespace", owner.GetNamespace(), "Name", desiredTCP)
return k8sClient.Create(ctx, desiredTCP)
} else if errTCP != nil {
return errTCP
}
logger.Info("Skip reconcile: Service already exists", "Namespace", foundTCP.Namespace, "Name", foundTCP.Name)

// UDP
if err := controllerutil.SetControllerReference(owner, desiredUDP, k8sClient.Scheme()); err != nil {
return err
}

foundUDP := &corev1.PersistentVolumeClaim{}
err := k8sClient.Get(ctx, types.NamespacedName{Name: serviceNameUDP, Namespace: owner.GetNamespace()}, foundUDP)
if err != nil && errors.IsNotFound(err) {
logger.Info("Creating a new Service", "Namespace", owner.GetNamespace(), "Name", desiredTCP)
return k8sClient.Create(ctx, desiredTCP)
} else if err != nil {
return err
}

logger.Info("Skip reconcile: Service already exists", "Namespace", foundUDP.Namespace, "Name", foundUDP.Name)
return nil
}

func separatePortsByProtocol(ports []corev1.ServicePort) (tcpPorts []corev1.ServicePort, udpPorts []corev1.ServicePort) {
for _, port := range ports {
if port.Protocol == corev1.ProtocolTCP {
tcpPorts = append(tcpPorts, port)
} else if port.Protocol == corev1.ProtocolUDP {
udpPorts = append(udpPorts, port)
}
}
return tcpPorts, udpPorts
}
46 changes: 5 additions & 41 deletions internal/controller/dayz_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ package controller
import (
"context"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/resource"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"sigs.k8s.io/controller-runtime/pkg/reconcile"

Expand Down Expand Up @@ -72,7 +71,7 @@ func (r *DayzReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.
}
return reconcile.Result{}, err
}
if err := r.reconcilePVC(ctx, instance); err != nil {
if err := ReconcilePVC(ctx, r.Client, instance, instance.Spec.Storage); err != nil {
return reconcile.Result{}, err
}

Expand All @@ -84,6 +83,10 @@ func (r *DayzReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.
return reconcile.Result{}, err
}

if err := ReconcileServices(ctx, r.Client, instance, instance.Spec.Ports); err != nil {
return reconcile.Result{}, err
}

return reconcile.Result{}, nil
}

Expand Down Expand Up @@ -237,45 +240,6 @@ func (r *DayzReconciler) reconcileDeployment(ctx context.Context, instance *game
return nil
}

func (r *DayzReconciler) reconcilePVC(ctx context.Context, instance *gameserverv1alpha1.Dayz) error {
logger := log.FromContext(ctx)

k8sResource := &corev1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: instance.Name + "-pvc",
Namespace: instance.Namespace,
},
Spec: corev1.PersistentVolumeClaimSpec{
AccessModes: []corev1.PersistentVolumeAccessMode{corev1.ReadWriteOnce},
Resources: corev1.ResourceRequirements{
Requests: corev1.ResourceList{
corev1.ResourceStorage: resource.MustParse(instance.Spec.Storage),
},
},
},
}

if err := controllerutil.SetControllerReference(instance, k8sResource, r.Scheme); err != nil {
return err
}

found := &corev1.PersistentVolumeClaim{}
err := r.Client.Get(ctx, client.ObjectKey{Name: k8sResource.Name, Namespace: k8sResource.Namespace}, found)
if err != nil && errors.IsNotFound(err) {
logger.Info("Creating a new PVC %s/%s\n", k8sResource.Namespace, k8sResource.Name)
err = r.Client.Create(ctx, k8sResource)
if err != nil {
return err
}
} else if err != nil {
return err
}

logger.Info("Skip reconcile: PVC %s/%s already exists", found.Namespace, found.Name)

return nil
}

func (r *DayzReconciler) reconcileConfigMap(ctx context.Context, instance *gameserverv1alpha1.Dayz) error {
logger := log.FromContext(ctx)

Expand Down

0 comments on commit 812fc2e

Please sign in to comment.