adding initial webapp chart templates

This commit is contained in:
Andy Pack 2024-05-20 17:04:27 +01:00
parent f8a44d2072
commit 1fe9145b65
Signed by: sarsoo
GPG Key ID: A55BA3536A5E0ED7
15 changed files with 750 additions and 0 deletions

2
charts/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
out
**/*.tgz

View File

@ -0,0 +1,9 @@
dependencies:
- name: postgresql
repository: https://charts.bitnami.com/bitnami
version: 15.3.3
- name: redis
repository: https://charts.bitnami.com/bitnami
version: 19.3.4
digest: sha256:bfff4efd6bc50e38e59637dfc3298b8340ff02f0169a65f3c8ec4c1c08f510fb
generated: "2024-05-20T12:20:55.905121+01:00"

View File

@ -0,0 +1,39 @@
apiVersion: v2
name: Selector
description: Helm charts for the Selector Spotify monitoring system
home: https://selector.sarsoo.xyz
# A chart can be either an 'application' or a 'library' chart.
#
# Application charts are a collection of templates that can be packaged into versioned archives
# to be deployed.
#
# Library charts provide useful utilities or functions for the chart developer. They're included as
# a dependency of application charts to inject those utilities and functions into the rendering
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
type: application
# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 0.0.1
# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using.
appVersion: 1.0.0
dependencies:
- name: postgresql
version: 15.3.3
repository: https://charts.bitnami.com/bitnami
condition: postgresql.enabled
- name: redis
version: 19.3.4
repository: https://charts.bitnami.com/bitnami
condition: redis.enabled
maintainers: # (optional)
- name: Andy Pack
email: andy@sarsoo.xyz
url: https://sarsoo.xyz

View File

@ -0,0 +1,23 @@
apiVersion: v2
name: listener
description: A Helm chart for the Selector Listener backend
# A chart can be either an 'application' or a 'library' chart.
#
# Application charts are a collection of templates that can be packaged into versioned archives
# to be deployed.
#
# Library charts provide useful utilities or functions for the chart developer. They're included as
# a dependency of application charts to inject those utilities and functions into the rendering
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
type: application
# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 0.0.1
# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using.
appVersion: latest

View File

@ -0,0 +1,23 @@
apiVersion: v2
name: webapp
description: A Helm chart for the Selector Web App
# A chart can be either an 'application' or a 'library' chart.
#
# Application charts are a collection of templates that can be packaged into versioned archives
# to be deployed.
#
# Library charts provide useful utilities or functions for the chart developer. They're included as
# a dependency of application charts to inject those utilities and functions into the rendering
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
type: application
# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 0.0.1
# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using.
appVersion: latest

View File

@ -0,0 +1,111 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Expand the name of the chart.
*/}}
{{- define "webapp.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 "webapp.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 }}
{{/*
Allow the release namespace to be overridden for multi-namespace deployments in combined charts
*/}}
{{- define "webapp.namespace" -}}
{{- if .Values.namespaceOverride }}
{{- .Values.namespaceOverride }}
{{- else }}
{{- .Release.Namespace }}
{{- end }}
{{- end }}
{{/*
Create the name of the service account
*/}}
{{- define "webapp.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "webapp.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "webapp.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Selector labels
*/}}
{{- define "webapp.selectorLabels" -}}
app.kubernetes.io/name: {{ include "webapp.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "webapp.labels" -}}
helm.sh/chart: {{ include "webapp.chart" . }}
{{ include "webapp.selectorLabels" . }}
{{- if or .Chart.AppVersion .Values.image.tag }}
app.kubernetes.io/version: {{ mustRegexReplaceAllLiteral "@sha.*" .Values.image.tag "" | default .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- with .Values.extraLabels }}
{{ toYaml . }}
{{- end }}
{{- end }}
{{/*
Return the appropriate apiVersion for ingress.
*/}}
{{- define "webapp.ingress.apiVersion" -}}
{{- if and ($.Capabilities.APIVersions.Has "networking.k8s.io/v1") (semverCompare ">= 1.19-0" .Capabilities.KubeVersion.Version) }}
{{- print "networking.k8s.io/v1" }}
{{- else if $.Capabilities.APIVersions.Has "networking.k8s.io/v1beta1" }}
{{- print "networking.k8s.io/v1beta1" }}
{{- else }}
{{- print "extensions/v1beta1" }}
{{- end }}
{{- end }}
{{/*
Return if ingress is stable.
*/}}
{{- define "webapp.ingress.isStable" -}}
{{- eq (include "webapp.ingress.apiVersion" .) "networking.k8s.io/v1" }}
{{- end }}
{{/*
Return if ingress supports ingressClassName.
*/}}
{{- define "webapp.ingress.supportsIngressClassName" -}}
{{- or (eq (include "webapp.ingress.isStable" .) "true") (and (eq (include "webapp.ingress.apiVersion" .) "networking.k8s.io/v1beta1") (semverCompare ">= 1.18-0" .Capabilities.KubeVersion.Version)) }}
{{- end }}
{{/*
Return if ingress supports pathType.
*/}}
{{- define "webapp.ingress.supportsPathType" -}}
{{- or (eq (include "webapp.ingress.isStable" .) "true") (and (eq (include "webapp.ingress.apiVersion" .) "networking.k8s.io/v1beta1") (semverCompare ">= 1.18-0" .Capabilities.KubeVersion.Version)) }}
{{- end }}

View File

@ -0,0 +1,46 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: "{{ include "webapp.fullname" . }}-appsettings"
namespace: {{ include "webapp.namespace" . }}
labels:
{{- include "webapp.labels" . | nindent 4 }}
{{- with .Values.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
data:
appsettings.json: |-
{
"Logging": {
"LogLevel": {
"Default": "Trace",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"Selector": {
"ClientId": "{{ .Values.spotify.clientId }}",
"ClientSecret": "{{ .Values.spotify.clientSecret }}",
"SpotifyCallback": "{{ .Values.spotify.spotifyCallback }}",
"LastfmClient": "{{ .Values.lastfmClient }}",
"Redis": {
"enabled": {{ .Values.redis.enabled }},
"connectionstring": "{{ .Values.redis.connectionString }}"
},
"Now": {
"ArtistResampleWindow": "14.00:00:00",
"AlbumResampleWindow": "14.00:00:00",
"TrackResampleWindow": "14.00:00:00"
}
},
"Jwt": {
"Issuer": "{{ .Values.jwt.issuer }}",
"Audience": "{{ .Values.jwt.audience }}",
"Key": "{{ .Values.jwt.key }}"
},
"ConnectionStrings": {
"Default": "{{ .Values.databaseConnectionString }}"
},
"AllowedHosts": "*"
}

View File

@ -0,0 +1,65 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "webapp.fullname" . }}-nlog
namespace: {{ include "webapp.namespace" . }}
labels:
{{- include "webapp.labels" . | nindent 4 }}
{{- with .Values.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
data:
nlog.config: |-
<?xml version="1.0" encoding="utf-8" ?>
<!-- XSD manual extracted from package NLog.Schema: https://www.nuget.org/packages/NLog.Schema-->
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xsi:schemaLocation="NLog NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
autoReload="true"
throwConfigExceptions="true"
internalLogFile="./log/selector.nlog.log"
internalLogLevel="Info" >
<extensions>
<add assembly="NLog.Web.AspNetCore"/>
</extensions>
<variable name="scopeFormat"
value="${all-event-properties:format=[[key]\:[value]]:includeScopeProperties=true:separator= }"/>
<variable name="format"
value="${longdate}|${level:uppercase=true}|${callsite}:${callsite-linenumber}|${message}${onexception:inner=${newline}}${exception:format=tostring,data:exceptionDataSeparator=\r\n}${newline} ${scopeFormat}"/>
<!-- the targets to write to -->
<targets>
<!-- write logs to file -->
<target xsi:type="File"
name="logfile"
fileName="./log/selector-${shortdate}.log"
layout="${format}" />
<target xsi:type="File"
name="tracefile"
fileName="./log/selector.trace-${shortdate}.log"
layout="${format}" />
<target xsi:type="ColoredConsole"
name="logconsole"
layout="${format}" />
</targets>
<!-- rules to map from logger name to target -->
<rules>
<!--<logger name="*" minlevel="Trace" writeTo="tracefile" />-->
<logger name="Selector.*" minlevel="Debug" writeTo="logconsole" />
<!--Output hosting lifetime messages to console target for faster startup detection -->
<logger name="Microsoft.Hosting.Lifetime" minlevel="Info" writeTo="logconsole" final="true" />
<!--Skip non-critical Microsoft logs and so log only own logs (BlackHole) -->
<logger name="Microsoft.*" maxlevel="Info" final="true" />
<logger name="System.Net.Http.*" maxlevel="Info" final="true" />
<!--<logger name="*" minlevel="Debug" writeTo="logfile" />-->
<logger name="Selector.*" minlevel="Info" writeTo="logfile" />
<logger name="Microsoft.*" minlevel="Warning" writeTo="logfile" />
</rules>
</nlog>

View File

@ -0,0 +1,111 @@
{{- if (not .Values.useStatefulSet) }}
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "webapp.fullname" . }}
namespace: {{ include "webapp.namespace" . }}
labels:
{{- include "webapp.labels" . | nindent 4 }}
{{- with .Values.labels }}
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .Values.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- if and (not .Values.autoscaling.enabled) (.Values.replicas) }}
replicas: {{ .Values.replicas }}
{{- end }}
revisionHistoryLimit: {{ .Values.revisionHistoryLimit }}
selector:
matchLabels:
{{- include "webapp.selectorLabels" . | nindent 6 }}
{{- with .Values.deploymentStrategy }}
strategy:
{{- toYaml . | trim | nindent 4 }}
{{- end }}
template:
metadata:
labels:
{{- include "webapp.selectorLabels" . | nindent 8 }}
{{- with .Values.podLabels }}
{{- toYaml . | nindent 8 }}
{{- end }}
annotations:
checksumNlog: {{ include (print $.Template.BasePath "/configmap.nlog.yaml") . | sha256sum }}
checksumAppsettings: {{ include (print $.Template.BasePath "/configmap.appsettings.yaml") . | sha256sum }}
spec:
serviceAccountName: {{ include "webapp.serviceAccountName" . }}
{{- with .Values.securityContext }}
securityContext:
{{- toYaml . | nindent 8 }}
{{- end }}
volumes:
- name: appsettings-volume
configMap:
name: {{ include "webapp.fullname" . }}-appsettings
- name: nlog-volume
configMap:
name: {{ include "webapp.fullname" . }}-nlog
containers:
- name: {{ .Chart.Name }}
{{- $registry := .Values.global.imageRegistry | default .Values.image.registry -}}
{{- if .Values.image.sha }}
image: "{{ $registry }}/{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}@sha256:{{ .Values.image.sha }}"
{{- else }}
image: "{{ $registry }}/{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
{{- end }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
{{- with .Values.containerSecurityContext }}
securityContext:
{{- toYaml . | nindent 12 }}
{{- end }}
ports:
- name: {{ .Values.podPortName }}
containerPort: {{ .Values.service.targetPort }}
protocol: TCP
{{- range $key, $value := .Values.env }}
- name: "{{ tpl $key $ }}"
value: "{{ tpl (print $value) $ }}"
{{- end }}
volumeMounts:
- name: appsettings-volume
mountPath: /app/appsettings.json
subPath: appsettings.json
- name: nlog-volume
mountPath: /app/nlog.config
subPath: nlog.config
livenessProbe:
exec:
command:
- touch
- alive
periodSeconds: 30
readinessProbe:
httpGet:
path: /health
port: {{ .Values.service.targetPort }}
periodSeconds: 30
{{- with .Values.resources }}
resources:
{{- toYaml . | nindent 6 }}
{{- end }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- end }}

View File

@ -0,0 +1,78 @@
{{- if .Values.ingress.enabled -}}
{{- $ingressApiIsStable := eq (include "webapp.ingress.isStable" .) "true" -}}
{{- $ingressSupportsIngressClassName := eq (include "grafana.ingress.supportsIngressClassName" .) "true" -}}
{{- $ingressSupportsPathType := eq (include "webapp.ingress.supportsPathType" .) "true" -}}
{{- $fullName := include "webapp.fullname" . -}}
{{- $servicePort := .Values.service.port -}}
{{- $ingressPath := .Values.ingress.path -}}
{{- $ingressPathType := .Values.ingress.pathType -}}
{{- $extraPaths := .Values.ingress.extraPaths -}}
apiVersion: {{ include "webapp.ingress.apiVersion" . }}
kind: Ingress
metadata:
name: {{ $fullName }}
namespace: {{ include "webapp.namespace" . }}
labels:
{{- include "webapp.labels" . | nindent 4 }}
{{- with .Values.ingress.labels }}
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .Values.ingress.annotations }}
annotations:
{{- range $key, $value := . }}
{{ $key }}: {{ tpl $value $ | quote }}
{{- end }}
{{- end }}
spec:
{{- if and $ingressSupportsIngressClassName .Values.ingress.ingressClassName }}
ingressClassName: {{ .Values.ingress.ingressClassName }}
{{- end -}}
{{- with .Values.ingress.tls }}
tls:
{{- tpl (toYaml .) $ | nindent 4 }}
{{- end }}
rules:
{{- if .Values.ingress.hosts }}
{{- range .Values.ingress.hosts }}
- host: {{ tpl . $ | quote }}
http:
paths:
{{- with $extraPaths }}
{{- toYaml . | nindent 10 }}
{{- end }}
- path: {{ $ingressPath }}
{{- if $ingressSupportsPathType }}
pathType: {{ $ingressPathType }}
{{- end }}
backend:
{{- if $ingressApiIsStable }}
service:
name: {{ $fullName }}
port:
number: {{ $servicePort }}
{{- else }}
serviceName: {{ $fullName }}
servicePort: {{ $servicePort }}
{{- end }}
{{- end }}
{{- else }}
- http:
paths:
- backend:
{{- if $ingressApiIsStable }}
service:
name: {{ $fullName }}
port:
number: {{ $servicePort }}
{{- else }}
serviceName: {{ $fullName }}
servicePort: {{ $servicePort }}
{{- end }}
{{- with $ingressPath }}
path: {{ . }}
{{- end }}
{{- if $ingressSupportsPathType }}
pathType: {{ $ingressPathType }}
{{- end }}
{{- end -}}
{{ end }}

