Add env_file + ConfigMaps feature to Kompose

When using env_file with Docker Compose, a ConfigMap will be generated

For example:

```sh
▶ ./kompose convert -f
script/test/fixtures/configmaps/docker-compose.yml
INFO Kubernetes file "redis-service.yaml" created
INFO Kubernetes file "redis-deployment.yaml" created
INFO Kubernetes file "foo-env-configmap.yaml" created
INFO Kubernetes file "bar-env-configmap.yaml" created
```

File:

```yaml
version: '3'

services:
  redis:
    image: 'bitnami/redis:latest'
    environment:
      - ALLOW_EMPTY_PASSWORD=no
    # Env file will override environment / warn!
    env_file:
      - "foo.env"
      - bar.env
    labels:
      kompose.service.type: nodeport
    ports:
      - '6379:6379'
```

To:

```yaml
apiVersion: v1
data:
  ALLOW_EMPTY_PASSWORD: "yes"
kind: ConfigMap
metadata:
  creationTimestamp: null
  name: foo-env
```

```yaml
...
      - env:
        - name: ALLOW_EMPTY_PASSWORD
          valueFrom:
            configMapKeyRef:
              key: ALLOW_EMPTY_PASSWORD
              name: foo-env
```
This commit is contained in:
Charlie Drage
2017-10-10 12:40:49 -04:00
parent 935e90febc
commit f4bfe1fcb5
11 changed files with 295 additions and 14 deletions
+32 -2
View File
@@ -22,6 +22,7 @@ import (
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
"reflect"
"strconv"
@@ -31,6 +32,7 @@ import (
log "github.com/Sirupsen/logrus"
"github.com/ghodss/yaml"
"github.com/joho/godotenv"
"github.com/kubernetes/kompose/pkg/kobject"
"github.com/kubernetes/kompose/pkg/transformer"
@@ -330,9 +332,13 @@ func (k *Kubernetes) CreateHeadlessService(name string, service kobject.ServiceC
}
// UpdateKubernetesObjects loads configurations to k8s objects
func (k *Kubernetes) UpdateKubernetesObjects(name string, service kobject.ServiceConfig, objects *[]runtime.Object) error {
func (k *Kubernetes) UpdateKubernetesObjects(name string, service kobject.ServiceConfig, opt kobject.ConvertOptions, objects *[]runtime.Object) error {
// Configure the environment variables.
envs := k.ConfigEnvs(name, service)
envs, err := k.ConfigEnvs(name, service, opt)
if err != nil {
return errors.Wrap(err, "Unable to load env variables")
}
// Configure the container volumes.
volumesMount, volumes, pvc, err := k.ConfigVolumes(name, service)
@@ -570,3 +576,27 @@ func DurationStrToSecondsInt(s string) (*int64, error) {
r := (int64)(duration.Seconds())
return &r, nil
}
func GetEnvsFromFile(file string, opt kobject.ConvertOptions) (map[string]string, error) {
// Get the correct file context / directory
composeDir, err := transformer.GetComposeFileDir(opt.InputFiles)
if err != nil {
return nil, errors.Wrap(err, "Unable to load file context")
}
fileLocation := path.Join(composeDir, file)
// Load environment variables from file
envLoad, err := godotenv.Read(fileLocation)
if err != nil {
return nil, errors.Wrap(err, "Unable to read env_file")
}
return envLoad, nil
}
func FormatEnvName(name string) string {
envName := strings.Trim(name, "./")
envName = strings.Replace(envName, ".", "-", -1)
envName = strings.Replace(envName, "/", "-", -1)
return envName
}
+91 -8
View File
@@ -158,6 +158,33 @@ func (k *Kubernetes) InitSvc(name string, service kobject.ServiceConfig) *api.Se
return svc
}
// InitConfigMap initialized a ConfigMap object
func (k *Kubernetes) InitConfigMap(name string, service kobject.ServiceConfig, opt kobject.ConvertOptions, envFile string) *api.ConfigMap {
envs, err := GetEnvsFromFile(envFile, opt)
if err != nil {
log.Fatalf("Unable to retrieve env file: %s", err)
}
// Remove root pathing
// replace all other slashes / preiods
envName := FormatEnvName(envFile)
// In order to differentiate files, we append to the name and remove '.env' if applicate from the file name
configMap := &api.ConfigMap{
TypeMeta: unversioned.TypeMeta{
Kind: "ConfigMap",
APIVersion: "v1",
},
ObjectMeta: api.ObjectMeta{
Name: envName,
},
Data: envs,
}
return configMap
}
// InitD initializes Kubernetes Deployment object
func (k *Kubernetes) InitD(name string, service kobject.ServiceConfig, replicas int) *extensions.Deployment {
dc := &extensions.Deployment{
@@ -476,19 +503,59 @@ func (k *Kubernetes) ConfigPVCVolumeSource(name string, readonly bool) *api.Volu
}
// ConfigEnvs configures the environment variables.
func (k *Kubernetes) ConfigEnvs(name string, service kobject.ServiceConfig) []api.EnvVar {
func (k *Kubernetes) ConfigEnvs(name string, service kobject.ServiceConfig, opt kobject.ConvertOptions) ([]api.EnvVar, error) {
envs := transformer.EnvSort{}
for _, v := range service.Environment {
envs = append(envs, api.EnvVar{
Name: v.Name,
Value: v.Value,
})
// If there is an env_file, use ConfigMaps and ignore the environment variables
// already specified
if len(service.EnvFile) > 0 {
// Load each env_file
for _, file := range service.EnvFile {
envName := FormatEnvName(file)
// Load environment variables from file
envLoad, err := GetEnvsFromFile(file, opt)
if err != nil {
return envs, errors.Wrap(err, "Unable to read env_file")
}
// Add configMapKeyRef to each environment variable
for k, _ := range envLoad {
envs = append(envs, api.EnvVar{
Name: k,
ValueFrom: &api.EnvVarSource{
ConfigMapKeyRef: &api.ConfigMapKeySelector{
LocalObjectReference: api.LocalObjectReference{
Name: envName,
},
Key: k,
}},
})
}
}
} else {
// Load up the environment variables
for _, v := range service.Environment {
envs = append(envs, api.EnvVar{
Name: v.Name,
Value: v.Value,
})
}
}
// Stable sorts data while keeping the original order of equal elements
// we need this because envs are not populated in any random order
// this sorting ensures they are populated in a particular order
sort.Stable(envs)
return envs
return envs, nil
}
// CreateKubernetesObjects generates a Kubernetes artifact for each input type service
@@ -517,6 +584,13 @@ func (k *Kubernetes) CreateKubernetesObjects(name string, service kobject.Servic
objects = append(objects, k.InitRC(name, service, replica))
}
if len(service.EnvFile) > 0 {
for _, envFile := range service.EnvFile {
configMap := k.InitConfigMap(name, service, opt, envFile)
objects = append(objects, configMap)
}
}
return objects
}
@@ -610,7 +684,10 @@ func (k *Kubernetes) Transform(komposeObject kobject.KomposeObject, opt kobject.
}
}
k.UpdateKubernetesObjects(name, service, &objects)
err := k.UpdateKubernetesObjects(name, service, opt, &objects)
if err != nil {
return nil, errors.Wrap(err, "Error transforming Kubernetes objects")
}
allobjects = append(allobjects, objects...)
}
@@ -744,6 +821,12 @@ func (k *Kubernetes) Deploy(komposeObject kobject.KomposeObject, opt kobject.Con
return err
}
log.Infof("Successfully created Pod: %s", t.Name)
case *api.ConfigMap:
_, err := client.ConfigMaps(namespace).Create(t)
if err != nil {
return err
}
log.Infof("Successfully created Config Map: %s", t.Name)
}
}
+5 -2
View File
@@ -398,8 +398,11 @@ func (o *OpenShift) Transform(komposeObject kobject.KomposeObject, opt kobject.C
}
}
// Update and then append the objects (we're done generating)
o.UpdateKubernetesObjects(name, service, &objects)
err := o.UpdateKubernetesObjects(name, service, opt, &objects)
if err != nil {
return nil, errors.Wrap(err, "Error transforming Kubernetes objects")
}
allobjects = append(allobjects, objects...)
}
+2 -1
View File
@@ -62,9 +62,10 @@ func TestOpenShiftUpdateKubernetesObjects(t *testing.T) {
var object []runtime.Object
o := OpenShift{}
serviceConfig := newServiceConfig()
opt := kobject.ConvertOptions{}
object = append(object, o.initDeploymentConfig("foobar", serviceConfig, 3))
o.UpdateKubernetesObjects("foobar", serviceConfig, &object)
o.UpdateKubernetesObjects("foobar", serviceConfig, opt, &object)
for _, obj := range object {
switch tobj := obj.(type) {