-
Notifications
You must be signed in to change notification settings - Fork 772
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for file based secret #1007
Changes from 4 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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" | ||
|
@@ -372,6 +373,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 currently supported - 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) | ||
|
@@ -516,6 +548,60 @@ func (k *Kubernetes) ConfigTmpfs(name string, service kobject.ServiceConfig) ([] | |
return volumeMounts, volumes | ||
} | ||
|
||
// ConfigSecretVolumes config volumes from secret. | ||
// Link: https://docs.docker.com/compose/compose-file/#secrets | ||
// In kubernetes' Secret resource, it has a data structure like a map[string]bytes, every key will act like the file name | ||
// when mount to a container. This is the part that missing in compose. So we will create a single key secret from compose | ||
// config and the key's name will be the secret's name, it's value is the file content. | ||
// compose'secret can only be mounted at `/run/secrets`, so we will hardcoded this. | ||
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 | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are you able to comment on some of the code above as to what's happening? |
||
|
||
// ConfigVolumes configure the container volumes. | ||
func (k *Kubernetes) ConfigVolumes(name string, service kobject.ServiceConfig) ([]api.VolumeMount, []api.Volume, []*api.PersistentVolumeClaim, error) { | ||
volumeMounts := []api.VolumeMount{} | ||
|
@@ -536,6 +622,11 @@ func (k *Kubernetes) ConfigVolumes(name string, service kobject.ServiceConfig) ( | |
useHostPath = true | ||
} | ||
|
||
// config volumes from secret if present | ||
secretsVolumeMounts, secretsVolumes := k.ConfigSecretVolumes(name, service) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add comment above here explaining that we are adding secrets to volumes |
||
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 { | ||
|
@@ -806,6 +897,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, "Unable to create Secret resource") | ||
} | ||
for _, item := range secrets { | ||
allobjects = append(allobjects, item) | ||
} | ||
} | ||
|
||
sortedKeys := SortedKeys(komposeObject) | ||
for _, name := range sortedKeys { | ||
service := komposeObject.ServiceConfigs[name] | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@hangyan
This shouldn't be
MountPath: "/run/secrets/" + vol.Name,
???Because without the MountPath targeting to the secret, it gives a error:
Just like in this issue: kubernetes/kubernetes#65835
And concating the secret name, it work!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With some research i discover that:
kubernetes
it create a file
/run/secrets/my_secret/my_secret_key_1
(with the contentmy_secret_value_1
)docker-compose/swarm
it create a file
/run/secrets/my_secret
it create a file
/run/secrets/my_secret/my_secret_key
I believe that this PR:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jvitor83 Sorry about the late response. I have re-checked the document of kubernetes and docker. I have different opinion on this problem. kompose is meant to translate compose to kubernetes as precision as possible. Because docker mount the secret to
/run/secrets/<secret-name>
, so this path should be the same in kubernetes, not/run/secrets/<secret-name>/<key>
. I believe the right choice is to use subPath ( you can see a example in the issues link you provide)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I get it that "kompose is meant to translate compose to kubernetes as precision as possible", but it didn't work with the k8s generated!
The generated mapping gives error (mentioned before) and the pod didn't start!
By my understanding, the generated files should work in first place to then keep the translation as precise as possible.
I guess we should remove the short syntax once it is incompatible and don't allow to get the file at the same path.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The issue link you provide has a solution in the end:subPath. I think it's a better solution, i haven't test it out, but I think it should work
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@hangyan
I am not understanding what you mean.
The thing is that this PR is generating:
which gives the error reported.
The issue (kubernetes/kubernetes#65835) was solved not because it put the
subPath
, but because the directory was added to the mountPath. (I think the issue author have been mistaken)If the kompose generate the k8s without the mountPath with a directory (with secret using short-syntax), it will give error on start. I have tested it with openjdk, dotnet and others. All gives the same error.
What you are suggesting to do?
If is to: "modify the code to add the subPath", it will continue to give the error.
I think we have to at least put some warning saying that secrets with short-syntax is not recommended and can gives error.