View File

@ -0,0 +1,61 @@
{{- if .Values.service.enabled }}
{{- $root := . }}
apiVersion: v1
kind: Service
metadata:
name: {{ include "webapp.fullname" . }}
namespace: {{ include "webapp.namespace" . }}
labels:
{{- include "webapp.labels" . | nindent 4 }}
{{- with .Values.service.labels }}
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .Values.service.annotations }}
annotations:
{{- tpl (toYaml . | nindent 4) $root }}
{{- end }}
spec:
{{- if (or (eq .Values.service.type "ClusterIP") (empty .Values.service.type)) }}
type: ClusterIP
{{- with .Values.service.clusterIP }}
clusterIP: {{ . }}
{{- end }}
{{- else if eq .Values.service.type "LoadBalancer" }}
type: LoadBalancer
{{- with .Values.service.loadBalancerIP }}
loadBalancerIP: {{ . }}
{{- end }}
{{- with .Values.service.loadBalancerClass }}
loadBalancerClass: {{ . }}
{{- end }}
{{- with .Values.service.loadBalancerSourceRanges }}
loadBalancerSourceRanges:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- else }}
type: {{ .Values.service.type }}
{{- end }}
{{- with .Values.service.externalIPs }}
externalIPs:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .Values.service.externalTrafficPolicy }}
externalTrafficPolicy: {{ . }}
{{- end }}
ports:
- name: {{ .Values.service.portName }}
port: {{ .Values.service.port }}
protocol: TCP
targetPort: {{ .Values.service.targetPort }}
{{- with .Values.service.appProtocol }}
appProtocol: {{ . }}
{{- end }}
{{- if (and (eq .Values.service.type "NodePort") (not (empty .Values.service.nodePort))) }}
nodePort: {{ .Values.service.nodePort }}
{{- end }}
{{- with .Values.extraExposePorts }}
{{- tpl (toYaml . | nindent 4) $root }}
{{- end }}
selector:
{{- include "webapp.selectorLabels" . | nindent 4 }}
{{- end }}

