Skip to content

Commit

Permalink
Support file based secrets
Browse files Browse the repository at this point in the history
  • Loading branch information
hangyan committed May 16, 2018
1 parent 40c7f36 commit 18b840c
Show file tree
Hide file tree
Showing 14 changed files with 678 additions and 8 deletions.
12 changes: 6 additions & 6 deletions docs/conversion.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ __Glossary:__
| deploy: replicas | - | - || Deployment.Spec.Replicas / DeploymentConfig.Spec.Replicas | |
| deploy: placement | - | - || Pod.Spec.NodeSelector | |
| deploy: update_config | - | - | n | | |
| deploy: resources | - | - || Containers.Resources.Limits.Memory / Containers.Resources.Limits.CPU | Support for memory as well as cpu |
| deploy: resources | - | - || Containers.Resources.Limits.Memory / Containers.Resources.Limits.CPU | Support for memory as well as cpu |
| deploy: restart_policy | - | - || Pod generation | This generated a Pod, see the [user guide on restart](http://kompose.io/user-guide/#restart) |
| deploy: labels | - | - | n | | |
| devices | x | x | x | | Not supported within Kubernetes, See issue https://github.com/kubernetes/kubernetes/issues/5607 |
Expand All @@ -57,18 +57,18 @@ __Glossary:__
| labels |||| Metadata.Annotations | |
| links | x | x | x | | All containers in the same pod are accessible in Kubernetes |
| logging | x | x | x | | Kubernetes has built-in logging support at the node-level |
| network_mode | x | x | x | | Kubernetes uses its own cluster networking |
| network_mode | x | x | x | | Kubernetes uses its own cluster networking |
| networks | x | x | x | | See `networks` key |
| networks: aliases | x | x | x | | See `networks` key |
| networks: addresses | x | x | x | | See `networks` key |
| pid |||| Pod.Spec.HostPID | |
| ports |||| Service.Spec.Ports | |
| ports: short-syntax |||| Service.Spec.Ports | |
| ports: long-syntax | - | - || Service.Spec.Ports | |
| secrets | - | - | n | | |
| secrets: short-syntax | - | - | n | | |
| secrets: long-syntax | - | - | n | | |
| security_opt | x | x | x | | Kubernetes uses its own container naming scheme |
| secrets | - | - | | Secret | External Secret is not Supported |
| secrets: short-syntax | - | - | | Secret | External Secret is not Supported |
| secrets: long-syntax | - | - | | Secret | External Secret is not Supported |
| security_opt | x | x | x | | Kubernetes uses its own container naming scheme |
| stop_grace_period |||| Pod.Spec.TerminationGracePeriodSeconds | |
| stop_signal | x | x | x | | Not supported within Kubernetes. See issue https://github.com/kubernetes/kubernetes/issues/30051 |
| sysctls | n | n | n | | |
Expand Down
15 changes: 13 additions & 2 deletions pkg/kobject/kobject.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package kobject

import (
cliTypes "github.com/docker/cli/cli/compose/types"
"github.com/docker/libcompose/yaml"
"k8s.io/kubernetes/pkg/api"
)
Expand All @@ -28,6 +29,8 @@ type KomposeObject struct {
// Transformer need to know origin format in order to tell user what tag is not supported in origin format
// as they can have different names. For example environment variables are called environment in compose but Env in bundle.
LoadedFrom string

Secrets map[string]cliTypes.SecretConfig
}

// ConvertOptions holds all options that controls transformation process
Expand Down Expand Up @@ -105,8 +108,9 @@ type ServiceConfig struct {
Replicas int `compose:"replicas"`
GroupAdd []int64 `compose:"group_add"`
Volumes []Volumes `compose:""`
HealthChecks HealthCheck `compose:""`
Placement map[string]string `compose:""`
Secrets []cliTypes.ServiceSecretConfig
HealthChecks HealthCheck `compose:""`
Placement map[string]string `compose:""`
}

// HealthCheck the healthcheck configuration for a service
Expand Down Expand Up @@ -147,3 +151,10 @@ type Volumes struct {
PVCName string // name of PVC
PVCSize string // PVC size
}

// SecretConfig hold secrets configs, for now only support file based secret
/*
type OldSecretConfig struct {
File string
}
*/
29 changes: 29 additions & 0 deletions pkg/loader/compose/v3.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ func parseV3(files []string) (kobject.KomposeObject, error) {
}
}
}
log.Debugf("fuck %+v", config)

