<p>I'm having trouble accessing a Kubernetes environment variable in my python app's init.py file.  It appears to be available in other files, however.  </p>

<p>My init.py file includes this code <code>app.config.from_object(os.environ['APP_SETTINGS'])</code>. The value of <code>APP_SETTINGS</code> depends on my environment with values being <code>config.DevelopmentConfig</code>, <code>config.StagingConfig</code> or <code>config.ProductionConfig</code>.  From here, my app pulls configs from my config.py file, which looks like this:</p>

<pre><code>import os
basedir = os.path.abspath(os.path.dirname(__file__))

class Config(object):
  WTF_CSRF_ENABLED = True
  SECRET_KEY = 'you-will-never-guess'
  APP_SETTINGS = os.environ['APP_SETTINGS'] # For debug purposes


class DevelopmentConfig(Config):
  TEMPLATES_AUTO_RELOAD = True
  DEBUG = True

class StagingConfig(Config):
  DEBUG = True

class ProductionConfig(Config):
  DEBUG = False
</code></pre>

<p>When I set APP_SETTINGS locally in my dev environment in my docker-compose, like so...</p>

<pre><code>environment:
  - APP_SETTINGS=config.DevelopmentConfig
</code></pre>

<p>everything works just fine.  When I deploy to my Staging pod in Kubernetes with <code>APP_SETTINGS=config.StagingConfig</code> set in my Secrets file, I'm greeted with the following error:</p>

<pre><code>Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/werkzeug/utils.py", line 434, in import_string
    return getattr(module, obj_name)
AttributeError: module 'config' has no attribute 'StagingConfig
'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/werkzeug/utils.py", line 436, in import_string
    raise ImportError(e)
ImportError: module 'config' has no attribute 'StagingConfig
'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
  File "manage.py", line 3, in &lt;module&gt;
    from app import app
  File "/root/app/__init__.py", line 11, in &lt;module&gt;
    app.config.from_object(os.environ['APP_SETTINGS'])
  File "/usr/local/lib/python3.6/site-packages/flask/config.py", line 168, in from_object
    obj = import_string(obj)
  File "/usr/local/lib/python3.6/site-packages/werkzeug/utils.py", line 443, in import_string
    sys.exc_info()[2])
  File "/usr/local/lib/python3.6/site-packages/werkzeug/_compat.py", line 137, in reraise
    raise value.with_traceback(tb)
  File "/usr/local/lib/python3.6/site-packages/werkzeug/utils.py", line 436, in import_string
    raise ImportError(e)
werkzeug.utils.ImportStringError: import_string() failed for 'config.StagingConfig\n'. Possible reasons are:
- missing __init__.py in a package;
- package or module path not included in sys.path;
- duplicated package or module name taking precedence in sys.path;
- missing module, class, function or variable;
Debugged import:
- 'config' found in '/root/config.py'.
- 'config.StagingConfig\n' not found.
Original exception:
ImportError: module 'config' has no attribute 'StagingConfig
'
upgrading database schema...
Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/werkzeug/utils.py", line 434, in import_string
    return getattr(module, obj_name)
AttributeError: module 'config' has no attribute 'StagingConfig
'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/werkzeug/utils.py", line 436, in import_string
    raise ImportError(e)
ImportError: module 'config' has no attribute 'StagingConfig
'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
  File "manage.py", line 3, in &lt;module&gt;
    from app import app
  File "/root/app/__init__.py", line 11, in &lt;module&gt;
    app.config.from_object(os.environ['APP_SETTINGS'])
  File "/usr/local/lib/python3.6/site-packages/flask/config.py", line 168, in from_object
    obj = import_string(obj)
  File "/usr/local/lib/python3.6/site-packages/werkzeug/utils.py", line 443, in import_string
    sys.exc_info()[2])
  File "/usr/local/lib/python3.6/site-packages/werkzeug/_compat.py", line 137, in reraise
    raise value.with_traceback(tb)
  File "/usr/local/lib/python3.6/site-packages/werkzeug/utils.py", line 436, in import_string
    raise ImportError(e)
werkzeug.utils.ImportStringError: import_string() failed for 'config.StagingConfig\n'. Possible reasons are:
- missing __init__.py in a package;
- package or module path not included in sys.path;
- duplicated package or module name taking precedence in sys.path;
- missing module, class, function or variable;
Debugged import:
- 'config' found in '/root/config.py'.
- 'config.StagingConfig\n' not found.
Original exception:
ImportError: module 'config' has no attribute 'StagingConfig
'
starting metriculous web server...
Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/werkzeug/utils.py", line 434, in import_string
    return getattr(module, obj_name)
AttributeError: module 'config' has no attribute 'StagingConfig
'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/werkzeug/utils.py", line 436, in import_string
    raise ImportError(e)
ImportError: module 'config' has no attribute 'StagingConfig
'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
  File "manage.py", line 3, in &lt;module&gt;
    from app import app
  File "/root/app/__init__.py", line 11, in &lt;module&gt;
    app.config.from_object(os.environ['APP_SETTINGS'])
  File "/usr/local/lib/python3.6/site-packages/flask/config.py", line 168, in from_object
    obj = import_string(obj)
  File "/usr/local/lib/python3.6/site-packages/werkzeug/utils.py", line 443, in import_string
    sys.exc_info()[2])
  File "/usr/local/lib/python3.6/site-packages/werkzeug/_compat.py", line 137, in reraise
    raise value.with_traceback(tb)
  File "/usr/local/lib/python3.6/site-packages/werkzeug/utils.py", line 436, in import_string
    raise ImportError(e)
