Substitute Values in String Without Spaces in Helm - unexpected unclosed action in command

10/29/2019

Right now, I am using helm to substitute variables in a file using values from my values.yaml file to create a ConfigMap which is working well for entries that are spaced correctly.

I am having a problem with the following:

CELERY_BROKER_URL = "amqp://admin:admin@{{ .Values.env.RABBITMQ_HOST }}:5672/"

Results in this error when running helm install:

Error: render error in "mynamespace-nonprod-chart/templates/configmap.yaml": template: mynamespace-nonprod-chart/templates/configmap.yaml:10:4: executing "mynamespace-nonprod-chart/templates/configmap.yaml" at <tpl (.Files.Glob "conf/*").AsConfig .>: error calling tpl: Error during tpl function execution for "settings.py: \"\\\"\\\"\\\"\\nDjango settings for imagegateway project.\\n\\nGenerated by 'django-admin\n  startproject' using Django 1.11.3.\\n\\nFor more information on this file, see\\nhttps://docs.djangoproject.com/en/1.11/topics/settings/\\n\\nFor\n  the full list of settings and their values, see\\nhttps://docs.djangoproject.com/en/1.11/ref/settings/\\n\\\"\\\"\\\"\\n\\nimport\n  os\\n\\nfrom kombu import Exchange, Queue\\n\\n\\n# Image Config\\nIMAGE_ASPECT_RATIO\n  = (4,3) # 4:3\\nMLS_BASE_URL = 'http://www.torontomls.net/MLSMULTIPHOTOS/FULL'\\nCALLBACK_KEY\n  = 'SECRET'\\n\\nBASE_URL = 'https://media-dev.mydomain.ca'\\n\\n#\n  Build paths inside the project like this: os.path.join(BASE_DIR, ...)\\nBASE_DIR\n  = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))\\n\\nMAXIMUM_HASH_DISTANCE\n  = 5\\n\\n# SECURITY WARNING: keep the secret key used in production secret!\\nSECRET_KEY\n  = 'mysecretkey'\\n\\n# SECURITY WARNING: don't run with debug turned on in production!\\n#\n  DEBUG = False\\nDEBUG = True\\n\\nALLOWED_HOSTS = ['0.0.0.0', '*']\\n\\n# Celery\\n\\n#\n  {{ .Values.env.INGRESS_CONTROLLER_ENDPOINT }} TEST this\\nCELERY_BROKER_URL = \\\"amqp://admin:xWQ7ye7YotNif@{{\n  .Values.env.RABBITMQ_HOST }}:5672/\\\"\\nCELERY_ACCEPT_CONTENT = ['application/json']\\nCELERY_TASK_SERIALIZER\n  = 'json'\\nCELERY_RESULT_SERIALIZER = 'json'\\nCELERY_RESULT_BACKEND = 'redis://dummyuser:R3dis0nEKS!@redis-imagegateway-dev-shared-headless'\\nCELERY_RESULT_PERSISTENT\n  = False\\nCELERY_TIMEZONE = 'Canada/Eastern'\\nCELERY_ALWAYS_EAGER = False  \\nCELERY_ACKS_LATE\n  = True  \\nCELERYD_PREFETCH_MULTIPLIER = 1\\nCELERY_TASK_PUBLISH_RETRY = True  \\nCELERY_DISABLE_RATE_LIMITS\n  = False\\nCELERY_IMPORTS = ('garden.tasks', 'keeper.tasks', 'tourist.tasks',)\\nCELERY_DEFAULT_QUEUE\n  = 'default'\\nCELERY_DEFAULT_EXCHANGE_TYPE = 'topic'\\nCELERY_DEFAULT_ROUTING_KEY\n  = 'default'\\nCELERYD_TASK_SOFT_TIME_LIMIT = 60\\n\\nCELERY_TASK_QUEUES = (\\n    Queue('tourist',\n  Exchange('tourist'), routing_key='tourist.#', queue_arguments={'x-max-priority':\n  1}),\\n    Queue('default', Exchange('default'), routing_key='default', queue_arguments={'x-max-priority':\n  5}),\\n    Queue('garden', Exchange('garden'), routing_key='garden.#', queue_arguments={'x-max-priority':\n  10}),\\n    Queue('keeper', Exchange('keeper'), routing_key='keeper.#', queue_arguments={'x-max-priority':\n  10}),\\n)\\n\\nCELERY_TASK_ROUTES = {\\n     'garden.tasks.fetch_mls_image' : {'queue':\n  'garden'},\\n     'garden.tasks.save_image' : {'queue': 'garden'},\\n     'garden.tasks.do_callback'\n  : {'queue': 'garden'},\\n     'garden.tasks.clear_cloudflare_cache' : {'queue': 'garden'},\\n\n  \\    'tourist.tasks.get_fetcher' : {'queue': 'tourist'},\\n     'tourist.tasks.get_tour_urls'\n  : {'queue': 'tourist'},\\n     'tourist.tasks.download_tour_images' : {'queue': 'tourist'},\\n\n  \\    'tourist.tasks.smallest_image_size' : {'queue': 'tourist'},\\n     'tourist.tasks.hash_and_compare'\n  : {'queue': 'tourist'},\\n     'tourist.tasks.process_tours' : {'queue': 'tourist'},\\n\n  \\    'tourist.tasks.all_done' : {'queue': 'tourist'},\\n     'tourist.tasks.save_hd_images'\n  : {'queue': 'tourist'},\\n     'keeper.tasks.create_sizes' : {'queue': 'keeper'},\\n\n  \\    'celery_tasks.debug_task': {'queue': 'default'},\\n}\\n\\n# Application definition\\n\\nINSTALLED_APPS\n  = [\\n    # Project\\n    'celery',\\n    'garden', # deals with retrieval of images\\n\n  \\   'keeper', # deals with resizing and image processing tasks\\n    'tourist', #\n  deals with virtual tours\\n    # Vendor Packages\\n    'djangocms_admin_style',\\n\n  \\   # Django\\n    'django.contrib.admin',\\n    'django.contrib.auth',\\n    'django.contrib.contenttypes',\\n\n  \\   'django.contrib.sessions',\\n    'django.contrib.messages',\\n    'django.contrib.staticfiles',\\n]\\n\\nMIDDLEWARE\n  = [\\n    'django.middleware.security.SecurityMiddleware',\\n    'django.contrib.sessions.middleware.SessionMiddleware',\\n\n  \\   'django.middleware.common.CommonMiddleware',\\n    'django.middleware.csrf.CsrfViewMiddleware',\\n\n  \\   'django.contrib.auth.middleware.AuthenticationMiddleware',\\n    'django.contrib.messages.middleware.MessageMiddleware',\\n\n  \\   'django.middleware.clickjacking.XFrameOptionsMiddleware',\\n]\\n\\nROOT_URLCONF\n  = 'imagegateway.urls'\\n\\nTEMPLATES = [\\n    {\\n        'BACKEND': 'django.template.backends.django.DjangoTemplates',\\n\n  \\       'DIRS': ['templates'],\\n        'APP_DIRS': True,\\n        'OPTIONS': {\\n\n  \\           'context_processors': [\\n                'django.template.context_processors.debug',\\n\n  \\               'django.template.context_processors.request',\\n                'django.contrib.auth.context_processors.auth',\\n\n  \\               'django.contrib.messages.context_processors.messages',\\n            ],\\n\n  \\       },\\n    },\\n]\\n\\nWSGI_APPLICATION = 'imagegateway.wsgi.application'\\n\\n\\n#\n  Database\\n\\nDATABASES = {\\n    'default': {\\n        'ENGINE': 'django.db.backends.postgresql',\\n\n  \\       'NAME': 'imagegateway',\\n        'USER': 'postgres',\\n        'PASSWORD':\n  'PostGresQL123',\\n        'HOST': 'postgresql-imagegateway-dev-shared-headless',\\n\n  \\       'PORT': 5432,\\n    }\\n}\\n\\n\\n# Password validation\\n\\nAUTH_PASSWORD_VALIDATORS\n  = [\\n    {\\n        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',\\n\n  \\   },\\n    {\\n        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',\\n\n  \\   },\\n    {\\n        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',\\n\n  \\   },\\n    {\\n        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',\\n\n  \\   },\\n]\\n\\n\\n# Internationalization\\n\\nLANGUAGE_CODE = 'en-us'\\n\\nTIME_ZONE =\n  'UTC'\\n\\nUSE_I18N = True\\n\\nUSE_L10N = True\\n\\nUSE_TZ = True\\n\\n\\n# Static files\n  (CSS, JavaScript, Images)\\n\\nSTATIC_URL = '/static/'\\nSTATIC_ROOT = os.path.join(BASE_DIR,\n  'static/')\\n\\n\\n# Media files (Images)\\n\\nMEDIA_URL = '/media/'\\n\\n# Controls whether\n  Keras and backend is imported every server restart to improve restart times\\n# Only\n  set the first bool!!! Then in production when DEBUG is set to false it will default\n  to True.\\n# Example: \\n#    WITH_IMAGE_CLASSIFIER = True if DEBUG else True \\n#\n  or WITH_IMAGE_CLASSIFIER = False if DEBUG else True\\nWITH_IMAGE_CLASSIFIER = True\n  if DEBUG else True\\n\\n# 20MB\\nFILE_UPLOAD_MAX_MEMORY_SIZE = 20971520\\nDATA_UPLOAD_MAX_MEMORY_SIZE\n  = 20971520\\n\\n# Cloudflare\\nCLOUDFLARE_API_URL = 'https://api.cloudflare.com/client/v4/'\\nCLOUDFLARE_API_KEY\n  = ''\\nCLOUDFLARE_API_EMAIL = ''\\nCLOUDFLARE_ZONE_ID = ''\\n\"\n": parse error in "mynamespace-nonprod-chart/templates/configmap.yaml": template: mynamespace-nonprod-chart/templates/configmap.yaml:12: unexpected unclosed action in command

