From 80634ef5e5104c191a8d32a2fca2f0a5a00e6599 Mon Sep 17 00:00:00 2001 From: Paramtamtam <7326800+tarampampam@users.noreply.github.com> Date: Sun, 2 Feb 2025 14:37:12 +0400 Subject: [PATCH] feat: Helm chart --- deployments/helm/Chart.yaml | 11 + deployments/helm/README.md | 35 +++ deployments/helm/templates/_helpers.tpl | 52 +++++ deployments/helm/templates/deployment.yaml | 194 ++++++++++++++++ deployments/helm/templates/service.yaml | 29 +++ deployments/helm/values.schema.json | 245 +++++++++++++++++++++ deployments/helm/values.yaml | 148 +++++++++++++ 7 files changed, 714 insertions(+) create mode 100644 deployments/helm/Chart.yaml create mode 100644 deployments/helm/README.md create mode 100644 deployments/helm/templates/_helpers.tpl create mode 100644 deployments/helm/templates/deployment.yaml create mode 100644 deployments/helm/templates/service.yaml create mode 100644 deployments/helm/values.schema.json create mode 100644 deployments/helm/values.yaml diff --git a/deployments/helm/Chart.yaml b/deployments/helm/Chart.yaml new file mode 100644 index 0000000..7ee94a2 --- /dev/null +++ b/deployments/helm/Chart.yaml @@ -0,0 +1,11 @@ +# yaml-language-server: $schema=https://json.schemastore.org/chart.json + +apiVersion: v2 +name: proxy-3proxy +description: Tiny free proxy server + +type: application +version: 0.0.0 # will be replaced by the release workflow +appVersion: 0.0.0 # will be replaced by the release workflow +icon: https://github.com/user-attachments/assets/023186cf-b153-459c-8417-038fd87a2065 +sources: [https://github.com/tarampampam/3proxy-docker] diff --git a/deployments/helm/README.md b/deployments/helm/README.md new file mode 100644 index 0000000..36ed41c --- /dev/null +++ b/deployments/helm/README.md @@ -0,0 +1,35 @@ +# 3proxy + +Important note: Since the chart is released together with the app under the same version (i.e., the chart version +matches the app version), its versioning is not compatible with semantic versioning (SemVer). I will do my best to +avoid non-backward-compatible changes in the chart, but due to Murphy's Law, I cannot guarantee that they will +never occur. + +Also, this chart does not include Ingress configuration. If you need it, please, create it manually. + +## Usage + +```shell +helm repo add tarampampam https://tarampampam.github.io/3proxy-docker/helm-charts +helm repo update + +helm install proxy-3proxy tarampampam/proxy-3proxy +``` + +Alternatively, add the following lines to your `Chart.yaml`: + +```yaml +dependencies: + - name: proxy-3proxy + version: + repository: https://tarampampam.github.io/proxy-3proxy/helm-charts +``` + +And override the default values in your `values.yaml`: + +```yaml +proxy-3proxy: + # ... + service: {port: 8800} + # ... +``` diff --git a/deployments/helm/templates/_helpers.tpl b/deployments/helm/templates/_helpers.tpl new file mode 100644 index 0000000..3312e6b --- /dev/null +++ b/deployments/helm/templates/_helpers.tpl @@ -0,0 +1,52 @@ +{{/* Define namespace of chart, useful for multi-namespace deployments */}} +{{- define "proxy-3proxy.namespace" -}} + {{- if .Values.namespaceOverride }} + {{- .Values.namespaceOverride }} + {{- else }} + {{- .Release.Namespace }} + {{- end }} +{{- end }} + +{{/* Expand the name of the chart */}} +{{- define "proxy-3proxy.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "proxy-3proxy.fullname" -}} + {{- if .Values.fullnameOverride }} + {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} + {{- else }} + {{- $name := default .Chart.Name .Values.nameOverride }} + {{- if contains $name .Release.Name }} + {{- .Release.Name | trunc 63 | trimSuffix "-" }} + {{- else }} + {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} + {{- end }} + {{- end }} +{{- end }} + +{{/* Create chart name and version as used by the chart label */}} +{{- define "proxy-3proxy.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* Common labels */}} +{{- define "proxy-3proxy.commonLabels" -}} +helm.sh/chart: {{ include "proxy-3proxy.chart" . }} +{{ include "proxy-3proxy.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* Selector labels */}} +{{- define "proxy-3proxy.selectorLabels" -}} +app.kubernetes.io/name: {{ include "proxy-3proxy.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} diff --git a/deployments/helm/templates/deployment.yaml b/deployments/helm/templates/deployment.yaml new file mode 100644 index 0000000..5869118 --- /dev/null +++ b/deployments/helm/templates/deployment.yaml @@ -0,0 +1,194 @@ +{{- if .Values.deployment.enabled }} +apiVersion: apps/v1 +kind: {{ .Values.deployment.kind | default "Deployment" }} + +metadata: + name: {{ include "proxy-3proxy.fullname" . }} + namespace: {{ template "proxy-3proxy.namespace" . }} + labels: + {{- include "proxy-3proxy.commonLabels" . | nindent 4 }} + +spec: + {{- with .Values.deployment }} + replicas: {{ .replicas | default 1 }} + selector: + matchLabels: + {{- include "proxy-3proxy.selectorLabels" $ | nindent 6 }} + template: + metadata: + {{- with .podAnnotations }} + annotations: + {{- tpl (toYaml .) $ | nindent 8 }} + {{- end }} + labels: + {{- include "proxy-3proxy.commonLabels" $ | nindent 8 }} + {{- with .labels }} + {{- tpl (toYaml .) $ | nindent 8 }} + {{- end }} + spec: + automountServiceAccountToken: false + {{- with .imagePullSecrets }} + imagePullSecrets: + {{- tpl (toYaml .) $ | nindent 8 }} + {{- end }} + containers: + - name: {{ include "proxy-3proxy.fullname" $ }} + + {{- with .securityContext }} + securityContext: + {{- toYaml . | nindent 12 }} + readOnlyRootFilesystem: false + {{- end }} + + {{- with $.Values.image }} + image: "{{ .repository }}:{{ .tag | default $.Chart.AppVersion }}" + imagePullPolicy: {{ .pullPolicy | default "IfNotPresent" }} + {{- end }} + ports: + - name: http + containerPort: 3128 + protocol: TCP + - name: socks + containerPort: 1080 + protocol: TCP + env: + - {name: PROXY_PORT, value: "3128"} + - {name: SOCKS_PORT, value: "1080"} + {{- with $.Values.config }} + {{- with .log }} + {{- $logOutputEnvName := "LOG_OUTPUT" }} + {{- if eq .enabled false }} + - {name: {{ $logOutputEnvName }}, value: "/dev/null"} + {{- else if .output }} + - {name: {{ $logOutputEnvName }}, value: "{{ .output }}"} + {{- end }} + {{- end }} + + {{- with .auth.login }} + {{- $authLoginEnvName := "PROXY_LOGIN" }} + {{- if .plain }} + - {name: {{ $authLoginEnvName }}, value: "{{ .plain }}"} + {{- else if .fromSecret.enabled }} + - name: {{ $authLoginEnvName }} + valueFrom: + secretKeyRef: + name: "{{ tpl (toYaml .fromSecret.secretName) $ }}" + key: "{{ tpl (toYaml .fromSecret.secretKey) $ }}" + {{- else if .fromConfigMap.enabled }} + - name: {{ $authLoginEnvName }} + valueFrom: + configMapKeyRef: + name: "{{ tpl (toYaml .fromConfigMap.configMapName) $ }}" + key: "{{ tpl (toYaml .fromConfigMap.configMapKey) $ }}" + {{- end }} + {{- end }} + + {{- with .auth.password }} + {{- $authPasswordEnvName := "PROXY_PASSWORD" }} + {{- if .plain }} + - {name: {{ $authPasswordEnvName }}, value: "{{ .plain }}"} + {{- else if .fromSecret.enabled }} + - name: {{ $authPasswordEnvName }} + valueFrom: + secretKeyRef: + name: "{{ tpl (toYaml .fromSecret.secretName) $ }}" + key: "{{ tpl (toYaml .fromSecret.secretKey) $ }}" + {{- else if .fromConfigMap.enabled }} + - name: {{ $authPasswordEnvName }} + valueFrom: + configMapKeyRef: + name: "{{ tpl (toYaml .fromConfigMap.configMapName) $ }}" + key: "{{ tpl (toYaml .fromConfigMap.configMapKey) $ }}" + {{- end }} + {{- end }} + + {{- with .auth.extraAccounts }} + {{- $extraAuthAccountsEnvName := "EXTRA_ACCOUNTS" }} + {{- if .plain }} + - name: {{ $extraAuthAccountsEnvName }} + value: >- + {{ .plain | toJson }} + {{- else if .fromSecret.enabled }} + - name: {{ $extraAuthAccountsEnvName }} + valueFrom: + secretKeyRef: + name: "{{ tpl (toYaml .fromSecret.secretName) $ }}" + key: "{{ tpl (toYaml .fromSecret.secretKey) $ }}" + {{- else if .fromConfigMap.enabled }} + - name: {{ $extraAuthAccountsEnvName }} + valueFrom: + configMapKeyRef: + name: "{{ tpl (toYaml .fromConfigMap.configMapName) $ }}" + key: "{{ tpl (toYaml .fromConfigMap.configMapKey) $ }}" + {{- end }} + {{- end }} + + {{- if .dns.primaryResolver }} + - {name: PRIMARY_RESOLVER, value: "{{ .dns.primaryResolver }}"} + {{- end }} + + {{- if .dns.secondaryResolver }} + - {name: SECONDARY_RESOLVER, value: "{{ .dns.secondaryResolver }}"} + {{- end }} + + {{- if ne .limits.maxConnections nil }} + - {name: MAX_CONNECTIONS, value: "{{ .limits.maxConnections }}"} + {{- end }} + + {{- if .extraConfig }} + - name: EXTRA_CONFIG + value: >- + {{ .extraConfig }} + {{- end }} + {{- with $.Values.deployment.env }} + {{- tpl (toYaml .) $ | nindent 12 }} + {{- end }} + {{- end }} + + {{- with .args }} + args: + {{- tpl (toYaml .) $ | nindent 12 }} + {{- end }} + + {{- with .probe }} + livenessProbe: + tcpSocket: {port: "{{ .port }}"} + periodSeconds: {{ .interval }} + initialDelaySeconds: {{ .initialDelay }} + readinessProbe: + tcpSocket: {port: "{{ .port }}"} + periodSeconds: {{ .interval }} + initialDelaySeconds: {{ .initialDelay }} + {{- end }} + + {{- with .resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + + {{- with .volumeMounts }} + volumeMounts: + {{- tpl (toYaml .) $ | nindent 12 }} + {{- end }} + + {{- with .volumes }} + volumes: + {{- tpl (toYaml .) $ | nindent 8 }} + {{- end }} + + {{- with .nodeSelector }} + nodeSelector: + {{- tpl (toYaml .) $ | nindent 8 }} + {{- end }} + + {{- with .affinity }} + affinity: + {{- tpl (toYaml .) $ | nindent 8 }} + {{- end }} + + {{- with .tolerations }} + tolerations: + {{- tpl (toYaml .) $ | nindent 8 }} + {{- end }} + {{- end }} +{{- end }} diff --git a/deployments/helm/templates/service.yaml b/deployments/helm/templates/service.yaml new file mode 100644 index 0000000..77ff0fd --- /dev/null +++ b/deployments/helm/templates/service.yaml @@ -0,0 +1,29 @@ +{{- if .Values.service.enabled }} +apiVersion: v1 +kind: Service + +metadata: + name: {{ include "proxy-3proxy.fullname" . }} + namespace: {{ template "proxy-3proxy.namespace" . }} + labels: + {{- include "proxy-3proxy.commonLabels" . | nindent 4 }} + +spec: + {{- with .Values.service }} + type: {{ .type }} + {{- with .externalName }} + externalName: {{ . }} + {{- end }} + ports: + - name: http + port: {{ .ports.http }} + targetPort: http + protocol: TCP + - name: http + port: {{ .ports.socks }} + targetPort: socks + protocol: TCP + selector: + {{- include "proxy-3proxy.selectorLabels" $ | nindent 4 }} + {{- end }} +{{- end }} diff --git a/deployments/helm/values.schema.json b/deployments/helm/values.schema.json new file mode 100644 index 0000000..4cc6774 --- /dev/null +++ b/deployments/helm/values.schema.json @@ -0,0 +1,245 @@ +{ + "$schema": "https://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "nameOverride": { + "oneOf": [ + {"type": "string", "minLength": 1}, + {"type": "null"} + ] + }, + "fullnameOverride": { + "oneOf": [ + {"type": "string", "minLength": 1}, + {"type": "null"} + ] + }, + "namespaceOverride": { + "oneOf": [ + {"type": "string", "minLength": 1}, + {"type": "null"} + ] + }, + "image": { + "type": "object", + "properties": { + "repository": {"type": "string", "minLength": 1}, + "tag": { + "oneOf": [ + {"type": "string", "minLength": 1}, + {"type": "null"} + ] + }, + "pullPolicy": { + "oneOf": [ + {"type": "string", "enum": ["Always", "IfNotPresent", "Never"]}, + {"type": "null"} + ] + } + } + }, + "deployment": { + "type": "object", + "properties": { + "enabled": {"type": "boolean"}, + "replicas": {"type": "integer"}, + "podAnnotations": { + "type": "object", + "additionalProperties": {"type": "string", "minLength": 1} + }, + "labels": { + "type": "object", + "additionalProperties": {"type": "string", "minLength": 1} + }, + "imagePullSecrets": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": {"type": "string"} + }, + "minProperties": 1 + } + }, + "securityContext": { + "type": "object", + "properties": { + "runAsNonRoot": {"type": "boolean"}, + "runAsUser": {"type": "integer"}, + "runAsGroup": {"type": "integer"} + } + }, + "probe": { + "type": "object", + "properties": { + "interval": {"type": "integer"}, + "initialDelay": {"type": "integer"} + } + }, + "resources": { + "type": "object", + "properties": { + "requests": { + "type": "object", + "properties": { + "cpu": {"type": "string"}, + "memory": {"type": "string"} + } + }, + "limits": { + "type": "object", + "properties": { + "cpu": {"type": "string"}, + "memory": {"type": "string"} + } + } + } + }, + "volumes": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": {"type": "string"}, + "configMap": { + "type": "object", + "properties": { + "name": {"type": "string"} + } + }, + "secret": { + "type": "object", + "properties": { + "secretName": {"type": "string"} + } + }, + "persistentVolumeClaim": { + "type": "object", + "properties": { + "claimName": {"type": "string"} + } + } + } + } + }, + "volumeMounts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": {"type": "string"}, + "mountPath": {"type": "string"}, + "subPath": {"type": "string"}, + "readOnly": {"type": "boolean"} + } + } + }, + "nodeSelector": { + "type": "object", + "additionalProperties": {"type": "string", "minLength": 1} + }, + "affinity": { + "type": "object", + "properties": { + "nodeAffinity": {"type": "object"}, + "podAffinity": {"type": "object"}, + "podAntiAffinity": {"type": "object"} + } + }, + "tolerations": { + "type": "array", + "items": { + "type": "object", + "properties": { + "key": {"type": "string"}, + "operator": {"type": "string"}, + "value": {"type": "string"}, + "effect": {"type": "string"} + } + } + }, + "env": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": {"type": "string"}, + "value": {"type": "string"}, + "valueFrom": {"type": "object"} + } + } + }, + "args": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + } + } + } + }, + "service": { + "type": "object", + "properties": { + "enabled": {"type": "boolean"}, + "type": { + "type": "string", + "enum": ["ClusterIP", "NodePort", "LoadBalancer", "ExternalName"] + }, + "externalName": { + "oneOf": [ + {"type": "string", "minLength": 1}, + {"type": "null"} + ] + }, + "port": {"type": "integer", "minimum": 1, "maximum": 65535} + } + }, + "ingress": { + "type": "object", + "properties": { + "enabled": {"type": "boolean"}, + "className": { + "oneOf": [ + {"type": "string", "minLength": 1}, + {"type": "null"} + ] + }, + "annotations": { + "type": "object", + "additionalProperties": {"type": "string", "minLength": 1} + }, + "hosts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "host": {"type": "string", "minLength": 1}, + "paths": { + "type": "array", + "items": { + "type": "object", + "properties": { + "path": {"type": "string", "minLength": 1}, + "pathType": {"type": "string", "minLength": 1} + } + } + } + } + } + }, + "tls": { + "type": "array", + "items": { + "type": "object", + "properties": { + "hosts": {"type": "array"}, + "secretName": {"type": "string"} + } + } + } + } + }, + "config": {} + } +} diff --git a/deployments/helm/values.yaml b/deployments/helm/values.yaml new file mode 100644 index 0000000..b300377 --- /dev/null +++ b/deployments/helm/values.yaml @@ -0,0 +1,148 @@ +# -- The name of the Helm release +fullnameOverride: null +# -- This is to override the chart name +nameOverride: null +# -- Override the default Release Namespace for Helm +namespaceOverride: null + +image: + # -- The image repository to pull from + repository: ghcr.io/tarampampam/3proxy + # -- Defines the image pull policy + pullPolicy: IfNotPresent + # -- Overrides the image tag whose default is the chart appVersion + tag: null + +deployment: + # -- Enable deployment + enabled: true + # -- The deployment kind + kind: Deployment # TODO: add into schema + # -- How many replicas to run + replicas: 1 + # -- Additional pod annotations (e.g. for mesh injection or prometheus scraping) + # It supports templating. One can set it with values like some/name: '{{ template "some.name" . }}' + # For more information checkout: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ + podAnnotations: {} # supports templating + # -- Additional deployment labels (e.g. for filtering deployment by custom labels) + labels: {} # supports templating + # -- This is for the secretes for pulling an image from a private repository more information can be found + # here: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + imagePullSecrets: [] # supports templating + # -- Security context for the pod, more information can be found here: + # https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#security-context-1 + securityContext: + runAsNonRoot: true + runAsUser: 10001 # as defined in the Dockerfile + runAsGroup: 10001 # as defined in the Dockerfile + probe: + port: http # or socks # TODO: add into schema + # -- How often (in seconds) to perform the probe + interval: 10 + # -- Number of seconds after the container has started before liveness probes are initiated + initialDelay: 2 + # -- Resource limits and requests, more information can be found here: + # https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + resources: + requests: {memory: 16Mi} + limits: {memory: 128Mi} + # -- Additional volumes to add to the pod, more information can be found here: + # https://kubernetes.io/docs/concepts/storage/volumes/ + volumes: [] # supports templating + # -- Additional volumeMounts to add to the container (for instance when using fs storage driver) + volumeMounts: [] # supports templating + # -- Node selector for pod assignment, more information can be found here: + # https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/ + nodeSelector: {} # supports templating + # -- Affinity for pod assignment, more information can be found here: + # https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/ + affinity: {} # supports templating + # -- Tolerations for pod assignment, more information can be found here: + # https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/ + tolerations: [] # supports templating + # -- The list of additional environment variables to set in the container + env: [] # supports templating + # -- The list of additional arguments to pass to the container + args: [] # supports templating + +service: + # -- Enable service + enabled: true + # -- Sets the service type more information can be found here: + # https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types + type: ClusterIP + # -- External name for the service (for type=ExternalName) + externalName: null + # -- Sets the port, more information can be found here: + # https://kubernetes.io/docs/concepts/services-networking/service/#field-spec-ports + ports: # TODO: add into schema + # -- The port number for the proxy to listen on + http: 3128 + # -- The same, but for socks proxy + socks: 1080 + +config: + log: + # -- Enable logging (set to false to disable) + enabled: true + # -- The output log file + # @default /dev/stdout + output: null + + auth: + login: + # -- Username (login) for proxy authentication, provided as a plain value + plain: null + fromSecret: + # -- Enable getting the username from a secret + enabled: false + secretName: null # supports templating + secretKey: null # supports templating + fromConfigMap: + # -- Enable getting the username from a config map + enabled: false + configMapName: null # supports templating + configMapKey: null # supports templating + password: + # -- Password for proxy authentication, provided as a plain value + plain: null + fromSecret: + # -- Enable getting the password from a secret + enabled: false + secretName: null # supports templating + secretKey: null # supports templating + fromConfigMap: + # -- Enable getting the password from a config map + enabled: false + configMapName: null # supports templating + configMapKey: null # supports templating + extraAccounts: + # -- The list of additional accounts to add to the configuration (a hashmap of username:password) + plain: {} + fromSecret: + # -- Enable getting the extra accounts from a secret (the value should be a JSON object) + enabled: false + secretName: null # supports templating + secretKey: null # supports templating + fromConfigMap: + # -- Enable getting the extra accounts from a config map (the value should be a JSON object) + enabled: false + configMapName: null # supports templating + configMapKey: null # supports templating + + dns: + # -- Primary DNS server + # @default 1.0.0.1 (Cloudflare) + primaryResolver: null + # -- Secondary DNS server + # @default 8.8.4.4 (Google) + secondaryResolver: null + + limits: + # -- The maximum number of connections + # @default 1024 + maxConnections: null + + # -- Additional 3proxy configuration (appended to the end of the config file, but before `proxy` and `flush`), + # new lines should be separated by `\n`, i.e.: "# line 1\n# line 2" + extraConfig: null