Skip to content

Commit

Permalink
feat: handle rcon secret
Browse files Browse the repository at this point in the history
  • Loading branch information
jeremylvln committed Mar 4, 2022
1 parent 820ae3d commit 43a1afa
Show file tree
Hide file tree
Showing 9 changed files with 199 additions and 26 deletions.
15 changes: 15 additions & 0 deletions api/v1alpha1/minecraftserver_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ type MinecraftServerSpec struct {
//+kubebuilder:default={disableNether: false, disableEnd: false}
World *MinecraftServerWorldSpec `json:"world,omitempty"`

// Configuration of Minecraft Server's rcon.
//+kubebuilder:default={enabled: true}
Rcon *MinecraftServerRconSpec `json:"rcon,omitempty"`

// The desired state of the Kubernetes Service to create for the
// Minecraft Server.
//+kubebuilder:default={enabled: false}
Expand Down Expand Up @@ -135,6 +139,17 @@ type MinecraftServerWorldSpec struct {
DisableEnd bool `json:"disableEnd,omitempty"`
}

type MinecraftServerRconSpec struct {
// Whether to enable rcon.
//+kubebuilder:default=true
Enabled bool `json:"enabled,omitempty"`

// Name of a Kubernetes Secret containing a `password` key to use
// as rcon password. If not provided, a Secret will be created
// dedicated to this Minecraft Server.
PasswordSecretName string `json:"passwordSecretName,omitempty"`
}

// Configuration attributes for the Service resource.
type MinecraftServerServiceSpec struct {
// Whether to create a Service for the Minecraft Server. Defaults
Expand Down
20 changes: 20 additions & 0 deletions api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions config/crd/bases/shulkermc.io_minecraftservers.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1037,6 +1037,21 @@ spec:
format: int64
type: integer
type: object
rcon:
default:
enabled: true
description: Configuration of Minecraft Server's rcon.
properties:
enabled:
default: true
description: Whether to enable rcon.
type: boolean
passwordSecretName:
description: Name of a Kubernetes Secret containing a `password`
key to use as rcon password. If not provided, a Secret will
be created dedicated to this Minecraft Server.
type: string
type: object
resources:
default:
limits:
Expand Down
22 changes: 21 additions & 1 deletion controllers/minecraftserver_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func (r *MinecraftServerReconciler) Reconcile(ctx context.Context, req ctrl.Requ
Instance: minecraftServer,
Scheme: r.Scheme,
}
builders := resourceBuilder.ResourceBuilders()
builders, dirtyBuilders := resourceBuilder.ResourceBuilders()

for _, builder := range builders {
resource, err := builder.Build()
Expand Down Expand Up @@ -120,6 +120,26 @@ func (r *MinecraftServerReconciler) Reconcile(ctx context.Context, req ctrl.Requ
// }
}

for _, dirtyBuilder := range dirtyBuilders {
resource, err := dirtyBuilder.Build()
if err != nil {
return ctrl.Result{}, err
}

existingResource := resource
apiError := r.Get(ctx, types.NamespacedName{
Namespace: resource.GetNamespace(),
Name: resource.GetName(),
}, existingResource)

if apiError == nil {
apiError = r.Delete(ctx, existingResource)
return ctrl.Result{}, apiError
} else if !k8serrors.IsNotFound(apiError) {
return ctrl.Result{}, apiError
}
}

return ctrl.Result{}, nil
}

Expand Down
17 changes: 15 additions & 2 deletions internal/resource/server/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,26 @@ type ResourceBuilder interface {
CanBeUpdated() bool
}

func (b *MinecraftServerResourceBuilder) ResourceBuilders() []ResourceBuilder {
func (b *MinecraftServerResourceBuilder) ResourceBuilders() ([]ResourceBuilder, []ResourceBuilder) {
builders := []ResourceBuilder{
b.MinecraftServerPod(),
b.MinecraftServerConfigMap(),
}
dirtyBuilders := []ResourceBuilder{}

if b.Instance.Spec.Rcon.Enabled {
builders = append(builders, b.MinecraftServerRconSecret())
} else {
dirtyBuilders = append(dirtyBuilders, b.MinecraftServerRconSecret())
}

if b.Instance.Spec.Service.Enabled {
builders = append(builders, b.MinecraftServerService())
} else {
dirtyBuilders = append(dirtyBuilders, b.MinecraftServerService())
}

return builders
return builders, dirtyBuilders
}

func (b *MinecraftServerResourceBuilder) getPodName() string {
Expand All @@ -40,6 +49,10 @@ func (b *MinecraftServerResourceBuilder) getConfigMapName() string {
return fmt.Sprintf("minecraft-server-config-%s", b.Instance.Name)
}

func (b *MinecraftServerResourceBuilder) getRconSecretName() string {
return fmt.Sprintf("minecraft-server-rcon-secret-%s", b.Instance.Name)
}

func (b *MinecraftServerResourceBuilder) getServiceName() string {
return fmt.Sprintf("minecraft-server-%s", b.Instance.Name)
}
Expand Down
74 changes: 52 additions & 22 deletions internal/resource/server/pod.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,14 +79,8 @@ func (b *MinecraftServerPodBuilder) Update(object client.Object) error {
Containers: []corev1.Container{{
Image: "itzg/minecraft-server:latest",
Name: "minecraft-server",
Ports: []corev1.ContainerPort{{
ContainerPort: 25565,
Name: "minecraft",
}, {
ContainerPort: 25575,
Name: "rcon",
}},
Env: b.getPodEnv(),
Ports: b.getPodPorts(),
Env: b.getPodEnv(),
LivenessProbe: &corev1.Probe{
ProbeHandler: corev1.ProbeHandler{
TCPSocket: &corev1.TCPSocketAction{
Expand Down Expand Up @@ -149,7 +143,8 @@ func (b *MinecraftServerPodBuilder) CanBeUpdated() bool {

func (b *MinecraftServerPodBuilder) getPodEnv() []corev1.EnvVar {
shouldEnforceWhitelist := len(b.Instance.Spec.WhitelistedPlayers) > 0
env := append([]corev1.EnvVar{

env := []corev1.EnvVar{
{
Name: "SERVER_NAME",
Value: b.Instance.Name,
Expand All @@ -170,18 +165,6 @@ func (b *MinecraftServerPodBuilder) getPodEnv() []corev1.EnvVar {
Name: "SERVER_PORT",
Value: "25565",
},
{
Name: "ENABLE_RCON",
Value: "true",
},
{
Name: "RCON_PORT",
Value: "25575",
},
{
Name: "RCON_PASSWORD",
Value: "hello", // TODO: change me
},
{
Name: "MAX_PLAYERS",
Value: fmt.Sprintf("%d", *b.Instance.Spec.MaxPlayers),
Expand Down Expand Up @@ -218,11 +201,58 @@ func (b *MinecraftServerPodBuilder) getPodEnv() []corev1.EnvVar {
Name: "WORLD",
Value: "https://i.jeremylvln.fr/shulker/hub.tar.gz",
},
}, b.Instance.Spec.PodOverrides.Env...)
}

if b.Instance.Spec.Rcon.Enabled {
rconSecretName := b.getRconSecretName()
if b.Instance.Spec.Rcon.PasswordSecretName != "" {
rconSecretName = b.Instance.Spec.Rcon.PasswordSecretName
}

env = append(env, []corev1.EnvVar{
{
Name: "ENABLE_RCON",
Value: strconv.FormatBool(b.Instance.Spec.Rcon.Enabled),
},
{
Name: "RCON_PORT",
Value: "25575",
},
{
Name: "RCON_PASSWORD",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: rconSecretName,
},
Key: "password",
},
},
},
}...)
}

env = append(env, b.Instance.Spec.PodOverrides.Env...)

return env
}

func (b *MinecraftServerPodBuilder) getPodPorts() []corev1.ContainerPort {
ports := []corev1.ContainerPort{{
ContainerPort: 25565,
Name: "minecraft",
}}

if b.Instance.Spec.Rcon.Enabled {
ports = append(ports, corev1.ContainerPort{
ContainerPort: 25575,
Name: "rcon",
})
}

return ports
}

func getTypeFromVersionChannel(channel shulkermciov1alpha1.MinecraftServerVersionChannel) string {
switch channel {
case shulkermciov1alpha1.MinecraftServerVersionVanilla:
Expand Down
58 changes: 58 additions & 0 deletions internal/resource/server/rcon_secret.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package resource

import (
"fmt"
"math/rand"

corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
)

type MinecraftServerRconSecretBuilder struct {
*MinecraftServerResourceBuilder
}

func (b *MinecraftServerResourceBuilder) MinecraftServerRconSecret() *MinecraftServerRconSecretBuilder {
return &MinecraftServerRconSecretBuilder{b}
}

func (b *MinecraftServerRconSecretBuilder) Build() (client.Object, error) {
return &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: b.getRconSecretName(),
Namespace: b.Instance.Namespace,
Labels: b.getLabels(),
},
Type: corev1.SecretTypeOpaque,
StringData: map[string]string{
"password": getRandomRconPassword(),
},
}, nil
}

func (b *MinecraftServerRconSecretBuilder) Update(object client.Object) error {
secret := object.(*corev1.Secret)

if err := controllerutil.SetControllerReference(b.Instance, secret, b.Scheme); err != nil {
return fmt.Errorf("failed setting controller reference for Secret: %v", err)
}

return nil
}

func (b *MinecraftServerRconSecretBuilder) CanBeUpdated() bool {
return true
}

const passwordChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"

func getRandomRconPassword() string {
password := make([]byte, 16)

for i := range password {
password[i] = passwordChars[rand.Intn(len(passwordChars))]
}
return string(password)
}
2 changes: 1 addition & 1 deletion internal/resource/server/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func (b *MinecraftServerServiceBuilder) Update(object client.Object) error {
Name: "minecraft",
}}

if b.Instance.Spec.Service.ExposesRconPort {
if b.Instance.Spec.Rcon.Enabled && b.Instance.Spec.Service.ExposesRconPort {
ports = append(ports, corev1.ServicePort{
Protocol: corev1.ProtocolTCP,
Port: 25575,
Expand Down
2 changes: 2 additions & 0 deletions test/test.ms.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ spec:
url: https://i.jeremylvln.fr/shulker/hub.tar.gz
disableNether: true
disableEnd: true
rcon:
enabled: false
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
Expand Down

0 comments on commit 43a1afa

Please sign in to comment.