If I had set env.RABBITMQ_HOST:rabbitmq.mydomain.com, i'd expect the substituted value to be:

CELERY_BROKER_URL = "amqp://admin:admin@rabbitmq.mydomain.com:5672/"

Values:

useCustomApplicatonConfigs:
  - mountPath: /var/www/html/django
    type: py
    name:
      - settings

env:
  CELERY_BROKER_URL: "amqp://admin:xWQ7ye7YotNif@rabbitmq-imagegateway-dev-shared-headless:5672"

Does anyone know the correct syntax for this? Do I need to escape the @ or something?

Edit: Added values section.

-- leeman24
go
kubernetes
kubernetes-helm

1 Answer

10/31/2019

There are two things I had to change to get my configuration to work.

The first issue was that I was using a yaml comments vs. a template comment which tried to get substituted when running helm install so I removed it completely. I should have been using {{- /* This is a comment. */ -}} according to the helm documentation instead of #.

The second issue was with the spaces between the curly braces {{ and }}.

Before:

CELERY_BROKER_URL = "amqp://{{ .Values.env.RABBITMQ_USER }}:{{ .Values.env.RABBITMQ_PASSWORD }}@{{ .Values.env.RABBITMQ_HOST }}:{{ .Values.env.RABBITMQ_PORT }}/"

After (working):

CELERY_BROKER_URL = "amqp://{{.Values.env.RABBITMQ_USER}}:{{.Values.env.RABBITMQ_PASSWORD}}@{{.Values.env.RABBITMQ_HOST}}:{{.Values.env.RABBITMQ_PORT}}/"
-- leeman24
Source: StackOverflow