// TODO: Check all "unsupported" keys and output details
// Specifically, keys such as "volumes_from" are not supported in V3.
Expand Down Expand Up @@ -221,12 +222,39 @@ func parseHealthCheck(composeHealthCheck types.HealthCheckConfig) (kobject.Healt
}, nil
}

// parseSecrets parse secrets, ignore docker external secret by now
/*func parseSecrets(composeObject *types.Config) (map[string]kobject.SecretConfig) {
secrets := composeObject.Secrets
if secrets == nil {
return nil
}
result := make(map[string]kobject.SecretConfig)
for name, config := range secrets {
if config.File != "" {
result[name] = kobject.SecretConfig{
File: config.File,
}
} else {
log.Warnf("External secrets is not supported by now - ignoring")
}
}
if len(result) == 0 {
return nil
} else {
return result
}
}*/

func dockerComposeToKomposeMapping(composeObject *types.Config) (kobject.KomposeObject, error) {

// Step 1. Initialize what's going to be returned
komposeObject := kobject.KomposeObject{
ServiceConfigs: make(map[string]kobject.ServiceConfig),
LoadedFrom: "compose",
Secrets: composeObject.Secrets,
}

// Step 2. Parse through the object and convert it to kobject.KomposeObject!
Expand Down Expand Up @@ -255,6 +283,7 @@ func dockerComposeToKomposeMapping(composeObject *types.Config) (kobject.Kompose
serviceConfig.Labels = composeServiceConfig.Labels
serviceConfig.HostName = composeServiceConfig.Hostname
serviceConfig.DomainName = composeServiceConfig.DomainName
serviceConfig.Secrets = composeServiceConfig.Secrets

//
// Deploy keys
Expand Down
10 changes: 10 additions & 0 deletions pkg/transformer/kubernetes/k8sutils.go
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,16 @@ func GetEnvsFromFile(file string, opt kobject.ConvertOptions) (map[string]string
return envLoad, nil
}

// GetSecretDataFromFile load secret content data
func GetSecretDataFromFile(file string, opt kobject.ConvertOptions) ([]byte, error) {
composeDir, err := transformer.GetComposeFileDir(opt.InputFiles)
if err != nil {
return nil, errors.Wrap(err, "Unable to load file context")
}
fileLocation := path.Join(composeDir, file)
return ioutil.ReadFile(fileLocation)
}

// FormatEnvName format env name
func FormatEnvName(name string) string {
envName := strings.Trim(name, "./")
Expand Down
96 changes: 96 additions & 0 deletions pkg/transformer/kubernetes/kubernetes.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import (

"github.com/kubernetes/kompose/pkg/loader/compose"
"github.com/pkg/errors"
"github.com/spf13/cast"
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/labels"
"path/filepath"
Expand Down Expand Up @@ -285,6 +286,37 @@ func (k *Kubernetes) initIngress(name string, service kobject.ServiceConfig, por
return ingress
}

// CreateSecrets create secrets
func (k *Kubernetes) CreateSecrets(komposeObject kobject.KomposeObject) ([]*api.Secret, error) {
var objects []*api.Secret
for name, config := range komposeObject.Secrets {
if config.File != "" {
data, err := GetSecretDataFromFile(config.File, k.Opt)
if err != nil {
log.Fatal("unable to read secret from file:", config.File)
return nil, err
}
secret := &api.Secret{
TypeMeta: unversioned.TypeMeta{
Kind: "Secret",
APIVersion: "v1",
},
ObjectMeta: api.ObjectMeta{
Name: name,
Labels: transformer.ConfigLabels(name),
},
Type: api.SecretTypeOpaque,
Data: map[string][]byte{name: data},
}
objects = append(objects, secret)
} else {
log.Warnf("External secrets %s is not supported by now - ignoring", name)
}
}
return objects, nil

}

// CreatePVC initializes PersistentVolumeClaim
func (k *Kubernetes) CreatePVC(name string, mode string, size string) (*api.PersistentVolumeClaim, error) {
volSize, err := resource.ParseQuantity(size)
Expand Down Expand Up @@ -429,6 +461,56 @@ func (k *Kubernetes) ConfigTmpfs(name string, service kobject.ServiceConfig) ([]
return volumeMounts, volumes
}

// ConfigSecretVolumes config volumes from secret.
// Note: compose's uid/gid config is not supported for now
func (k *Kubernetes) ConfigSecretVolumes(name string, service kobject.ServiceConfig) ([]api.VolumeMount, []api.Volume) {
var volumeMounts []api.VolumeMount
var volumes []api.Volume
if len(service.Secrets) > 0 {
for _, secretConfig := range service.Secrets {
if secretConfig.UID != "" {
log.Warnf("Ignore pid in secrets for service: %s", name)
}
if secretConfig.GID != "" {
log.Warnf("Ignore gid in secrets for service: %s", name)
}

target := secretConfig.Target
if target == "" {
target = secretConfig.Source
}

volSource := api.VolumeSource{
Secret: &api.SecretVolumeSource{
SecretName: secretConfig.Source,
Items: []api.KeyToPath{{
Key: secretConfig.Source,
Path: target,
}},
},
}

if secretConfig.Mode != nil {
mode := cast.ToInt32(*secretConfig.Mode)
volSource.Secret.DefaultMode = &mode
}

vol := api.Volume{
Name: secretConfig.Source,
VolumeSource: volSource,
}
volumes = append(volumes, vol)

volMount := api.VolumeMount{
Name: vol.Name,
MountPath: "/run/secrets",
}
volumeMounts = append(volumeMounts, volMount)
}
}
return volumeMounts, volumes
}

// ConfigVolumes configure the container volumes.
func (k *Kubernetes) ConfigVolumes(name string, service kobject.ServiceConfig) ([]api.VolumeMount, []api.Volume, []*api.PersistentVolumeClaim, error) {
volumeMounts := []api.VolumeMount{}
Expand All @@ -449,6 +531,10 @@ func (k *Kubernetes) ConfigVolumes(name string, service kobject.ServiceConfig) (
useHostPath = true
}

secretsVolumeMounts, secretsVolumes := k.ConfigSecretVolumes(name, service)
volumeMounts = append(volumeMounts, secretsVolumeMounts...)
volumes = append(volumes, secretsVolumes...)

var count int
//iterating over array of `Vols` struct as it contains all necessary information about volumes
for _, volume := range service.Volumes {
Expand Down Expand Up @@ -697,6 +783,16 @@ func (k *Kubernetes) Transform(komposeObject kobject.KomposeObject, opt kobject.
// this will hold all the converted data
var allobjects []runtime.Object

if komposeObject.Secrets != nil {
secrets, err := k.CreateSecrets(komposeObject)
if err != nil {
return nil, errors.Wrapf(err, "create secrets error")
}
for _, item := range secrets {
allobjects = append(allobjects, item)
}
}

sortedKeys := SortedKeys(komposeObject)
for _, name := range sortedKeys {
service := komposeObject.ServiceConfigs[name]
Expand Down
10 changes: 10 additions & 0 deletions pkg/transformer/openshift/openshift.go
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,16 @@ func (o *OpenShift) Transform(komposeObject kobject.KomposeObject, opt kobject.C
buildRepo := opt.BuildRepo
buildBranch := opt.BuildBranch

if komposeObject.Secrets != nil {
secrets, err := o.CreateSecrets(komposeObject)
if err != nil {
return nil, errors.Wrapf(err, "create secrets error")
}
for _, item := range secrets {
allobjects = append(allobjects, item)
}
}

sortedKeys := kubernetes.SortedKeys(komposeObject)
for _, name := range sortedKeys {
service := komposeObject.ServiceConfigs[name]
Expand Down
24 changes: 24 additions & 0 deletions script/test/cmd/tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,31 @@ cmd="kompose --provider openshift -f $KOMPOSE_ROOT/script/test/fixtures/service-
sed -e "s;%VERSION%;$version;g" -e "s;%CMD%;$cmd;g" $KOMPOSE_ROOT/script/test/fixtures/service-name-change/output-os-template.json > /tmp/output-os.json
convert::expect_success_and_warning "$cmd" "/tmp/output-os.json" "Unsupported root level volumes key - ignoring"

#####
# Test secrets
# Kubernetes Test

cmd="kompose -f $KOMPOSE_ROOT/script/test/fixtures/secrets/docker-compose-secrets-long.yml convert --stdout -j"
sed -e "s;%VERSION%;$version;g" -e "s;%CMD%;$cmd;g" $KOMPOSE_ROOT/script/test/fixtures/secrets/output-long-k8s.json > /tmp/output-k8s.json
convert::expect_success_and_warning "$cmd" "/tmp/output-k8s.json" "External secrets my_other_secret is not supported by now - ignoring"

cmd="kompose -f $KOMPOSE_ROOT/script/test/fixtures/secrets/docker-compose-secrets-short.yml convert --stdout -j"
sed -e "s;%VERSION%;$version;g" -e "s;%CMD%;$cmd;g" $KOMPOSE_ROOT/script/test/fixtures/secrets/output-short-k8s.json > /tmp/output-k8s.json
convert::expect_success_and_warning "$cmd" "/tmp/output-k8s.json" "External secrets my_other_secret is not supported by now - ignoring"



# Openshift Test
cmd="kompose --provider openshift -f $KOMPOSE_ROOT/script/test/fixtures/secrets/docker-compose-secrets-long.yml convert --stdout -j"
sed -e "s;%VERSION%;$version;g" -e "s;%CMD%;$cmd;g" $KOMPOSE_ROOT/script/test/fixtures/secrets/output-long-os.json > /tmp/output-os.json
convert::expect_success_and_warning "$cmd" "/tmp/output-os.json" "External secrets my_other_secret is not supported by now - ignoring"

cmd="kompose --provider openshift -f $KOMPOSE_ROOT/script/test/fixtures/secrets/docker-compose-secrets-short.yml convert --stdout -j"
sed -e "s;%VERSION%;$version;g" -e "s;%CMD%;$cmd;g" $KOMPOSE_ROOT/script/test/fixtures/secrets/output-short-os.json > /tmp/output-os.json
convert::expect_success_and_warning "$cmd" "/tmp/output-os.json" "External secrets my_other_secret is not supported by now - ignoring"


#####
# Test regarding validating dockerfilepath
convert::expect_failure "kompose -f $KOMPOSE_ROOT/script/test/fixtures/dockerfilepath/docker-compose.yml convert --stdout"

Expand Down
17 changes: 17 additions & 0 deletions script/test/fixtures/secrets/docker-compose-secrets-long.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
version: "3.1"
services:
redis:
image: redis:latest
deploy:
replicas: 1
secrets:
- source: my_secret
target: redis_secret
uid: '103'
gid: '103'
mode: 0440
secrets:
my_secret:
file: ./my_secret.txt
my_other_secret:
external: true
14 changes: 14 additions & 0 deletions script/test/fixtures/secrets/docker-compose-secrets-short.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
version: "3.1"
services:
redis:
image: redis:latest
deploy:
replicas: 1
secrets:
- my_secret
- my_other_secret
secrets:
my_secret:
file: ./my_secret.txt
my_other_secret:
external: true
1 change: 1 addition & 0 deletions script/test/fixtures/secrets/my_secret.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
For the Watch!
Loading

0 comments on commit 18b840c

Please sign in to comment.