mirror of
https://gitea.fenix-dev.com/fenix-gitea-admin/iac-ansible-private.git
synced 2026-05-14 00:15:20 +00:00
lingarr correction
This commit is contained in:
@ -17,7 +17,7 @@ spec:
|
|||||||
- name: regcred
|
- name: regcred
|
||||||
containers:
|
containers:
|
||||||
- name: bazarr
|
- name: bazarr
|
||||||
image: lscr.io/linuxserver/bazarr:1.5.3
|
image: lscr.io/linuxserver/bazarr:1.5.6
|
||||||
securityContext:
|
securityContext:
|
||||||
capabilities:
|
capabilities:
|
||||||
add:
|
add:
|
||||||
@ -26,6 +26,8 @@ spec:
|
|||||||
- containerPort: 6767
|
- containerPort: 6767
|
||||||
name: webui
|
name: webui
|
||||||
env:
|
env:
|
||||||
|
- name: BAZARR_DEBUG
|
||||||
|
value: "true"
|
||||||
- name: PUID
|
- name: PUID
|
||||||
value: "1013"
|
value: "1013"
|
||||||
- name: PGID
|
- name: PGID
|
||||||
@ -58,6 +60,10 @@ spec:
|
|||||||
mountPath: /config
|
mountPath: /config
|
||||||
- name: media
|
- name: media
|
||||||
mountPath: /media
|
mountPath: /media
|
||||||
|
- name: lingarrpy
|
||||||
|
mountPath: /app/bazarr/bin/bazarr/subtitles/tools/translate/services/lingarr_translator.py
|
||||||
|
subPath: lingarr_translator.py
|
||||||
|
readOnly: true
|
||||||
volumes:
|
volumes:
|
||||||
- name: config
|
- name: config
|
||||||
persistentVolumeClaim:
|
persistentVolumeClaim:
|
||||||
@ -65,6 +71,9 @@ spec:
|
|||||||
- name: media
|
- name: media
|
||||||
persistentVolumeClaim:
|
persistentVolumeClaim:
|
||||||
claimName: bazarr-media-pvc
|
claimName: bazarr-media-pvc
|
||||||
|
- name: lingarrpy
|
||||||
|
configMap:
|
||||||
|
name: bazarr-lingarrpy-configmap
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
207
roles/bazarr/files/lingarr-configmap.yaml
Normal file
207
roles/bazarr/files/lingarr-configmap.yaml
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: bazarr-lingarrpy-configmap
|
||||||
|
namespace: stack-arr
|
||||||
|
data:
|
||||||
|
lingarr_translator.py: |
|
||||||
|
# coding=utf-8
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import pysubs2
|
||||||
|
import requests
|
||||||
|
|
||||||
|
from retry.api import retry
|
||||||
|
from deep_translator.exceptions import TooManyRequests, RequestError
|
||||||
|
|
||||||
|
from app.config import settings
|
||||||
|
from app.database import TableShows, TableEpisodes, TableMovies, database, select
|
||||||
|
from app.jobs_queue import jobs_queue
|
||||||
|
from languages.custom_lang import CustomLanguage
|
||||||
|
from languages.get_languages import alpha3_from_alpha2, language_from_alpha2, language_from_alpha3
|
||||||
|
from radarr.history import history_log_movie
|
||||||
|
from sonarr.history import history_log
|
||||||
|
from subtitles.processing import ProcessSubtitlesResult
|
||||||
|
from utilities.path_mappings import path_mappings
|
||||||
|
|
||||||
|
from ..core.translator_utils import add_translator_info, create_process_result, get_title
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class LingarrTranslatorService:
|
||||||
|
def __init__(self, source_srt_file, dest_srt_file, lang_obj, to_lang, from_lang, media_type,
|
||||||
|
video_path, orig_to_lang, forced, hi, sonarr_series_id, sonarr_episode_id,
|
||||||
|
radarr_id):
|
||||||
|
self.source_srt_file = source_srt_file
|
||||||
|
self.dest_srt_file = dest_srt_file
|
||||||
|
self.lang_obj = lang_obj
|
||||||
|
self.to_lang = to_lang
|
||||||
|
self.from_lang = from_lang
|
||||||
|
self.media_type = media_type
|
||||||
|
self.video_path = video_path
|
||||||
|
self.orig_to_lang = orig_to_lang
|
||||||
|
self.forced = forced
|
||||||
|
self.hi = hi
|
||||||
|
self.sonarr_series_id = sonarr_series_id
|
||||||
|
self.sonarr_episode_id = sonarr_episode_id
|
||||||
|
self.radarr_id = radarr_id
|
||||||
|
self.language_code_convert_dict = {
|
||||||
|
'zh': 'zh-CN',
|
||||||
|
'zt': 'zh-TW',
|
||||||
|
'pb': 'pt-BR',
|
||||||
|
}
|
||||||
|
|
||||||
|
def translate(self, job_id=None):
|
||||||
|
try:
|
||||||
|
jobs_queue.update_job_progress(job_id=job_id, progress_max=1, progress_message=self.source_srt_file)
|
||||||
|
|
||||||
|
subs = pysubs2.load(self.source_srt_file, encoding='utf-8')
|
||||||
|
lines_list = [x.plaintext for x in subs]
|
||||||
|
lines_list_len = len(lines_list)
|
||||||
|
|
||||||
|
if lines_list_len == 0:
|
||||||
|
logger.debug('No lines to translate in subtitle file')
|
||||||
|
return self.dest_srt_file
|
||||||
|
|
||||||
|
logger.debug(f'Starting translation for {self.source_srt_file}')
|
||||||
|
translated_lines = self._translate_content(lines_list, job_id=job_id)
|
||||||
|
|
||||||
|
if translated_lines is None:
|
||||||
|
logger.error(f'Translation failed for {self.source_srt_file}')
|
||||||
|
jobs_queue.update_job_progress(job_id=job_id,
|
||||||
|
progress_message=f'Translation failed for {self.source_srt_file}')
|
||||||
|
return False
|
||||||
|
|
||||||
|
logger.debug(f'BAZARR saving Lingarr translated subtitles to {self.dest_srt_file}')
|
||||||
|
translation_map = {}
|
||||||
|
for item in translated_lines:
|
||||||
|
if isinstance(item, dict) and 'position' in item and 'line' in item:
|
||||||
|
translation_map[item['position']] = item['line']
|
||||||
|
|
||||||
|
for i, line in enumerate(subs):
|
||||||
|
if i in translation_map and translation_map[i]:
|
||||||
|
line.text = translation_map[i]
|
||||||
|
|
||||||
|
try:
|
||||||
|
subs.save(self.dest_srt_file)
|
||||||
|
add_translator_info(self.dest_srt_file, f"# Subtitles translated with Lingarr # ")
|
||||||
|
except OSError:
|
||||||
|
logger.error(f'BAZARR is unable to save translated subtitles to {self.dest_srt_file}')
|
||||||
|
jobs_queue.update_job_progress(job_id=job_id,
|
||||||
|
progress_message=f'Translation failed: Unable to save translated '
|
||||||
|
f'subtitles to {self.dest_srt_file}')
|
||||||
|
raise OSError
|
||||||
|
|
||||||
|
message = (f"{language_from_alpha2(self.from_lang)} subtitles translated to "
|
||||||
|
f"{language_from_alpha3(self.to_lang)} using Lingarr.")
|
||||||
|
result = create_process_result(message, self.video_path, self.orig_to_lang, self.forced, self.hi,
|
||||||
|
self.dest_srt_file, self.media_type)
|
||||||
|
|
||||||
|
if self.media_type == 'series':
|
||||||
|
history_log(action=6,
|
||||||
|
sonarr_series_id=self.sonarr_series_id,
|
||||||
|
sonarr_episode_id=self.sonarr_episode_id,
|
||||||
|
result=result)
|
||||||
|
else:
|
||||||
|
history_log_movie(action=6,
|
||||||
|
radarr_id=self.radarr_id,
|
||||||
|
result=result)
|
||||||
|
|
||||||
|
jobs_queue.update_job_progress(job_id=job_id, progress_value='max')
|
||||||
|
|
||||||
|
return self.dest_srt_file
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f'BAZARR encountered an error during Lingarr translation: {str(e)}')
|
||||||
|
jobs_queue.update_job_progress(job_id=job_id, progress_message=f'Lingarr translation failed: {str(e)}')
|
||||||
|
return False
|
||||||
|
|
||||||
|
@retry(exceptions=(TooManyRequests, RequestError, requests.exceptions.RequestException), tries=3, delay=1,
|
||||||
|
backoff=2, jitter=(0, 1))
|
||||||
|
def _translate_content(self, lines_list, job_id):
|
||||||
|
try:
|
||||||
|
source_lang = self.language_code_convert_dict.get(self.from_lang, self.from_lang)
|
||||||
|
target_lang = self.language_code_convert_dict.get(self.orig_to_lang, self.orig_to_lang)
|
||||||
|
|
||||||
|
lines_payload = []
|
||||||
|
for i, line in enumerate(lines_list):
|
||||||
|
lines_payload.append({
|
||||||
|
"position": i,
|
||||||
|
"line": line if line and line.strip() else 'a'
|
||||||
|
})
|
||||||
|
|
||||||
|
title = get_title(
|
||||||
|
media_type=self.media_type,
|
||||||
|
radarr_id=self.radarr_id,
|
||||||
|
sonarr_series_id=self.sonarr_series_id,
|
||||||
|
sonarr_episode_id=self.sonarr_episode_id
|
||||||
|
)
|
||||||
|
|
||||||
|
if self.media_type == 'series':
|
||||||
|
api_media_type = "Episode"
|
||||||
|
arr_media_id = self.sonarr_series_id or 0
|
||||||
|
else:
|
||||||
|
api_media_type = "Movie"
|
||||||
|
arr_media_id = self.radarr_id or 0
|
||||||
|
|
||||||
|
payload = {
|
||||||
|
"arrMediaId": arr_media_id,
|
||||||
|
"title": title,
|
||||||
|
"sourceLanguage": source_lang,
|
||||||
|
"targetLanguage": target_lang,
|
||||||
|
"mediaType": api_media_type,
|
||||||
|
"lines": lines_payload
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug(f'BAZARR is sending {len(lines_payload)} lines to Lingarr with full media context')
|
||||||
|
|
||||||
|
headers = {"Content-Type": "application/json"}
|
||||||
|
if settings.translator.lingarr_token:
|
||||||
|
headers["X-Api-Key"] = settings.translator.lingarr_token
|
||||||
|
|
||||||
|
response = requests.post(
|
||||||
|
f"{settings.translator.lingarr_url}/api/translate/content",
|
||||||
|
json=payload,
|
||||||
|
headers=headers,
|
||||||
|
timeout=1800
|
||||||
|
)
|
||||||
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
translated_batch = response.json()
|
||||||
|
# Validate response
|
||||||
|
if isinstance(translated_batch, list):
|
||||||
|
for item in translated_batch:
|
||||||
|
if not isinstance(item, dict) or 'position' not in item or 'line' not in item:
|
||||||
|
logger.error(f'Invalid response format from Lingarr API: {item}')
|
||||||
|
return None
|
||||||
|
return translated_batch
|
||||||
|
else:
|
||||||
|
logger.error(f'Unexpected response format from Lingarr API: {translated_batch}')
|
||||||
|
return None
|
||||||
|
elif response.status_code == 401:
|
||||||
|
raise RequestError("Authentication failed: Invalid or missing API key")
|
||||||
|
elif response.status_code == 429:
|
||||||
|
raise TooManyRequests("Rate limit exceeded")
|
||||||
|
elif response.status_code >= 500:
|
||||||
|
raise RequestError(f"Server error: {response.status_code}")
|
||||||
|
else:
|
||||||
|
logger.debug(f'Lingarr API error: {response.status_code} - {response.text}')
|
||||||
|
return None
|
||||||
|
|
||||||
|
except requests.exceptions.Timeout:
|
||||||
|
logger.debug('Lingarr API request timed out')
|
||||||
|
raise RequestError("Request timed out")
|
||||||
|
except requests.exceptions.ConnectionError:
|
||||||
|
logger.debug('Lingarr API connection error')
|
||||||
|
raise RequestError("Connection error")
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
logger.debug(f'Lingarr API request failed: {str(e)}')
|
||||||
|
raise
|
||||||
|
except (TooManyRequests, RequestError) as e:
|
||||||
|
logger.error(f'Lingarr API error after retries: {str(e)}')
|
||||||
|
jobs_queue.update_job_progress(job_id=job_id, progress_message=f'Lingarr API error: {str(e)}')
|
||||||
|
raise
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f'Unexpected error in Lingarr translation: {str(e)}')
|
||||||
|
jobs_queue.update_job_progress(job_id=job_id, progress_message=f'Translation error: {str(e)}')
|
||||||
BIN
roles/bazarr/files/teste.yml
Normal file
BIN
roles/bazarr/files/teste.yml
Normal file
Binary file not shown.
24
roles/lingarr/files/lingarr-configmap.yaml
Normal file
24
roles/lingarr/files/lingarr-configmap.yaml
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: lingarr-configmap
|
||||||
|
namespace: stack-arr
|
||||||
|
data:
|
||||||
|
Lingarr.Server.runtimeconfig.json: |
|
||||||
|
{
|
||||||
|
"runtimeOptions": {
|
||||||
|
"tfm": "net9.0",
|
||||||
|
"frameworks": [
|
||||||
|
{ "name": "Microsoft.NETCore.App", "version": "9.0.0" },
|
||||||
|
{ "name": "Microsoft.AspNetCore.App", "version": "9.0.0" }
|
||||||
|
],
|
||||||
|
"configProperties": {
|
||||||
|
"System.GC.Server": true,
|
||||||
|
"System.Globalization.Invariant": false,
|
||||||
|
"System.Reflection.Metadata.MetadataUpdater.IsSupported": false,
|
||||||
|
"System.Reflection.NullabilityInfoContext.IsSupported": true,
|
||||||
|
"System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false,
|
||||||
|
"Npgsql.EnableLegacyTimestampBehavior": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -15,10 +15,12 @@ spec:
|
|||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: lingarr
|
- name: lingarr
|
||||||
image: lingarr/lingarr:latest
|
image: lingarr/lingarr:main
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 9876
|
- containerPort: 9876
|
||||||
env:
|
env:
|
||||||
|
- name: TZ
|
||||||
|
value: "UTC"
|
||||||
- name: ASPNETCORE_URLS
|
- name: ASPNETCORE_URLS
|
||||||
value: "http://+:9876"
|
value: "http://+:9876"
|
||||||
- name: WHISPER_BASE_URL
|
- name: WHISPER_BASE_URL
|
||||||
@ -27,10 +29,48 @@ spec:
|
|||||||
value: "auto"
|
value: "auto"
|
||||||
- name: TARGET_LANGUAGE
|
- name: TARGET_LANGUAGE
|
||||||
value: "pt"
|
value: "pt"
|
||||||
|
- name: DB_CONNECTION
|
||||||
|
value: postgresql
|
||||||
|
- name: DB_HOST
|
||||||
|
value: 'stolon-proxy-service.postgresql.svc.cluster.local'
|
||||||
|
- name: DB_PORT
|
||||||
|
value: '5432'
|
||||||
|
- name: DB_USERNAME
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: lingarr-secret
|
||||||
|
key: username
|
||||||
|
- name: DB_PASSWORD
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: lingarr-secret
|
||||||
|
key: password
|
||||||
|
- name: DB_DATABASE
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: lingarr-secret
|
||||||
|
key: maindb
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
- name: config
|
- name: config
|
||||||
mountPath: /app/config
|
mountPath: /app/config
|
||||||
|
- name: runtimeconfig
|
||||||
|
mountPath: /app/Lingarr.Server.runtimeconfig.json
|
||||||
|
subPath: Lingarr.Server.runtimeconfig.json
|
||||||
|
readOnly: true
|
||||||
|
- name: tv
|
||||||
|
mountPath: /tv
|
||||||
|
- name: anime
|
||||||
|
mountPath: /anime
|
||||||
volumes:
|
volumes:
|
||||||
- name: config
|
- name: config
|
||||||
persistentVolumeClaim:
|
persistentVolumeClaim:
|
||||||
claimName: lingarr-config-pvc
|
claimName: lingarr-config-pvc
|
||||||
|
- name: runtimeconfig
|
||||||
|
configMap:
|
||||||
|
name: lingarr-configmap
|
||||||
|
- name: tv
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: sonarr-tv-pvc
|
||||||
|
- name: anime
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: sonarr-anime-pvc
|
||||||
|
|||||||
10
roles/lingarr/files/lingarr-secret.yaml
Normal file
10
roles/lingarr/files/lingarr-secret.yaml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: lingarr-secret
|
||||||
|
namespace: stack-arr
|
||||||
|
type: Opaque
|
||||||
|
data:
|
||||||
|
username: dXNlcm5hbWU=
|
||||||
|
password: cGFzc3dvcmQ=
|
||||||
|
maindb: bWFpbmRiLXByb3dsYXJy
|
||||||
@ -18,18 +18,18 @@
|
|||||||
mode: '0644'
|
mode: '0644'
|
||||||
|
|
||||||
|
|
||||||
#- name: Obter várias notas do Bitwarden
|
- name: Obter várias notas do Bitwarden
|
||||||
# shell: |
|
shell: |
|
||||||
# echo "unlock"
|
echo "unlock"
|
||||||
# BW_SESSION=$(bw unlock {{ bw_password }} --raw)
|
BW_SESSION=$(bw unlock {{ bw_password }} --raw)
|
||||||
# echo "get item"
|
echo "get item"
|
||||||
# bw get item "{{ item.id }}" --session $BW_SESSION | jq -r '.notes' > {{ item.dest }}
|
bw get item "{{ item.id }}" --session $BW_SESSION | jq -r '.notes' > {{ item.dest }}
|
||||||
# loop:
|
loop:
|
||||||
# - { id: "iac.ansible.stackarr.radarr.secret", dest: "/tmp/stack-arr/radarr/kubernetes-files/files/radarr-secret.yaml" }
|
- { id: "iac.ansible.stackarr.lingarr.secret", dest: "/tmp/stack-arr/lingarr/kubernetes-files/files/lingarr-secret.yaml" }
|
||||||
# args:
|
args:
|
||||||
# executable: /bin/bash
|
executable: /bin/bash
|
||||||
# environment:
|
environment:
|
||||||
# BW_PASSWORD: "{{ BW_PASSWORD }}"
|
BW_PASSWORD: "{{ BW_PASSWORD }}"
|
||||||
|
|
||||||
|
|
||||||
- name: Listar conteúdo do diretório remoto
|
- name: Listar conteúdo do diretório remoto
|
||||||
|
|||||||
@ -1,18 +1,18 @@
|
|||||||
- name: Remover o diretório /tmp/stack-arr/lidarr/kubernetes-files
|
- name: Remover o diretório /tmp/stack-arr/soularr/kubernetes-files
|
||||||
ansible.builtin.file:
|
ansible.builtin.file:
|
||||||
path: /tmp/stack-arr/lidarr/kubernetes-files
|
path: /tmp/stack-arr/soularr/kubernetes-files
|
||||||
state: absent
|
state: absent
|
||||||
|
|
||||||
- name: Criar diretório temporário no remoto
|
- name: Criar diretório temporário no remoto
|
||||||
file:
|
file:
|
||||||
path: /tmp/stack-arr/lidarr/kubernetes-files
|
path: /tmp/stack-arr/soularr/kubernetes-files
|
||||||
state: directory
|
state: directory
|
||||||
mode: '0755'
|
mode: '0755'
|
||||||
|
|
||||||
- name: Copy file with owner and permissions
|
- name: Copy file with owner and permissions
|
||||||
ansible.builtin.copy:
|
ansible.builtin.copy:
|
||||||
src: ../files
|
src: ../files
|
||||||
dest: /tmp/stack-arr/lidarr/kubernetes-files
|
dest: /tmp/stack-arr/soularr/kubernetes-files
|
||||||
owner: fenix
|
owner: fenix
|
||||||
group: root
|
group: root
|
||||||
mode: '0644'
|
mode: '0644'
|
||||||
@ -25,7 +25,7 @@
|
|||||||
echo "get item"
|
echo "get item"
|
||||||
bw get item "{{ item.id }}" --session $BW_SESSION | jq -r '.notes' > {{ item.dest }}
|
bw get item "{{ item.id }}" --session $BW_SESSION | jq -r '.notes' > {{ item.dest }}
|
||||||
loop:
|
loop:
|
||||||
- { id: "iac.ansible.stackarr.lidarr.secret", dest: "/tmp/stack-arr/lidarr/kubernetes-files/files/lidarr-secret.yaml" }
|
- { id: "iac.ansible.stackarr.soularr.secret", dest: "/tmp/stack-arr/soularr/kubernetes-files/files/soularr-secret.yaml" }
|
||||||
args:
|
args:
|
||||||
executable: /bin/bash
|
executable: /bin/bash
|
||||||
environment:
|
environment:
|
||||||
@ -33,7 +33,7 @@
|
|||||||
|
|
||||||
|
|
||||||
- name: Listar conteúdo do diretório remoto
|
- name: Listar conteúdo do diretório remoto
|
||||||
shell: ls -l /tmp/stack-arr/lidarr/kubernetes-files/files
|
shell: ls -l /tmp/stack-arr/soularr/kubernetes-files/files
|
||||||
register: resultado_ls
|
register: resultado_ls
|
||||||
|
|
||||||
|
|
||||||
@ -46,6 +46,6 @@
|
|||||||
become: yes
|
become: yes
|
||||||
become_user: fenix
|
become_user: fenix
|
||||||
shell: |
|
shell: |
|
||||||
kubectl apply -f /tmp/stack-arr/lidarr/kubernetes-files/files/
|
kubectl apply -f /tmp/stack-arr/soularr/kubernetes-files/files/
|
||||||
environment:
|
environment:
|
||||||
KUBECONFIG: /home/fenix/.kube/config
|
KUBECONFIG: /home/fenix/.kube/config
|
||||||
@ -60,6 +60,8 @@ spec:
|
|||||||
env:
|
env:
|
||||||
- name: SLSKD_FLAGS_NO_SQLITE_POOLING
|
- name: SLSKD_FLAGS_NO_SQLITE_POOLING
|
||||||
value: "true"
|
value: "true"
|
||||||
|
- name: SLSKD_FLAGS_VOLATILE_AGENT_TOKEN
|
||||||
|
value: "true"
|
||||||
- name: TZ
|
- name: TZ
|
||||||
value: 'Etc/UTC'
|
value: 'Etc/UTC'
|
||||||
- name: UID
|
- name: UID
|
||||||
@ -70,7 +72,7 @@ spec:
|
|||||||
- name: app
|
- name: app
|
||||||
mountPath: /app
|
mountPath: /app
|
||||||
- name: media
|
- name: media
|
||||||
mountPath: /data
|
mountPath: /downloads
|
||||||
volumes:
|
volumes:
|
||||||
- name: app
|
- name: app
|
||||||
persistentVolumeClaim:
|
persistentVolumeClaim:
|
||||||
|
|||||||
Reference in New Issue
Block a user