View File

@ -0,0 +1,15 @@
{{- if .Values.serviceAccount.create -}}
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ include "webapp.serviceAccountName" . }}
labels:
{{- include "webapp.labels" . | nindent 4 }}
{{- with .Values.serviceAccount.labels }}
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .Values.serviceAccount.annotations }}
annotations:
{{- tpl (toYaml . | nindent 4) $ }}
{{- end }}
{{- end }}

View File

@ -0,0 +1,159 @@
replicaCount: 1
global:
# -- Overrides the Docker registry globally for all images
imageRegistry: null
image:
# -- The Docker registry
registry: docker.io
# -- Docker image repository
repository: sarsoo/selector-web
# Overrides the Grafana image tag whose default is the chart appVersion
tag: ""
sha: ""
pullPolicy: Always
nameOverride: ""
fullnameOverride: ""
serviceAccount:
create: true
name:
nameTest:
## ServiceAccount labels.
labels: {}
## Service account annotations. Can be templated.
annotations: {}
## Create HorizontalPodAutoscaler object for deployment type
#
autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 5
targetCPU: "60"
targetMemory: ""
behavior: {}
service:
enabled: true
type: ClusterIP
loadBalancerIP: ""
loadBalancerClass: ""
loadBalancerSourceRanges: []
port: 80
targetPort: 8080
# targetPort: 4181 To be used with a proxy extraContainer
## Service annotations. Can be templated.
annotations: {}
labels: {}
portName: service
# Adds the appProtocol field to the service. This allows to work with istio protocol selection. Ex: "http" or "tcp"
appProtocol: ""
ingress:
enabled: false
# For Kubernetes >= 1.18 you should specify the ingress-controller via the field ingressClassName
# See https://kubernetes.io/blog/2020/04/02/improvements-to-the-ingress-api-in-kubernetes-1.18/#specifying-the-class-of-an-ingress
# ingressClassName: nginx
# Values can be templated
annotations: {}
# kubernetes.io/ingress.class: nginx
# kubernetes.io/tls-acme: "true"
labels: {}
path: /
# pathType is only for k8s >= 1.1=
pathType: Prefix
hosts:
- chart-example.local
## Extra paths to prepend to every host configuration. This is useful when working with annotation based services.
extraPaths: []
# - path: /*
# backend:
# serviceName: ssl-redirect
# servicePort: use-annotation
## Or for k8s > 1.19
# - path: /*
# pathType: Prefix
# backend:
# service:
# name: ssl-redirect
# port:
# name: use-annotation
tls: []
# - secretName: chart-example-tls
# hosts:
# - chart-example.local
containerSecurityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
seccompProfile:
type: RuntimeDefault
podPortName: webapp
## Number of old ReplicaSets to retain
##
revisionHistoryLimit: 10
## Override the deployment namespace
##
namespaceOverride: ""
# Apply extra labels to common labels.
extraLabels: {}
env: {}
resources: {}
# limits:
# cpu: 100m
# memory: 128Mi
# requests:
# cpu: 100m
# memory: 128Mi
## Node labels for pod assignment
## ref: https://kubernetes.io/docs/user-guide/node-selection/
#
nodeSelector: {}
## Tolerations for pod assignment
## ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/
##
tolerations: []
## Affinity for pod assignment (evaluated as template)
## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity
##
affinity: {}
# securityContext:
# runAsNonRoot: true
# runAsUser: 472
# runAsGroup: 472
# fsGroup: 472
databaseConnectionString: ""
spotify:
clientId: ""
clientSecret: ""
spotifyCallback: "https://localhost:5001/api/spotify/callback"
lastfmClient: ""
redis:
enabled: false
connectionString: ""
jwt:
key: ""
issuer: ""
audience: ""

View File

@ -0,0 +1,7 @@
postgresql:
enabled: false
redis:
enabled: false

1
charts/template.sh Executable file
View File

@ -0,0 +1 @@
helm template testrelease selector --output-dir './out' --debug