werkzeug.utils.ImportStringError: import_string() failed for 'config.StagingConfig\n'. Possible reasons are:
- missing __init__.py in a package;
- package or module path not included in sys.path;
- duplicated package or module name taking precedence in sys.path;
- missing module, class, function or variable;
Debugged import:
- 'config' found in '/root/config.py'.
- 'config.StagingConfig\n' not found.
Original exception:
ImportError: module 'config' has no attribute 'StagingConfig
</code></pre>

<p>However, when I hard code the APP_SETTINGS value in my init.py file like so <code>app.config.from_object('config.StagingConfig')</code> and deploy to Kubernetes, it works fine.  When I do it this way, I can even confirm that my APP_SETTINGS env var declared in my Settings in Kubernetes exists by logging into my pod and running <code>echo $APP_SETTINGS</code>.</p>

<p>Any thoughts about what I'm doing wrong?</p>

<p>EDIT #1 - Adding my deployment.yaml file</p>

<pre><code>kind: Deployment
apiVersion: apps/v1beta2
metadata:
  annotations:
    deployment.kubernetes.io/revision: '4'
  selfLink: /apis/apps/v1beta2/namespaces/tools/deployments/met-staging-myapp
  resourceVersion: '51731234'
  name: met-staging-myapp
  uid: g1fce905-1234-56y4-9c15-12de61100d0a
  creationTimestamp: '2018-01-29T17:22:14Z'
  generation: 6
  namespace: tools
  labels:
    app: myapp
    chart: myapp-1.0.1
    heritage: Tiller
    release: met-staging
spec:
  replicas: 1
  selector:
    matchLabels:
      app: myapp
      release: met-staging
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: myapp
        release: met-staging
    spec:
      containers:
        - name: myapp-web
          image: 'gitlab.ourdomain.com:4567/ourspace/myapp:web-latest'
          ports:
            - containerPort: 80
              protocol: TCP
          env:
            - name: APP_SETTINGS
              valueFrom:
                secretKeyRef:
                  name: myapp-creds
                  key: APP_SETTINGS
            - name: AWS_ACCESS_KEY_ID
              valueFrom:
                secretKeyRef:
                  name: myapp-creds
                  key: AWS_ACCESS_KEY_ID
            - name: AWS_SECRET_ACCESS_KEY
              valueFrom:
                secretKeyRef:
                  name: myapp-creds
                  key: AWS_SECRET_ACCESS_KEY
          resources: {}
          terminationMessagePath: /dev/termination-log
          terminationMessagePolicy: File
          imagePullPolicy: Always
        - name: myapp-celery
          image: 'gitlab.ourdomain.com:4567/ourspace/myapp:celery-latest'
          env:
            - name: APP_SETTINGS
              valueFrom:
                secretKeyRef:
                  name: myapp-creds
                  key: APP_SETTINGS
            - name: AWS_ACCESS_KEY_ID
              valueFrom:
                secretKeyRef:
                  name: myapp-creds
                  key: AWS_ACCESS_KEY_ID
            - name: AWS_SECRET_ACCESS_KEY
              valueFrom:
                secretKeyRef:
                  name: myapp-creds
                  key: AWS_SECRET_ACCESS_KEY
          resources: {}
          terminationMessagePath: /dev/termination-log
          terminationMessagePolicy: File
          imagePullPolicy: Always
        - name: rabbit
          image: 'rabbitmq:alpine'
          env:
            - name: RABBITMQ_DEFAULT_USER
              value: rabbit_user
            - name: RABBITMQ_DEFAULT_PASS
              value: fake_pw
          resources: {}
          terminationMessagePath: /dev/termination-log
          terminationMessagePolicy: File
          imagePullPolicy: IfNotPresent
      restartPolicy: Always
      terminationGracePeriodSeconds: 30
      dnsPolicy: ClusterFirst
      securityContext: {}
      imagePullSecrets:
        - name: gitlab-registry
      schedulerName: default-scheduler
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1
      maxSurge: 1
  revisionHistoryLimit: 10
  progressDeadlineSeconds: 600
status:
  observedGeneration: 6
  replicas: 1
  updatedReplicas: 1
  readyReplicas: 1
  availableReplicas: 1
  conditions:
    - type: Available
      status: 'True'
      lastUpdateTime: '2018-01-29T17:22:14Z'
      lastTransitionTime: '2018-01-29T17:22:14Z'
      reason: MinimumReplicasAvailable
      message: Deployment has minimum availability.
    - type: Progressing
      status: 'True'
      lastUpdateTime: '2018-05-25T10:20:49Z'
      lastTransitionTime: '2018-02-16T20:29:45Z'
      reason: NewReplicaSetAvailable
      message: &gt;-
        ReplicaSet "met-staging-myapp-2615c4545f" has successfully
        progressed.
</code></pre>

<blockquote>
  <p><code>werkzeug.utils.ImportStringError: import_string() failed for 'config.StagingConfig\n'. Possible reasons are:</code></p>
</blockquote>

<p>It very clearly shows you that the module name has a trailing newline character, which is a very, very, very common error for people who try to <code>echo something | base64</code> and put that value into a kubernetes <code>Secret</code>. The <em>correct</em> way of doing that is either via <code>kubectl create secret generic myapp-creds --from-literal=APP_SETTINGS=config.StagingConfig</code>, or <code>printf '%s' config.StagingConfig | base64</code>. Or, of course, stop putting non-Secret text into a Secret and using either a <code>ConfigMap</code> or just a traditional environment <code>value: config.StagingConfig</code> setting, and reserve the <code>Secret</code> construct for <code>Secret</code> values.</p>
