diff --git a/.github/workflows/goreleaser-snapshot.yml b/.github/workflows/goreleaser-snapshot.yml new file mode 100644 index 0000000..129b635 --- /dev/null +++ b/.github/workflows/goreleaser-snapshot.yml @@ -0,0 +1,31 @@ +name: GoReleaser Snapshot Release (dry run) + +on: + workflow_dispatch: + +permissions: + contents: read + +jobs: + goreleaser: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v5 + with: + fetch-depth: 0 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Run GoReleaser + uses: goreleaser/goreleaser-action@v6 + with: + distribution: goreleaser + version: "~> v2" + args: release --clean --snapshot + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..6511c77 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,82 @@ +name: Release + +on: + push: + tags: + - "v*" + +permissions: + contents: write + packages: write + +jobs: + goreleaser: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v5 + with: + fetch-depth: 0 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GitHub Container Registry (GHCR) + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Log in to Docker Hub + uses: docker/login-action@v3 + with: + registry: docker.io + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Set up Go + uses: actions/setup-go@v6 + with: + go-version-file: "go.mod" + + - name: Run GoReleaser + uses: goreleaser/goreleaser-action@v6 + with: + distribution: goreleaser + version: "~> v2" + args: release --clean + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + helm: + runs-on: ubuntu-latest + needs: goreleaser + steps: + - name: Checkout + uses: actions/checkout@v5 + + - name: Set up Helm + uses: azure/setup-helm@v4 + + - name: Package Helm chart + run: | + # get tag and strip leading 'v' if present + TAG="${{ github.ref_name }}" + CLEAN_TAG=${TAG#v} + echo "Packaging chart with version/appVersion=${CLEAN_TAG}" + mkdir -p dist + helm package ./dist/chart --destination ./dist/chartrelease --version "${CLEAN_TAG}" --app-version "${CLEAN_TAG}" + + - name: Login to GHCR (GitHub Container Registry) + run: | + echo "${{ secrets.GITHUB_TOKEN }}" | helm registry login ghcr.io --username "${{ github.actor }}" --password-stdin + + - name: Push chart to GHCR (OCI) + run: | + CHART_TGZ=$(ls ./dist/chartrelease/*.tgz | head -n1) + echo "pushing $CHART_TGZ to ghcr.io/${{ github.repository_owner }}/charts" + helm push "$CHART_TGZ" oci://ghcr.io/${{ github.repository_owner }}/charts diff --git a/.github/workflows/test-chart.yml b/.github/workflows/test-chart.yml new file mode 100644 index 0000000..9a68e56 --- /dev/null +++ b/.github/workflows/test-chart.yml @@ -0,0 +1,89 @@ +name: Test Chart + +on: + workflow_dispatch: + +jobs: + test-e2e: + name: Run on Ubuntu + runs-on: ubuntu-latest + steps: + - name: Clone the code + uses: actions/checkout@v4 + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + + - name: Install the latest version of kind + run: | + curl -Lo ./kind https://kind.sigs.k8s.io/dl/latest/kind-linux-amd64 + chmod +x ./kind + sudo mv ./kind /usr/local/bin/kind + + - name: Verify kind installation + run: kind version + + - name: Create kind cluster + run: kind create cluster + + - name: Prepare gameserver-operator + run: | + go mod tidy + make docker-build IMG=gameserver-operator:v0.1.0 + kind load docker-image gameserver-operator:v0.1.0 + + - name: Setup Helm + uses: azure/setup-helm@v4 + with: + version: v3.14.4 + - name: Verify Helm installation + run: helm version + + - name: Lint Helm Chart + run: | + helm lint ./dist/chart + + # TODO: Uncomment if cert-manager is enabled + # - name: Install cert-manager via Helm + # run: | + # helm repo add jetstack https://charts.jetstack.io + # helm repo update + # helm install cert-manager jetstack/cert-manager --namespace cert-manager --create-namespace --set crds.enabled=true + # + # - name: Wait for cert-manager to be ready + # run: | + # kubectl wait --namespace cert-manager --for=condition=available --timeout=300s deployment/cert-manager + # kubectl wait --namespace cert-manager --for=condition=available --timeout=300s deployment/cert-manager-cainjector + # kubectl wait --namespace cert-manager --for=condition=available --timeout=300s deployment/cert-manager-webhook + + # TODO: Uncomment if Prometheus is enabled + # - name: Install Prometheus Operator CRDs + # run: | + # helm repo add prometheus-community https://prometheus-community.github.io/helm-charts + # helm repo update + # helm install prometheus-crds prometheus-community/prometheus-operator-crds + # + # - name: Install Prometheus via Helm + # run: | + # helm repo add prometheus-community https://prometheus-community.github.io/helm-charts + # helm repo update + # helm install prometheus prometheus-community/prometheus --namespace monitoring --create-namespace + # + # - name: Wait for Prometheus to be ready + # run: | + # kubectl wait --namespace monitoring --for=condition=available --timeout=300s deployment/prometheus-server + + - name: Install Helm chart for project + run: | + helm install my-release ./dist/chart --create-namespace --namespace gameserver-operator-system + + - name: Check Helm release status + run: | + helm status my-release --namespace gameserver-operator-system + +# TODO: Uncomment if prometheus.enabled is set to true to confirm that the ServiceMonitor gets created +# - name: Check Presence of ServiceMonitor +# run: | +# kubectl wait --namespace gameserver-operator-system --for=jsonpath='{.kind}'=ServiceMonitor servicemonitor/gameserver-operator-controller-manager-metrics-monitor diff --git a/.gitignore b/.gitignore index a0b57b1..13b357b 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,9 @@ *.dylib bin/* Dockerfile.cross +dist/* +# keep generated Helm chart +!/dist/chart # Test binary, built with `go test -c` *.test diff --git a/.goreleaser.yaml b/.goreleaser.yaml new file mode 100644 index 0000000..adf6106 --- /dev/null +++ b/.goreleaser.yaml @@ -0,0 +1,80 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/goreleaser/goreleaser/v2.12.5/www/docs/static/schema.json + +version: 2 + +dist: dist/goreleaser + +builds: + - id: manager + main: cmd/main.go + binary: manager + goos: + - linux + goarch: + - amd64 + - arm64 + env: + - CGO_ENABLED=0 + ldflags: + - "-s" + - "-w" + - "-X github.com/idebeijer/gameserver-operator/pkg/version.Version={{ .Version }}" + - "-X github.com/idebeijer/gameserver-operator/pkg/version.Commit={{ .ShortCommit }}" + - "-X github.com/idebeijer/gameserver-operator/pkg/version.Date={{ .Date }}" + - "-X github.com/idebeijer/gameserver-operator/pkg/version.BuiltBy=goreleaser" + +dockers_v2: + - dockerfile: Dockerfile.goreleaser + ids: + - manager + images: + - "ghcr.io/idebeijer/gameserver-operator" + - "docker.io/idebeijer/gameserver-operator" + tags: + - "{{ .Version }}" + labels: + "org.opencontainers.image.created": "{{ .Date }}" + "org.opencontainers.image.url": "https://github.com/idebeijer/gameserver-operator" + "org.opencontainers.image.documentation": "https://github.com/idebeijer/gameserver-operator/blob/main/README.md" + "org.opencontainers.image.source": "{{ .GitURL }}" + "org.opencontainers.image.version": "{{ .Version }}" + "org.opencontainers.image.revision": "{{ .FullCommit }}" + "org.opencontainers.image.licenses": "MIT" + "org.opencontainers.image.title": "{{ .ProjectName }}" + "org.opencontainers.image.description": "Kubernetes operator for managing game servers" + platforms: + - linux/amd64 + - linux/arm64 + +snapshot: + version_template: "{{ incpatch .Version }}" + +release: + footer: | + **Full Changelog**: https://github.com/idebeijer/gameserver-operator/compare/{{ .PreviousTag }}...{{ .Tag }} + +changelog: + sort: asc + use: github + groups: + - title: "Breaking changes" + regexp: '^.*?\w+(\(.+\))?!:\s?.+$' + order: 0 + - title: "Features" + regexp: '^.*?feat(\(.+\))??!?:.+$' + order: 1 + - title: "Bug fixes" + regexp: '^.*?fix(\(.+\))??!?:.+$' + order: 2 + - title: "Documentation" + regexp: '^.*?docs(\(.+\))??!?:.+$' + order: 3 + - title: "Build system" + regexp: '^.*?(build|ci)(\(.+\))??!?:.+$' + order: 4 + - title: "Others" + order: 999 + filters: + exclude: + - '^.*?(test|style)(\(.+\))??!?:.+$' + - 'initial commit' diff --git a/Dockerfile.goreleaser b/Dockerfile.goreleaser new file mode 100644 index 0000000..11b7ae5 --- /dev/null +++ b/Dockerfile.goreleaser @@ -0,0 +1,8 @@ +FROM gcr.io/distroless/static:nonroot AS runtime +ARG TARGETPLATFORM + +COPY --chown=65532:65532 ${TARGETPLATFORM}/manager /usr/local/bin/manager +WORKDIR /home/nonroot +USER 65532:65532 + +ENTRYPOINT ["/usr/local/bin/manager"] diff --git a/Makefile b/Makefile index f35ff85..8530e67 100644 --- a/Makefile +++ b/Makefile @@ -143,6 +143,10 @@ docker-buildx: ## Build and push docker image for the manager for cross-platform - $(CONTAINER_TOOL) buildx rm gameserver-operator-builder rm Dockerfile.cross +.PHONY: goreleaser-release-snapshot +goreleaser-release-snapshot: ## Build a snapshot release locally with goreleaser (does not publish). + goreleaser release --snapshot --clean + .PHONY: build-installer build-installer: manifests generate kustomize ## Generate a consolidated YAML with CRDs and deployment. mkdir -p dist diff --git a/PROJECT b/PROJECT index dd4756f..d760efe 100644 --- a/PROJECT +++ b/PROJECT @@ -6,6 +6,8 @@ cliVersion: 4.9.0 domain: idebeijer.github.io layout: - go.kubebuilder.io/v4 +plugins: + helm.kubebuilder.io/v1-alpha: {} projectName: gameserver-operator repo: github.com/idebeijer/gameserver-operator resources: diff --git a/dist/chart/.helmignore b/dist/chart/.helmignore new file mode 100644 index 0000000..7d92f7f --- /dev/null +++ b/dist/chart/.helmignore @@ -0,0 +1,25 @@ +# Patterns to ignore when building Helm packages. +# Operating system files +.DS_Store + +# Version control directories +.git/ +.gitignore +.bzr/ +.hg/ +.hgignore +.svn/ + +# Backup and temporary files +*.swp +*.tmp +*.bak +*.orig +*~ + +# IDE and editor-related files +.idea/ +.vscode/ + +# Helm chart artifacts +dist/chart/*.tgz diff --git a/dist/chart/Chart.yaml b/dist/chart/Chart.yaml new file mode 100644 index 0000000..2cd25f6 --- /dev/null +++ b/dist/chart/Chart.yaml @@ -0,0 +1,7 @@ +apiVersion: v2 +name: gameserver-operator +description: A Helm chart to distribute the project gameserver-operator +type: application +version: 0.1.0 +appVersion: "0.1.0" +icon: "https://example.com/icon.png" diff --git a/dist/chart/templates/_helpers.tpl b/dist/chart/templates/_helpers.tpl new file mode 100644 index 0000000..2aa222e --- /dev/null +++ b/dist/chart/templates/_helpers.tpl @@ -0,0 +1,50 @@ +{{- define "chart.name" -}} +{{- if .Chart }} + {{- if .Chart.Name }} + {{- .Chart.Name | trunc 63 | trimSuffix "-" }} + {{- else if .Values.nameOverride }} + {{ .Values.nameOverride | trunc 63 | trimSuffix "-" }} + {{- else }} + gameserver-operator + {{- end }} +{{- else }} + gameserver-operator +{{- end }} +{{- end }} + + +{{- define "chart.labels" -}} +{{- if .Chart.AppVersion -}} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +{{- if .Chart.Version }} +helm.sh/chart: {{ .Chart.Version | quote }} +{{- end }} +app.kubernetes.io/name: {{ include "chart.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + + +{{- define "chart.selectorLabels" -}} +app.kubernetes.io/name: {{ include "chart.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + + +{{- define "chart.hasMutatingWebhooks" -}} +{{- $hasMutating := false }} +{{- range . }} + {{- if eq .type "mutating" }} + $hasMutating = true }}{{- end }} +{{- end }} +{{ $hasMutating }}}}{{- end }} + + +{{- define "chart.hasValidatingWebhooks" -}} +{{- $hasValidating := false }} +{{- range . }} + {{- if eq .type "validating" }} + $hasValidating = true }}{{- end }} +{{- end }} +{{ $hasValidating }}}}{{- end }} diff --git a/dist/chart/templates/certmanager/certificate.yaml b/dist/chart/templates/certmanager/certificate.yaml new file mode 100644 index 0000000..2d34a0c --- /dev/null +++ b/dist/chart/templates/certmanager/certificate.yaml @@ -0,0 +1,36 @@ +{{- if .Values.certmanager.enable }} +# Self-signed Issuer +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + labels: + {{- include "chart.labels" . | nindent 4 }} + name: selfsigned-issuer + namespace: {{ .Release.Namespace }} +spec: + selfSigned: {} +{{- if .Values.metrics.enable }} +--- +# Certificate for the metrics +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + annotations: + {{- if .Values.crd.keep }} + "helm.sh/resource-policy": keep + {{- end }} + labels: + {{- include "chart.labels" . | nindent 4 }} + name: metrics-certs + namespace: {{ .Release.Namespace }} +spec: + dnsNames: + - gameserver-operator.{{ .Release.Namespace }}.svc + - gameserver-operator.{{ .Release.Namespace }}.svc.cluster.local + - gameserver-operator-metrics-service.{{ .Release.Namespace }}.svc + issuerRef: + kind: Issuer + name: selfsigned-issuer + secretName: metrics-server-cert +{{- end }} +{{- end }} diff --git a/dist/chart/templates/crd/games.idebeijer.github.io_gameservers.yaml b/dist/chart/templates/crd/games.idebeijer.github.io_gameservers.yaml new file mode 100644 index 0000000..74f645d --- /dev/null +++ b/dist/chart/templates/crd/games.idebeijer.github.io_gameservers.yaml @@ -0,0 +1,323 @@ +{{- if .Values.crd.enable }} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + labels: + {{- include "chart.labels" . | nindent 4 }} + annotations: + {{- if .Values.crd.keep }} + "helm.sh/resource-policy": keep + {{- end }} + controller-gen.kubebuilder.io/version: v0.19.0 + name: gameservers.games.idebeijer.github.io +spec: + group: games.idebeijer.github.io + names: + kind: GameServer + listKind: GameServerList + plural: gameservers + singular: gameserver + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: GameServer is the Schema for the gameservers API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: spec defines the desired state of GameServer + properties: + gameConfigs: + description: GameConfigs holds game-specific configuration options. + properties: + minecraft: + description: Minecraft holds configuration specific to Minecraft + game servers. + properties: + mods: + description: Mods is a list of mods to be installed on the + Minecraft server. + items: + type: string + type: array + version: + description: Version specifies the Minecraft server version. + type: string + type: object + type: object + gameName: + description: |- + GameName is the name of the game server. + For LinuxGSM, this should match the shortname of a supported game server. + Examples include 'rust' for Rust and 'mc' for Minecraft. + + For a list of supported games, see: + https://github.com/GameServerManagers/LinuxGSM/blob/master/lgsm/data/serverlist.csv + type: string + gameVersion: + description: GameVersion is the version of the game server. + type: string + manager: + default: LinuxGSM + description: |- + Manager specifies the installation and management tool for the game server. + 'LinuxGSM' is the default and currently the only supported option. + enum: + - LinuxGSM + type: string + replicas: + default: 1 + description: Replicas is the number of game server instances to run. + format: int32 + minimum: 0 + type: integer + resources: + description: |- + Resources defines resource requests and limits for the primary game server container. + If not specified, Kubernetes scheduler defaults apply. + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This field depends on the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + service: + description: |- + Service defines the service configuration for the game server. + If not specified, no service will be created. + properties: + annotations: + additionalProperties: + type: string + description: Annotations is a map of annotations to add to the + service. + type: object + ports: + description: |- + Ports is a list of ports to expose on the service. + The operator will automatically configure the underlying Pod's containerPorts + based on the targetPort values specified here. + If not specified, no ports will be exposed. + items: + description: ServicePort defines a port to be exposed on the + game server service. + properties: + name: + description: Name is the name of the port. + type: string + nodePort: + description: NodePort is the port number to expose on each + node in the cluster. + format: int32 + type: integer + port: + description: Port is the port number to expose on the service. + format: int32 + type: integer + protocol: + default: UDP + description: Protocol is the protocol used by the port. + Defaults to UDP. + enum: + - TCP + - UDP + type: string + targetPort: + anyOf: + - type: integer + - type: string + description: |- + TargetPort is the port number to forward to on the game server pod. + If not specified, the value of Port will be used. + x-kubernetes-int-or-string: true + type: object + type: array + type: + default: ClusterIP + description: |- + Type is the type of the Kubernetes Service to create for the game server. + Supported types include 'ClusterIP', 'NodePort', and 'LoadBalancer'. + If not specified, 'ClusterIP' will be used. + enum: + - ClusterIP + - NodePort + - LoadBalancer + - ExternalName + type: string + type: object + storage: + description: |- + Storage defines the storage configuration for the game server. + If not specified, a default storage size of 10Gi will be used. + properties: + enabled: + default: true + description: |- + Enabled indicates whether persistent storage is enabled for the game server. + If not specified, storage is enabled by default. + type: boolean + size: + default: 10Gi + description: Size is the size of the persistent volume claim for + the game server data. + pattern: ^\d+Gi$ + type: string + storageClassName: + description: |- + StorageClassName is the name of the StorageClass to use for the persistent volume claim. + If not specified, the default StorageClass for the cluster will be used. + type: string + type: object + required: + - gameName + type: object + status: + description: status defines the observed state of GameServer + properties: + conditions: + description: |- + conditions represent the current state of the GameServer resource. + Each condition has a unique type and reflects the status of a specific aspect of the resource. + + Standard condition types include: + - "Available": the resource is fully functional + - "Progressing": the resource is being created or updated + - "Degraded": the resource failed to reach or maintain its desired state + + The status of each condition is one of True, False, or Unknown. + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} +{{- end -}} diff --git a/dist/chart/templates/manager/manager.yaml b/dist/chart/templates/manager/manager.yaml new file mode 100644 index 0000000..04845cc --- /dev/null +++ b/dist/chart/templates/manager/manager.yaml @@ -0,0 +1,74 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: gameserver-operator-controller-manager + namespace: {{ .Release.Namespace }} + labels: + {{- include "chart.labels" . | nindent 4 }} + control-plane: controller-manager +spec: + replicas: {{ .Values.controllerManager.replicas }} + selector: + matchLabels: + {{- include "chart.selectorLabels" . | nindent 6 }} + control-plane: controller-manager + template: + metadata: + annotations: + kubectl.kubernetes.io/default-container: manager + labels: + {{- include "chart.labels" . | nindent 8 }} + control-plane: controller-manager + {{- if and .Values.controllerManager.pod .Values.controllerManager.pod.labels }} + {{- range $key, $value := .Values.controllerManager.pod.labels }} + {{ $key }}: {{ $value }} + {{- end }} + {{- end }} + spec: + containers: + - name: manager + args: + {{- range .Values.controllerManager.container.args }} + - {{ . }} + {{- end }} + command: + - /manager + image: {{ .Values.controllerManager.container.image.repository }}:{{ .Values.controllerManager.container.image.tag }} + {{- if .Values.controllerManager.container.imagePullPolicy }} + imagePullPolicy: {{ .Values.controllerManager.container.imagePullPolicy }} + {{- end }} + {{- if .Values.controllerManager.container.env }} + env: + {{- range $key, $value := .Values.controllerManager.container.env }} + - name: {{ $key }} + value: {{ $value }} + {{- end }} + {{- end }} + livenessProbe: + {{- toYaml .Values.controllerManager.container.livenessProbe | nindent 12 }} + readinessProbe: + {{- toYaml .Values.controllerManager.container.readinessProbe | nindent 12 }} + resources: + {{- toYaml .Values.controllerManager.container.resources | nindent 12 }} + securityContext: + {{- toYaml .Values.controllerManager.container.securityContext | nindent 12 }} + {{- if and .Values.certmanager.enable .Values.metrics.enable }} + volumeMounts: + {{- if and .Values.metrics.enable .Values.certmanager.enable }} + - name: metrics-certs + mountPath: /tmp/k8s-metrics-server/metrics-certs + readOnly: true + {{- end }} + {{- end }} + securityContext: + {{- toYaml .Values.controllerManager.securityContext | nindent 8 }} + serviceAccountName: {{ .Values.controllerManager.serviceAccountName }} + terminationGracePeriodSeconds: {{ .Values.controllerManager.terminationGracePeriodSeconds }} + {{- if and .Values.certmanager.enable .Values.metrics.enable }} + volumes: + {{- if and .Values.metrics.enable .Values.certmanager.enable }} + - name: metrics-certs + secret: + secretName: metrics-server-cert + {{- end }} + {{- end }} diff --git a/dist/chart/templates/metrics/metrics-service.yaml b/dist/chart/templates/metrics/metrics-service.yaml new file mode 100644 index 0000000..3241f0d --- /dev/null +++ b/dist/chart/templates/metrics/metrics-service.yaml @@ -0,0 +1,18 @@ +{{- if .Values.metrics.enable }} +apiVersion: v1 +kind: Service +metadata: + name: gameserver-operator-controller-manager-metrics-service + namespace: {{ .Release.Namespace }} + labels: + {{- include "chart.labels" . | nindent 4 }} + control-plane: controller-manager +spec: + ports: + - port: 8443 + targetPort: 8443 + protocol: TCP + name: https + selector: + control-plane: controller-manager +{{- end }} diff --git a/dist/chart/templates/network-policy/allow-metrics-traffic.yaml b/dist/chart/templates/network-policy/allow-metrics-traffic.yaml new file mode 100644 index 0000000..51c698e --- /dev/null +++ b/dist/chart/templates/network-policy/allow-metrics-traffic.yaml @@ -0,0 +1,28 @@ +{{- if .Values.networkPolicy.enable }} +# This NetworkPolicy allows ingress traffic +# with Pods running on namespaces labeled with 'metrics: enabled'. Only Pods on those +# namespaces are able to gather data from the metrics endpoint. +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + labels: + {{- include "chart.labels" . | nindent 4 }} + name: allow-metrics-traffic + namespace: {{ .Release.Namespace }} +spec: + podSelector: + matchLabels: + control-plane: controller-manager + app.kubernetes.io/name: gameserver-operator + policyTypes: + - Ingress + ingress: + # This allows ingress traffic from any namespace with the label metrics: enabled + - from: + - namespaceSelector: + matchLabels: + metrics: enabled # Only from namespaces with this label + ports: + - port: 8443 + protocol: TCP +{{- end -}} diff --git a/dist/chart/templates/prometheus/monitor.yaml b/dist/chart/templates/prometheus/monitor.yaml new file mode 100644 index 0000000..342866c --- /dev/null +++ b/dist/chart/templates/prometheus/monitor.yaml @@ -0,0 +1,40 @@ +# To integrate with Prometheus. +{{- if .Values.prometheus.enable }} +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + labels: + {{- include "chart.labels" . | nindent 4 }} + control-plane: controller-manager + name: gameserver-operator-controller-manager-metrics-monitor + namespace: {{ .Release.Namespace }} +spec: + endpoints: + - path: /metrics + port: https + scheme: https + bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token + tlsConfig: + {{- if .Values.certmanager.enable }} + serverName: gameserver-operator-controller-manager-metrics-service.{{ .Release.Namespace }}.svc + # Apply secure TLS configuration with cert-manager + insecureSkipVerify: false + ca: + secret: + name: metrics-server-cert + key: ca.crt + cert: + secret: + name: metrics-server-cert + key: tls.crt + keySecret: + name: metrics-server-cert + key: tls.key + {{- else }} + # Development/Test mode (insecure configuration) + insecureSkipVerify: true + {{- end }} + selector: + matchLabels: + control-plane: controller-manager +{{- end }} diff --git a/dist/chart/templates/rbac/gameserver_admin_role.yaml b/dist/chart/templates/rbac/gameserver_admin_role.yaml new file mode 100644 index 0000000..cb1ce78 --- /dev/null +++ b/dist/chart/templates/rbac/gameserver_admin_role.yaml @@ -0,0 +1,28 @@ +{{- if .Values.rbac.enable }} +# This rule is not used by the project gameserver-operator itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants full permissions ('*') over games.idebeijer.github.io. +# This role is intended for users authorized to modify roles and bindings within the cluster, +# enabling them to delegate specific permissions to other users or groups as needed. + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + {{- include "chart.labels" . | nindent 4 }} + name: gameserver-admin-role +rules: +- apiGroups: + - games.idebeijer.github.io + resources: + - gameservers + verbs: + - '*' +- apiGroups: + - games.idebeijer.github.io + resources: + - gameservers/status + verbs: + - get +{{- end -}} diff --git a/dist/chart/templates/rbac/gameserver_editor_role.yaml b/dist/chart/templates/rbac/gameserver_editor_role.yaml new file mode 100644 index 0000000..a2d6703 --- /dev/null +++ b/dist/chart/templates/rbac/gameserver_editor_role.yaml @@ -0,0 +1,34 @@ +{{- if .Values.rbac.enable }} +# This rule is not used by the project gameserver-operator itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants permissions to create, update, and delete resources within the games.idebeijer.github.io. +# This role is intended for users who need to manage these resources +# but should not control RBAC or manage permissions for others. + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + {{- include "chart.labels" . | nindent 4 }} + name: gameserver-editor-role +rules: +- apiGroups: + - games.idebeijer.github.io + resources: + - gameservers + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - games.idebeijer.github.io + resources: + - gameservers/status + verbs: + - get +{{- end -}} diff --git a/dist/chart/templates/rbac/gameserver_viewer_role.yaml b/dist/chart/templates/rbac/gameserver_viewer_role.yaml new file mode 100644 index 0000000..a3ec2ad --- /dev/null +++ b/dist/chart/templates/rbac/gameserver_viewer_role.yaml @@ -0,0 +1,30 @@ +{{- if .Values.rbac.enable }} +# This rule is not used by the project gameserver-operator itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants read-only access to games.idebeijer.github.io resources. +# This role is intended for users who need visibility into these resources +# without permissions to modify them. It is ideal for monitoring purposes and limited-access viewing. + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + {{- include "chart.labels" . | nindent 4 }} + name: gameserver-viewer-role +rules: +- apiGroups: + - games.idebeijer.github.io + resources: + - gameservers + verbs: + - get + - list + - watch +- apiGroups: + - games.idebeijer.github.io + resources: + - gameservers/status + verbs: + - get +{{- end -}} diff --git a/dist/chart/templates/rbac/leader_election_role.yaml b/dist/chart/templates/rbac/leader_election_role.yaml new file mode 100644 index 0000000..85b4bcb --- /dev/null +++ b/dist/chart/templates/rbac/leader_election_role.yaml @@ -0,0 +1,42 @@ +{{- if .Values.rbac.enable }} +# permissions to do leader election. +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + labels: + {{- include "chart.labels" . | nindent 4 }} + namespace: {{ .Release.Namespace }} + name: gameserver-operator-leader-election-role +rules: +- apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +{{- end -}} diff --git a/dist/chart/templates/rbac/leader_election_role_binding.yaml b/dist/chart/templates/rbac/leader_election_role_binding.yaml new file mode 100644 index 0000000..fa638a5 --- /dev/null +++ b/dist/chart/templates/rbac/leader_election_role_binding.yaml @@ -0,0 +1,17 @@ +{{- if .Values.rbac.enable }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + labels: + {{- include "chart.labels" . | nindent 4 }} + namespace: {{ .Release.Namespace }} + name: gameserver-operator-leader-election-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: gameserver-operator-leader-election-role +subjects: +- kind: ServiceAccount + name: {{ .Values.controllerManager.serviceAccountName }} + namespace: {{ .Release.Namespace }} +{{- end -}} diff --git a/dist/chart/templates/rbac/metrics_auth_role.yaml b/dist/chart/templates/rbac/metrics_auth_role.yaml new file mode 100644 index 0000000..06d0f83 --- /dev/null +++ b/dist/chart/templates/rbac/metrics_auth_role.yaml @@ -0,0 +1,21 @@ +{{- if and .Values.rbac.enable .Values.metrics.enable }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + {{- include "chart.labels" . | nindent 4 }} + name: gameserver-operator-metrics-auth-role +rules: +- apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create +- apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create +{{- end -}} diff --git a/dist/chart/templates/rbac/metrics_auth_role_binding.yaml b/dist/chart/templates/rbac/metrics_auth_role_binding.yaml new file mode 100644 index 0000000..11a383f --- /dev/null +++ b/dist/chart/templates/rbac/metrics_auth_role_binding.yaml @@ -0,0 +1,16 @@ +{{- if and .Values.rbac.enable .Values.metrics.enable }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + {{- include "chart.labels" . | nindent 4 }} + name: gameserver-operator-metrics-auth-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: gameserver-operator-metrics-auth-role +subjects: +- kind: ServiceAccount + name: {{ .Values.controllerManager.serviceAccountName }} + namespace: {{ .Release.Namespace }} +{{- end -}} diff --git a/dist/chart/templates/rbac/metrics_reader_role.yaml b/dist/chart/templates/rbac/metrics_reader_role.yaml new file mode 100644 index 0000000..2737bc2 --- /dev/null +++ b/dist/chart/templates/rbac/metrics_reader_role.yaml @@ -0,0 +1,13 @@ +{{- if and .Values.rbac.enable .Values.metrics.enable }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + {{- include "chart.labels" . | nindent 4 }} + name: gameserver-operator-metrics-reader +rules: +- nonResourceURLs: + - "/metrics" + verbs: + - get +{{- end -}} diff --git a/dist/chart/templates/rbac/role.yaml b/dist/chart/templates/rbac/role.yaml new file mode 100644 index 0000000..0159cf4 --- /dev/null +++ b/dist/chart/templates/rbac/role.yaml @@ -0,0 +1,60 @@ +{{- if .Values.rbac.enable }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + {{- include "chart.labels" . | nindent 4 }} + name: gameserver-operator-manager-role +rules: +- apiGroups: + - "" + resources: + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - apps + resources: + - statefulsets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - games.idebeijer.github.io + resources: + - gameservers + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - games.idebeijer.github.io + resources: + - gameservers/finalizers + verbs: + - update +- apiGroups: + - games.idebeijer.github.io + resources: + - gameservers/status + verbs: + - get + - patch + - update +{{- end -}} diff --git a/dist/chart/templates/rbac/role_binding.yaml b/dist/chart/templates/rbac/role_binding.yaml new file mode 100644 index 0000000..c2e6e1c --- /dev/null +++ b/dist/chart/templates/rbac/role_binding.yaml @@ -0,0 +1,16 @@ +{{- if .Values.rbac.enable }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + {{- include "chart.labels" . | nindent 4 }} + name: gameserver-operator-manager-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: gameserver-operator-manager-role +subjects: +- kind: ServiceAccount + name: {{ .Values.controllerManager.serviceAccountName }} + namespace: {{ .Release.Namespace }} +{{- end -}} diff --git a/dist/chart/templates/rbac/service_account.yaml b/dist/chart/templates/rbac/service_account.yaml new file mode 100644 index 0000000..93e0a32 --- /dev/null +++ b/dist/chart/templates/rbac/service_account.yaml @@ -0,0 +1,15 @@ +{{- if .Values.rbac.enable }} +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + {{- include "chart.labels" . | nindent 4 }} + {{- if and .Values.controllerManager.serviceAccount .Values.controllerManager.serviceAccount.annotations }} + annotations: + {{- range $key, $value := .Values.controllerManager.serviceAccount.annotations }} + {{ $key }}: {{ $value }} + {{- end }} + {{- end }} + name: {{ .Values.controllerManager.serviceAccountName }} + namespace: {{ .Release.Namespace }} +{{- end -}} diff --git a/dist/chart/values.yaml b/dist/chart/values.yaml new file mode 100644 index 0000000..ba55223 --- /dev/null +++ b/dist/chart/values.yaml @@ -0,0 +1,77 @@ +# [MANAGER]: Manager Deployment Configurations +controllerManager: + replicas: 1 + container: + image: + repository: controller + tag: latest + imagePullPolicy: IfNotPresent + args: + - "--leader-elect" + - "--metrics-bind-address=:8443" + - "--health-probe-bind-address=:8081" + resources: + limits: + cpu: 500m + memory: 128Mi + requests: + cpu: 10m + memory: 64Mi + livenessProbe: + initialDelaySeconds: 15 + periodSeconds: 20 + httpGet: + path: /healthz + port: 8081 + readinessProbe: + initialDelaySeconds: 5 + periodSeconds: 10 + httpGet: + path: /readyz + port: 8081 + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - "ALL" + securityContext: + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + terminationGracePeriodSeconds: 10 + serviceAccountName: gameserver-operator-controller-manager + +# [RBAC]: To enable RBAC (Permissions) configurations +rbac: + enable: true + +# [CRDs]: To enable the CRDs +crd: + # This option determines whether the CRDs are included + # in the installation process. + enable: true + + # Enabling this option adds the "helm.sh/resource-policy": keep + # annotation to the CRD, ensuring it remains installed even when + # the Helm release is uninstalled. + # NOTE: Removing the CRDs will also remove all cert-manager CR(s) + # (Certificates, Issuers, ...) due to garbage collection. + keep: true + +# [METRICS]: Set to true to generate manifests for exporting metrics. +# To disable metrics export set false, and ensure that the +# ControllerManager argument "--metrics-bind-address=:8443" is removed. +metrics: + enable: true + +# [PROMETHEUS]: To enable a ServiceMonitor to export metrics to Prometheus set true +prometheus: + enable: false + +# [CERT-MANAGER]: To enable cert-manager injection to webhooks set true +certmanager: + enable: false + +# [NETWORK POLICIES]: To enable NetworkPolicies set true +networkPolicy: + enable: false diff --git a/pkg/version/version.go b/pkg/version/version.go new file mode 100644 index 0000000..531774b --- /dev/null +++ b/pkg/version/version.go @@ -0,0 +1,22 @@ +package version + +// variables overwritten by -ldflags -X at build time. +var ( + Version = "dev" + Commit = "none" + Date = "unknown" + BuiltBy = "local" +) + +func Short() string { + return Version +} + +func All() map[string]string { + return map[string]string{ + "version": Version, + "commit": Commit, + "date": Date, + "builtBy": BuiltBy, + } +}