-
Notifications
You must be signed in to change notification settings - Fork 12
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 volumes implementation support #88
Add volumes implementation support #88
Conversation
a1e11fd
to
5e03a5e
Compare
using opencompose file: version: 0.1-dev
services:
- name: db
containers:
- image: mysql
mounts:
- volumeName: db
mountPath: /var/lib/mysql
- name: backup
containers:
- image: backup
mounts:
- volumeName: db
mountPath: /app/store
volumeSubPath: foo/bar
readOnly: true
- name: process
containers:
- image: process
mounts:
- volumeName: temp
mountPath: /app/data
emptyDirVolumes:
- name: temp
volumes:
- name: db
size: 5Gi
accessMode: ReadWriteMany
storageClass: fast converting as: $ opencompose convert -f ex.yml -o -
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
creationTimestamp: null
labels:
service: db
name: db
spec:
strategy:
type: RollingUpdate
template:
metadata:
creationTimestamp: null
labels:
service: db
spec:
containers:
- image: mysql
name: db-0
resources: {}
volumeMounts:
- mountPath: /var/lib/mysql
name: db
volumes:
- name: db
persistentVolumeClaim:
claimName: db
status: {}
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
creationTimestamp: null
labels:
service: backup
name: backup
spec:
strategy:
type: RollingUpdate
template:
metadata:
creationTimestamp: null
labels:
service: backup
spec:
containers:
- image: backup
name: backup-0
resources: {}
volumeMounts:
- mountPath: /app/store
name: db
readOnly: true
subPath: foo/bar
volumes:
- name: db
persistentVolumeClaim:
claimName: db
status: {}
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
creationTimestamp: null
labels:
service: process
name: process
spec:
strategy:
type: RollingUpdate
template:
metadata:
creationTimestamp: null
labels:
service: process
spec:
containers:
- image: process
name: process-0
resources: {}
volumeMounts:
- mountPath: /app/data
name: temp
volumes:
- emptyDir: {}
name: temp
status: {}
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
annotations:
volume.beta.kubernetes.io/storage-class: fast
creationTimestamp: null
name: db
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 5Gi
status: {} |
5e03a5e
to
21b9407
Compare
was clicked by mistake |
@kadel For testing the parsing part/
Do you feel anything is missing and I should think of something else? |
f8070a9
to
a19a891
Compare
pkg/object/object.go
Outdated
} | ||
|
||
// validate mountPath | ||
if err := validatePath(mount.MountPath); err != nil { |
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.
@kadel should we do this?
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.
or just assume that user will give valid 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.
If i'm not mistaken MountPath should be always absolute path. We could check at least that.
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.
done!
dfa2e8e
to
d559494
Compare
examples/vols/ex.yml
Outdated
mounts: | ||
- volumeName: db | ||
mountPath: /app/store | ||
volumeSubPath: foo/bar |
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.
Total thumbsup for this feature, I do need it for my usecase:
https://github.com/brainstorm/cgtd/commit/344fa1a15d5874c3dff17e91537864b77b5fd2b1
Would it be reasonable to include support for paths like $HOME
in the spec?
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.
Would it be reasonable to include support for paths like $HOME in the spec?
That is interesting idea. We won't add it in this initial implementation, but definitely will keep it in mind for future improvements. Will have to do some research on how this can be done in Kubernetes. (any pointers?)
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 have no idea, unfortunately, quite a newbie on kubernetes but I know from related-ish projects like:
https://github.com/BD2KGenomics/toil
https://github.com/common-workflow-language/common-workflow-language
That users are gonna expect it to be supported :-S
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.
@surajssd How about we move the type declarations to object.go?
pkg/encoding/v1/encoding.go
Outdated
@@ -179,10 +179,55 @@ type ImageRef string | |||
|
|||
// FIXME: implement ImageRef unmarshalling | |||
|
|||
type Path string |
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.
How about we move the type declaration to object.go and do something like type Path object.Path
here?
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.
Hmm, I guess in this case its ok. As there is no object.Path
its inside Mount
struct.
But if we don't need custom UnmarshalYAML
for Path
than we don't even need Path
as separate type here.
We can use just string
instead of Path
in Mount
struct
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.
done
pkg/encoding/v1/encoding.go
Outdated
return nil | ||
} | ||
|
||
type Mount struct { |
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.
How about we move the type declaration to object.go
@@ -205,10 +250,34 @@ func (c *Container) UnmarshalYAML(unmarshal func(interface{}) error) error { | |||
return nil | |||
} | |||
|
|||
type EmptyDirVolume struct { |
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.
How about we move the type declaration to object.go
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.
This needs json tags so keeping it here.
Ref: @tnozicka 's explanation
@@ -189,14 +190,91 @@ func (t *Transformer) CreateDeployments(o *object.Service) ([]runtime.Object, er | |||
}) | |||
} | |||
|
|||
// TODO: It is assumed that the check is done about the existence of volume in root level volume section | |||
for _, mount := range c.Mounts { | |||
|
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.
nit: remove this \n
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.
done
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.
Here is my first pass on this.
Didn't have time to look at tests yet :-(
pkg/encoding/v1/encoding.go
Outdated
@@ -233,16 +302,37 @@ func (s *Service) UnmarshalYAML(unmarshal func(interface{}) error) error { | |||
|
|||
type VolumeSize string | |||
|
|||
// FIXME: add VolumeSize parsing/validation | |||
func (vs *VolumeSize) UnmarshalYAML(unmarshal func(interface{}) error) error { |
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.
If no validation or additional parsing is done here, this function is not needed, is it?
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 did it as it's done for ResourceName
here.
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.
UnmarshalYAML
for ResourceName
does validation on top of unmarshalling .
But in this case you are not doing anything special, you are just calling unmarshal
function on string
type. You don't need custom UnmarshalYAML
function in this case
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.
removed those types changed it to built in type string
pkg/encoding/v1/encoding.go
Outdated
|
||
type VolumeMode string | ||
|
||
// FIXME: add VolumeMode parsing/validation | ||
func (vm *VolumeMode) UnmarshalYAML(unmarshal func(interface{}) error) error { |
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.
If no validation or additional parsing is done here, this function is not needed, is it?
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.
see the comment above!
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.
see comment above ;-)
pkg/encoding/v1/encoding.go
Outdated
@@ -179,10 +179,55 @@ type ImageRef string | |||
|
|||
// FIXME: implement ImageRef unmarshalling | |||
|
|||
type Path string | |||
|
|||
func (p *Path) UnmarshalYAML(unmarshal func(interface{}) error) error { |
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.
If no validation or additional parsing is done here, this function is not needed, is it?
pkg/object/object.go
Outdated
@@ -50,16 +71,129 @@ type OpenCompose struct { | |||
Volumes []Volume | |||
} | |||
|
|||
func (s *Service) EmptyDirVolumeExists(name string) bool { |
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.
can you please add comment explaining what this function is for?
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.
done
pkg/object/object.go
Outdated
return false | ||
} | ||
|
||
func (o *OpenCompose) VolumeExists(name string) bool { |
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.
can you please add comment explaining what this function is for?
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.
done
pkg/object/object.go
Outdated
|
||
// Documentation about the valid identifiers can be found at | ||
// https://github.com/kubernetes/community/blob/master/contributors/design-proposals/identifiers.md | ||
func isValidName(name string) error { |
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.
this is called isValidName
but other validation function are called validate<something>
( validateVolumeMode
validatePath
). Wouldn't be better to use same naming scheme for all function validation fields?
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.
another issue is that when I put wrong name i get just this error:
ERROR: must match the regex [a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)* (e.g. 'example.com')
But it doesn't say where it is. :-(
I guess this issue will be same with other validations here. :-(
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.
this is called
isValidName
but other validation function are calledvalidate<something>
(validateVolumeMode
validatePath
). Wouldn't be better to use same naming scheme for all function validation fields?
done
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.
another issue is that when I put wrong name i get just this error:
ERROR: must match the regex [a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)* (e.g. 'example.com')
But it doesn't say where it is. :-(
I guess this issue will be same with other validations here. :-(
done
pkg/object/object.go
Outdated
return nil | ||
} | ||
|
||
func (v *Volume) Validate() error { |
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.
Is this called outside object
pkg?
I think this can be private. There is no reason for someone validating just volume outside of this package.
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.
Just thinking out loud, thought these could turn into interface or something similar?
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.
interface? why? Don't know what you mean :-(
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 actually don;t remember what was I thinking :-D
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.
@surajssd so what do you think about making this private?
I would make all Validate
function private expect func (o *OpenCompose) Validate()
. If we are thinking about this being used as library, this is only Validate function that should be exposed.
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.
done !
pkg/object/object.go
Outdated
// or emptydirvolumes | ||
for _, container := range service.Containers { | ||
for _, mount := range container.Mounts { | ||
if !o.VolumeExists(mount.VolumeName) && !service.EmptyDirVolumeExists(mount.VolumeName) { |
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.
There should be one more check that validates that there are not multiple mounts for same path.
mounts:
- volumeName: bar
mountPath: /storage
- volumeName: foo
mountPath: /storage
This is not valid and should be checked.
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.
done
// if this mount does not exist in emptydir then this is coming from root level volumes directive | ||
// if tomorrow we add support for ConfigMaps or Secrets mounted as volumes the check should be done | ||
// here to see if it is not coming from configMaps or Secrets | ||
if !o.EmptyDirVolumeExists(mount.VolumeName) { |
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.
Wouldn't be better to do if o.VolumeExists(mount.VolumeName)
here? It might make more sense once we add secrets as volumes and It will be also a bit safer.
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.
We don't have the object opencompose
to do that call(o.VolumeExists
). We have access to service
type object o
.
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.
hmm, that is quite confusing if its called o
:-)
OK than, lets keep it that way, but it might bring some problem in the future.
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.
changed it to s
.
pvc.Spec.AccessModes = []api_v1.PersistentVolumeAccessMode{api_v1.ReadOnlyMany} | ||
case "ReadWriteMany": | ||
pvc.Spec.AccessModes = []api_v1.PersistentVolumeAccessMode{api_v1.ReadWriteMany} | ||
} |
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.
Can we add default
section that will cause error?
I know that it shouldn't happen because its validated before, but you never know ;-) That one line might save a lot of debugging in future ;-)
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.
yeah SGTM, done.
ef81fda
to
6a7c60a
Compare
6a7c60a
to
b5ee51f
Compare
examples/vols/ex.yml
Outdated
- volumeName: db | ||
mountPath: /var/lib/mysql | ||
- volumeName: iiit | ||
mountPath: /var/lib/mysql |
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.
This example is invalid!
ERROR: service "db": container#1: mount "iiit": mountPath "/var/lib/mysql": cannot have same mountPath as "db"
allMounts := make(map[string]string) | ||
// validate Mounts | ||
for _, mount := range c.Mounts { | ||
// validate volumeName |
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.
if we have (c *Container) Validate()
for container validation, (s *Service) Validate()
for service validation, (v *Volume) Validate()
for volume validation. Would it make sense to have (m *Mount) Validate()
for validating single mount ?
Everything expect checking for colliding paths could be in that separate Validate
function
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 problem is that we might not be able to do the validation of colliding mountPath
? Since the variable allMounts
is defined outside the for loop and used inside the loop, one workaround would be to pass it with every call but then it makes mount's validate
function inconsistent than other validate
functions.
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.
What I mend is to keep validation of colliding mountPath
where it is.
Just move name validation ( if err := validateName(mount.VolumeName);
) and path validation (if !path.IsAbs(mount.MountPath) {
) to separate (m *Mount) Validate()
. Those to checks don't need information about other mounts
they can be separate.
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.
@surajssd what do you think about my suggestion?
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 think having it in the function makes more sense than having half things here and others there
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.
At least all those are checks in same place to avoid confusion
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 still feel like this is more confusing. With other parts of OpenCompose object (service, volumes, ...) we have nicely separated validation function but here we are validating whole Mount
object in Container
object validation.
But if you think this is better than let's keep it like this. I can make peace with it. 😄
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.
it's confusing to have some tests outside in a different place and only one in here, that is why i was planning to keep it here. If the container function grows and become very huge then we can think of separating it? WDYT?
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.
if you think this is better, lets keep it
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.
so is this good to go in?
36e694e
to
2a620eb
Compare
2a620eb
to
17a3581
Compare
do we need |
d0bf775
to
80f4f1c
Compare
@kadel done |
80f4f1c
to
88bfdbf
Compare
88bfdbf
to
81ac143
Compare
This PR adds support for volumes in OpenCompose. With this there is new root level directive called `volumes` and new directive in container level called `mounts` and also adds service level directive called `emptyDirVolumes`. More info about volumes is in the proposal: https://github.com/surajssd/opencompose/blob/22207ea605580d70c6718fa87368d0385bc3cbf0/docs/proposals/volumes-proposal.md Fixes redhat-developer#25
81ac143
to
1369132
Compare
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 found one small issue with this. Its described in #114 We don't have to block this PR because of that.
Cool will merge this then |
This PR adds support for volumes in OpenCompose.
With this there is new root level directive called
volumes
and new directive in container level calledmounts
and also adds service level directive calledemptyDirVolumes
.More info about volumes is in the proposal: https://github.com/surajssd/opencompose/blob/22207ea605580d70c6718fa87368d0385bc3cbf0/docs/proposals/volumes-proposal.md
Fixes #25