Skip to content

Commit

Permalink
Make deployments configurable (#59)
Browse files Browse the repository at this point in the history
* Update charts and configuration to optionally disable services

* Fix default configuration
Fix typos from #56

* Update charts to support multiple neon-core ingresses
Update CLI entrypoint to make iris Gradio UI deployments optional

* Add prompt to set image tag in core setup

* Add method to read configured backend domain for default iris endpoint

* Add LLMs to backend configuration checks

* Update chart versioning and push to Helm repo

* Update tests to account for added LLMs

* Update logging to clearly print disabled MQ and HTTP backend services

---------

Co-authored-by: Daniel McKnight <[email protected]>
  • Loading branch information
NeonDaniel and NeonDaniel authored Jan 23, 2024
1 parent 9290ab0 commit 77ea21d
Show file tree
Hide file tree
Showing 24 changed files with 292 additions and 100 deletions.
134 changes: 120 additions & 14 deletions neon_diana_utils/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@

from enum import Enum
from pprint import pformat
from typing import Optional
from typing import Optional, Set
from os import makedirs, listdir
from os.path import expanduser, join, abspath, isfile, isdir, dirname
from ovos_utils.xdg_utils import xdg_config_home
Expand Down Expand Up @@ -503,6 +503,61 @@ def _get_chatbots_mq_config(rmq_config: str) -> dict:
return {"MQ": chatbot_config}


def _get_unconfigured_mq_backend_services(config: dict) -> Set[str]:
"""
Get a list of MQ Backend services that are not configured to run
@param config: dict Configuration (diana.yaml)
"""
config_to_service = {'keys.api_services': 'neon-api-proxy',
'keys.emails': 'neon-email-proxy',
'keys.track_my_brands': 'neon-brands-service',
'LLM_CHAT_GPT': 'neon-llm-chatgpt',
'LLM_FASTCHAT': 'neon-llm-fastchat',
'LLM_CLAUDE': 'neon-llm-claude',
'LLM_GEMINI': 'neon-llm-gemini',
'LLM_PALM2': 'neon-llm-palm'}
disabled = list()
for key, service in config_to_service.items():
if '.' in key:
parts = key.split('.')
test = dict(config)
for part in parts:
test = test.get(part)
if not test:
disabled.append(service)
else:
if not config.get(key):
disabled.append(service)
return set(disabled)


def _get_optional_http_backend() -> Set[str]:
"""
Get a set of optional HTTP backend services
"""
return {'tts-larynx', 'tts-ljspeech', 'tts-mozilla', 'tts-nancy',
'tts-glados', 'ww-snowboy'}


def _read_backend_domain(deploy_path: str) -> str:
"""
Read backend configuration to determine the configured root domain
@param deploy_path: Path to diana-backend deployment
"""
if isdir(join(deploy_path, "diana-backend")):
deploy_path = join(deploy_path, "diana-backend")
values_file = join(deploy_path, "values.yaml")
if not isfile(values_file):
raise FileNotFoundError(values_file)
with open(values_file, 'r') as f:
backend_config = yaml.safe_load(f)
root_domain = backend_config.get('backend', {}).get('diana-http',
{}).get('domain')
if not root_domain:
raise ValueError(f"Expected config not found in: {values_file}")
return root_domain


def configure_backend(username: str = None,
password: str = None,
output_path: str = None,
Expand All @@ -523,6 +578,11 @@ def configure_backend(username: str = None,
click.echo(f"Path exists: {output_path}")
return

# Collect user inputs for service configuration
keys_config = make_keys_config(False)
disabled_mq_services = list(
_get_unconfigured_mq_backend_services(keys_config))

if orchestrator == Orchestrator.KUBERNETES:
shutil.copytree(join(dirname(__file__), "templates", "backend"),
join(output_path, "diana-backend"))
Expand All @@ -541,6 +601,8 @@ def configure_backend(username: str = None,
else:
# Define a default value so secret can be generated
encoded_token = get_github_encoded_auth("", "")
to_disable = ['neon-brands-service', 'neon-script-parser']
disabled_mq_services += to_disable
confirmed = False
email = ''
domain = ''
Expand All @@ -557,13 +619,30 @@ def configure_backend(username: str = None,
'tag': tag}))
confirmed = click.confirm("Is this configuration correct?")

click.echo(f"The following MQ services are disabled: "
f"{disabled_mq_services}")

if click.confirm(f"Disable optional HTTP Services?"):
disabled_http_services = _get_optional_http_backend()
click.echo(f"The following HTTP services are disabled: "
f"{disabled_http_services}")
else:
disabled_http_services = set()

# Generate values.yaml with configured params
values_file = join(output_path, "diana-backend", "values.yaml")
with open(values_file, 'r') as f:
helm_values = yaml.safe_load(f)
helm_values['backend']['letsencrypt']['email'] = email
helm_values['backend']['diana-http']['domain'] = domain
helm_values['backend']['ghTokenEncoded'] = encoded_token
for service in disabled_mq_services:
LOG.debug(f"Disable {service}")
helm_values['backend']['diana-mq'][service]['replicaCount'] = 0
for service in disabled_http_services:
LOG.debug(f"Disable {service}")
helm_values['backend']['diana-http'].setdefault(service, dict())
helm_values['backend']['diana-http'][service]['replicaCount'] = 0
for service in helm_values['backend']['diana-mq']:
helm_values['backend']['diana-mq'][service]['image']['tag'] = \
tag
Expand Down Expand Up @@ -591,7 +670,6 @@ def configure_backend(username: str = None,
click.echo(f"Generated auth for services: {set(mq_auth_config.keys())}")

# Generate `diana.yaml` output
keys_config = make_keys_config(False)
if keys_config.get("LLM_CHAT_GPT"):
llm_config = make_llm_bot_config()
else:
Expand Down Expand Up @@ -692,23 +770,51 @@ def configure_neon_core(mq_user: str = None,
return

# Prompt for IRIS Web UI configuration
confirmed = False
iris_domain = "iris.diana.k8s" # TODO: Read from backend config
while not confirmed:
iris_domain = click.prompt("Hostname for Iris Web UI", type=str,
default=iris_domain)
confirmed = click.confirm(f"Is {iris_domain} correct?")
iris_domain = None
if click.confirm("Configure IRIS Gradio Web UI?"):
confirmed = False
try:
domain = _read_backend_domain(output_path)
except FileNotFoundError:
domain = "diana.k8s"
while not confirmed:
iris_domain = click.prompt("Hostname for Iris Gradio Web UI",
type=str, default=f"iris.{domain}")
confirmed = click.confirm(f"Is {iris_domain} correct?")

if orchestrator == Orchestrator.KUBERNETES:
shutil.copytree(join(dirname(__file__), "templates", "neon"),
join(output_path, "neon-core"))
neon_config_file = join(output_path, "neon-core", "neon.yaml")
# TODO: Configure image tag to use

# Determine image tag to use
tag = 'latest'
confirmed = False
while not confirmed:
tag = click.prompt("Image tags to use for Neon Core Services",
type=str, default=tag)
confirmed = click.confirm(f"Is `{tag}` correct?")
values = join(output_path, "neon-core", "values.yaml")
with open(values, "r") as f:
config = yaml.safe_load(f)
config['iris']['subdomain'], config['iris']['domain'] =\
iris_domain.split('.', 1)
for service in {'neon-messagebus', 'neon-speech', 'neon-skills',
'neon-audio', 'neon-enclosure', 'neon-gui',
'iris-gradio'}:
config['core'][service]['image']['tag'] = tag

if iris_domain:
iris_subdomain, iris_domain = iris_domain.split('.', 1)
config['core']['domain'] = iris_domain
config['core']['ingress']['rules'].append(
{'host': iris_subdomain, 'serviceName': 'neon-core-iris',
'servicePort': 7860})
else:
click.echo("iris Gradio UI disabled")
config['core']['iris-gradio']['replicaCount'] = 0

if not config['core']['ingress']['rules']:
click.echo('No HTTP ingress configured')
config['core']['ingress']['enabled'] = False
with open(values, 'w') as f:
yaml.safe_dump(config, f)
elif orchestrator == Orchestrator.COMPOSE:
Expand Down Expand Up @@ -817,7 +923,7 @@ def configure_klat_chat(external_url: str = None,

forward_www = False
if subdomain != "www":
forward_www = click.prompt(f"Route www.{domain} traffic to Klat?")
forward_www = click.confirm(f"Route www.{domain} traffic to Klat?")

# Get Libretranslate HTTP API URL
libretranslate_url = "https://libretranslate.2022.us"
Expand Down Expand Up @@ -914,10 +1020,10 @@ def configure_klat_chat(external_url: str = None,
'servicePort': 8010}
]
if forward_www:
helm_values['klat']['ingress']['rules'].append({
helm_values['klat']['ingress']['rules'].append(
{'host': 'www', 'serviceName': 'klat-chat-client',
'servicePort': 8001}
})
)
with open(join(output_path, "klat-chat", "values.yaml"), 'w') as f:
yaml.safe_dump(helm_values, f)
else:
Expand Down
6 changes: 3 additions & 3 deletions neon_diana_utils/helm_charts/backend/diana-backend/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ 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.1.22
version: 0.1.23

# 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
Expand All @@ -32,8 +32,8 @@ dependencies:
version: 11.13.0
repository: https://charts.bitnami.com/bitnami
- name: diana-http
version: 0.0.11
version: 0.0.12
repository: file://../http-services
- name: diana-mq
version: 0.0.16
version: 0.0.17
repository: file://../mq-services
10 changes: 5 additions & 5 deletions neon_diana_utils/helm_charts/backend/http-services/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: diana-http
description: Deploy DIANA HTTP Services

type: application
version: 0.0.11
version: 0.0.12
appVersion: "1.0.1a19"
dependencies:
- name: libretranslate
Expand All @@ -14,10 +14,10 @@ dependencies:
alias: tts-coqui
version: 0.0.5
repository: file://../../http/tts-coqui
# - name: tts-glados
# alias: tts-glados
# version: 0.0.1
# repository: https://neongeckocom.github.io/neon-diana-utils
- name: tts-glados
alias: tts-glados
version: 0.0.1
repository: https://neongeckocom.github.io/neon-diana-utils
- name: tts-larynx
alias: tts-larynx
version: 0.0.5
Expand Down
19 changes: 10 additions & 9 deletions neon_diana_utils/helm_charts/backend/http-services/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,13 @@ tts-coqui:
ingress:
enabled: False
# image.tag:
#tts-glados:
# subdomain: &host_glados glados
# servicePort: *default_port
# ingress:
# enabled: False
# image.tag:
tts-glados:
replicaCount: 0
subdomain: &host_glados glados
servicePort: *default_port
ingress:
enabled: False
image.tag:
tts-larynx:
subdomain: &host_larynx larynx
servicePort: *default_port
Expand Down Expand Up @@ -69,9 +70,9 @@ ingress:
- host: *host_coqui
serviceName: backend-coqui
servicePort: *default_port
# - host: *host_glados
# serviceName: tts-glados
# servicePort: *default_port
- host: *host_glados
serviceName: tts-glados
servicePort: *default_port
- host: *host_larynx
serviceName: backend-larynx
servicePort: *default_port
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: diana-mq
description: Deploy DIANA MQ Services

type: application
version: 0.0.16
version: 0.0.17
appVersion: "1.0.1a19"
dependencies:
- name: neon-api-proxy
Expand Down
6 changes: 6 additions & 0 deletions neon_diana_utils/helm_charts/backend/mq-services/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,9 @@ neon-metrics-service:
neon-script-parser:
image:
tag: *tag
neon-llm-chatgpt:
image:
tag: *tag
neon-llm-fastchat:
image:
tag: *tag
2 changes: 1 addition & 1 deletion neon_diana_utils/helm_charts/base/base-neon/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ name: base-neon
description: Library chart for basic Neon Core Services

type: library
version: 0.0.4
version: 0.0.5
appVersion: "1.0.1a19"
40 changes: 40 additions & 0 deletions neon_diana_utils/helm_charts/base/base-neon/templates/_ingress.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{{- define "base-neon.ingress" -}}
{{ if .Values.ingress.enabled }}
{{- $domain := .Values.domain -}}
{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}}
apiVersion: networking.k8s.io/v1
{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}}
apiVersion: networking.k8s.io/v1beta1
{{- else -}}
apiVersion: extensions/v1beta1
{{- end }}
kind: Ingress
metadata:
name: ingress-iris-http
annotations:
kubernetes.io/ingress.class: {{ .Values.ingress.ingressClassName }}
cert-manager.io/issuer: {{ .Values.ingress.certIssuer }}
spec:
tls:
- secretName: {{ .Values.ingress.tlsSecretName }}
hosts:
{{- range .Values.ingress.rules }}
- {{ printf "%s.%s " .host $.Values.domain }}
{{- end }}

rules:
{{- range .Values.ingress.rules }}
- host: {{ printf "%s.%s " .host $.Values.domain }}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: {{ .serviceName }}
port:
number: {{ .servicePort }}
{{- end }}

{{- end -}}
{{- end -}}
Loading

0 comments on commit 77ea21d

Please sign in to comment.