# ConfigMaps as environment variables

## Goal

Populating environment variables of a container from an entire ConfigMap.

## Design Points

A container can specify a set of existing ConfigMaps to populate environment variables.

There needs to be an easy way to isolate the variables introduced by a given
ConfigMap. The contents of a ConfigMap may not be known in advance and it may
be generated by someone or something else. Services may provide binding
information via a ConfigMap. If you have a common service with multiple
instances like a Message Queue or Database, there needs to be a way to
uniquely identify and prevent collision when consuming multiple ConfigMaps in
a single container using this feature.

## Proposed Design

Containers can specify a set of sources that are consumed as environment
variables. One such source is a ConfigMap.
Each key defined in the ConfigMap's `Data` object must be a "C" identifier. If
an invalid key is present, the container will fail to start.

Environment variables defined by a `Container` are processed in a specific
order.  The processing order is as follows:

1. All automatic service environment variables
1. All `EnvFrom` blocks in order
1. All `Env` blocks in order.

The last value processed for any given environment variable will be the
decided winner.  Variable references defined by an `EnvVar` struct will be
resolved by the current values defined even if the value is replaced later.

To prevent collisions amongst multiple ConfigMaps, each defined ConfigMap can
have an optional associated prefix that is prepended to each key in the
ConfigMap.  Prefixes must be a "C" identifier.

### Kubectl updates

The `describe` command will display the configmap name that have been defined as
part of the environment variable section including the optional prefix when
defined.

### API Resource

A new `EnvFromSource` type containing a `ConfigMapRef` will be added to the
`Container` struct.

```go
// EnvFromSource represents the source of a set of ConfigMaps
type EnvFromSource struct {
  // A string to place in front of every key. Must be a C_IDENTIFIER.
  // +optional
  Prefix string `json:"prefix,omitempty"`
  // The ConfigMap to select from
  ConfigMapRef *LocalObjectReference `json:"configMapRef,omitempty"`
}

type Container struct {
  // List of sources to populate environment variables in the container.
  // The keys defined within a source must be a C_IDENTIFIER. An invalid key
  // will prevent the container from starting. When a key exists in multiple
  // sources, the value associated with the last source will take precedence.
  // Values defined by an Env with a duplicate key will take precedence over 
  // any listed source.
  // Cannot be updated.
  // +optional
  EnvFrom []EnvFromSource `json:"envFrom,omitempty"`
}
```

### Examples

### Consuming `ConfigMap` as Environment Variables

```yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: etcd-env-config
data:
  number_of_members: "1"
  initial_cluster_state: new
  initial_cluster_token: DUMMY_ETCD_INITIAL_CLUSTER_TOKEN
  discovery_token: DUMMY_ETCD_DISCOVERY_TOKEN
  discovery_url: http://etcd_discovery:2379
  etcdctl_peers: http://etcd:2379
  duplicate_key: FROM_CONFIG_MAP
  REPLACE_ME: "a value"
```

This pod consumes the entire `ConfigMap` as environment variables:

```yaml
apiVersion: v1
kind: Pod
metadata:
  name: config-env-example
spec:
  containers:
  - name: etcd
    image: openshift/etcd-20-centos7
    ports:
    - containerPort: 2379
      protocol: TCP
    - containerPort: 2380
      protocol: TCP
    env:
    - name: duplicate_key
      value: FROM_ENV
    - name: expansion
      value: $(REPLACE_ME)
    envFrom:
    - configMapRef:
        name: etcd-env-config
```

The resulting environment variables will be:

```
number_of_members="1"
initial_cluster_state="new"
initial_cluster_token="DUMMY_ETCD_INITIAL_CLUSTER_TOKEN"
discovery_token="DUMMY_ETCD_DISCOVERY_TOKEN"
discovery_url="http://etcd_discovery:2379"
etcdctl_peers="http://etcd:2379"
duplicate_key="FROM_ENV"
expansion="a value"
REPLACE_ME="a value"
```

### Consuming multiple `ConfigMap` as Environment Variables

```yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: env-config
data:
  key1: a
  key2: b
```

This pod consumes the entire `ConfigMap` as environment variables:

```yaml
apiVersion: v1
kind: Pod
metadata:
  name: config-env-example
spec:
  containers:
  - name: etcd
    image: openshift/etcd-20-centos7
    ports:
    - containerPort: 2379
      protocol: TCP
    - containerPort: 2380
      protocol: TCP
    envFrom:
    - prefix: cm1_
      configMapRef:
        name: env-config
    - prefix: cm2_
      configMapRef:
        name: env-config
```

The resulting environment variables will be:

```
cm1_key1="a"
cm1_key2="b"
cm2_key1="a"
cm2_key2="b"
```

### Future

Add similar support for Secrets.

