-
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
Added support for providing replicas #66
Conversation
Commented in the original issue #19 (comment) |
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.
- Allow specifying replica count for service #19 (comment)
- missing unit tests
@surajssd FYI and to save you time, you need to move from |
pkg/cmd/convert.go
Outdated
@@ -75,7 +75,7 @@ func GetValidatedObject(v *viper.Viper, cmd *cobra.Command, out, outerr io.Write | |||
|
|||
o, err := decoder.Decode(data) | |||
if err != nil { | |||
return nil, fmt.Errorf("could not unmarsha data for file '%s': %s", file, err) | |||
return nil, fmt.Errorf("could not unmarshal data for file '%s': %s", file, err) |
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.
Could you please make it a different commit as it has no relation to Added support for providing replicas?
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!
50a5235
to
2c6c608
Compare
// Set replica value only if it is given | ||
if o.Replicas > 0 { | ||
replicas := o.Replicas | ||
d.Spec.Replicas = &replicas |
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 a better thing to do or should I do: d.Spec.Replicas = &o.Replicas
?
Thoughts: @tnozicka @containscafeine ?
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 d.Spec.Replicas = &o.Replicas
should be fine, no?
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.
@containscafeine i thought it will have to save entire object o
to just assign value, until d
is alive. But if I can copy it in some variable then it will be less wastage of resources?
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 d.Spec.Replicas = o.Replicas
you should make o.Replicas pointer as well because it will be a pointer from the parser too
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.
oh yes, makes sense
pkg/encoding/v1/encoding.go
Outdated
@@ -319,6 +320,9 @@ func (d *Decoder) Decode(data []byte) (*object.OpenCompose, error) { | |||
Name: string(s.Name), | |||
} | |||
|
|||
// user given value of replicas | |||
os.Replicas = s.Replicas |
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.
using values as it is here and checking down whether it should land in deployment
or not? thoughts: @tnozicka
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.
probably out-of date now
pkg/encoding/v1/encoding_test.go
Outdated
Image: "tomaskral/nonroot-nginx", | ||
}, | ||
}, | ||
Replicas: 0, |
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.
since using values as it is so not much changes, checks done at a level up when creating deployment
, so original info is not lost from opencompose as well as in cluster.
pkg/encoding/v1/encoding.go
Outdated
@@ -184,6 +184,7 @@ func (c *Container) UnmarshalYAML(unmarshal func(interface{}) error) error { | |||
type Service struct { | |||
Name ResourceName `yaml:"name"` | |||
Containers []Container `yaml:"containers"` | |||
Replicas int32 `yaml:"replicas,omitempty"` |
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 has to be a pointer here otherwise you can't distinguish between user setting 0 and not specifying the value
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
@@ -319,6 +320,9 @@ func (d *Decoder) Decode(data []byte) (*object.OpenCompose, error) { | |||
Name: string(s.Name), | |||
} | |||
|
|||
// user given value of replicas | |||
os.Replicas = s.Replicas |
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.
probably out-of date now
@@ -106,6 +106,11 @@ func (t *Transformer) CreateDeployments(o *object.Service) ([]runtime.Object, er | |||
}, | |||
} | |||
|
|||
// Set replica value only if it is given | |||
if o.Replicas != nil { | |||
d.Spec.Replicas = o.Replicas |
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.
only L111 is needed since it's now a pointer
pkg/encoding/v1/encoding_test.go
Outdated
@@ -419,5 +419,44 @@ volumes: [] | |||
continue | |||
} | |||
} | |||
} |
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 don't like these unit test. We do table driven testing (a bit of Golang way) everywhere else.
There should be 2 unit tests for:
- all the test cases (nil, 0, 42, fail, ...) - TestService_UnmarshalYAML (you can get inspiration here)
The unit test for service hasn't been there before which is my fault, but If you wouldn't mind adding it there, that will be great - to ensure the value gets copied (probably just add one case to existing test) -https://github.com/surajssd/opencompose/blob/8e34ab28588e02b85fada5cc52661cf300f36b0a/pkg/encoding/v1/encoding_test.go#L147
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
@@ -319,6 +320,9 @@ func (d *Decoder) Decode(data []byte) (*object.OpenCompose, error) { | |||
Name: string(s.Name), | |||
} | |||
|
|||
// user given value of replicas |
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 now a pointer to the value; maybe just drop the comment since its obvious anyway
546162d
to
df245ff
Compare
pkg/encoding/v1/encoding_test.go
Outdated
version: 0.1-dev | ||
services: | ||
- name: helloworld | ||
replicas: -1 |
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.
even after giving value as negative this test passes, since the check on negativity happens later? Apart from adding tests about createdeployment and verifying this actually fails @tnozicka do you have any thought on handling this well?
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.
parser should check this
} else { | ||
return nil, fmt.Errorf("Replica count is negative in service: %q", o.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.
this is where I have added the check and this is needed otherwise the artifact is generated with negative number and k8s won't take 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.
actually, parser should be the one checking that since it's invalid input
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.
Added checks to parser as well
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 this is checked in parser, does it make sense to check it here again?
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 agreed. That's what my previous comment was suppose to mean (move, not copy the check) - this isn't task transformer should do
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 remove check from here, how do we address the condition where someone wants to use the opencompose as lib?
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.
and we wanna do the validation? ideally for an app like this where data is coming from multiple places we should have a layer which does it for us? WDYT @tnozicka ?
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.
May be we could use object.Validate for this.
All validation logic should be in one place
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 makes sense to me!
} else { | ||
return nil, fmt.Errorf("Replica count is negative in service: %q", o.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.
actually, parser should be the one checking that since it's invalid input
pkg/encoding/v1/encoding_test.go
Outdated
} | ||
|
||
for _, test := range tests { | ||
t.Logf("Running test: %q", test.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.
please use t.Run where you can pass the name
we are moving to using subtests (https://blog.golang.org/subtests)
pkg/encoding/v1/encoding_test.go
Outdated
version: 0.1-dev | ||
services: | ||
- name: helloworld | ||
replicas: -1 |
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.
parser should check this
pkg/encoding/v1/encoding_test.go
Outdated
"Giving replica as string to see parsing fails", | ||
false, ` | ||
name: frontend | ||
replicas: notint |
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.
probably a typo: s/notint/nonint/ or s/notint/non-int/
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 just for showing this is 'not an integer' value
ba67038
to
4b49e16
Compare
Can you please update docs (file-reference) as part of this PR? |
@kadel done! |
docs/file-reference.md
Outdated
| int32 | no | | ||
|
||
Number of desired pods of this particluar service. This is an optional field. | ||
If nothing is provided the artifact generated won't have anything either. |
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 don't know about this sentence :-(
It won't say anything useful for people that are not familiar with Kubernetes.
I think that we should avoid mentioning generated artifacts as much as possible.
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 just delete that last sentence sentence?
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!
docs/file-reference.md
Outdated
@@ -38,11 +39,12 @@ Version of OpenCompose format. | |||
Describes one service. Service can be composed of one or multiple containers which are scheduled | |||
together and can communicate using localhost. (Containers in the same service share network namespace.) | |||
|
|||
Each service has name and list of the containers. | |||
Each service has name, replicas and list of the containers. |
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 doesn't have to have replicas
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!
d702237
to
b5b5086
Compare
pkg/object/object.go
Outdated
// validate containers | ||
|
||
// validate replicas | ||
if err := validateReplicas(s.Replicas); 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.
Stupid question as usual 😄
If replica check is just one line, and it is used in one place, does it make sense to have a separate function for it? Wouldn't be code more readable if actual check was 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.
I wanted to make it testable so separated it out!
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.
than you testing separately one if
statement?
I don't know, but for me it looks like it is just overcomplicating things
Validate
can be tested as whole.
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.
Even I thought this might be overdoing but then there arent other tests so just for testing replicas I will need to create entire stucts object.
And here I can do specific tests like these one: https://github.com/redhat-developer/opencompose/pull/66/files#diff-4f9edbaf8553598b5a269f232bab233cR16
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.
As soon as you add another validation test to this you will have to test whole Validate
function anyway.
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.
updated as per comments!
true, | ||
&object.Service{ | ||
Name: name, | ||
Replicas: goutil.Int32Addr(-1), |
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 also comment on this test?
pkg/encoding/v1/encoding_test.go
Outdated
`, | ||
&Service{ | ||
Name: "frontend", | ||
Replicas: goutil.Int32Addr(-1), |
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 also this test, please comment if this is needed ?
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.
lgtm
@kadel is it okay to merge? |
ping @tnozicka |
docs/file-reference.md
Outdated
#### replicas | ||
| type | required | | ||
|-------|----------| | ||
| int32 | no | |
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 uint32 be more descriptive since any negative value is invalid. Or it would be good to mention it somewhere else
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.
should the internal object be also changed to uint32
?
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 int32
in the code is ok.
I'm just thinking if it makes sense in documentation mention int32
type, maybe just integer
?
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_test.go
Outdated
} | ||
|
||
for _, test := range tests { | ||
t.Run(fmt.Sprintf("Running test: %q", test.Name), func(t *testing.T) { |
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 just a test name; go test tool will make the appropriate announcement on what is running
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_test.go
Outdated
|
||
tests := []struct { | ||
Name string | ||
ExpectedSucess 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.
consistent naming with the rest would be Succeed
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_test.go
Outdated
} | ||
|
||
if !test.ExpectedSucess { | ||
t.Errorf("Expected %#v to fail, but succeeded!", test.RawService) |
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.
you can merge these 2 lines by using t.Fatalf
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/goutil/goutil_test.go
Outdated
|
||
for _, test := range tests { | ||
if test.input != *test.output { | ||
t.Errorf("The input and output doesn't match, for %d", test.input) |
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.
logically you should print both of those values to know what's wrong, not just input
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
// validate containers | ||
|
||
// validate replicas | ||
if s.Replicas != nil && *s.Replicas < 0 { |
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.
such checks should be done in the parser as I have mentioned in my previous comments. it's n invalid input for the parser and it is the only place we can have more info like a line number - and all the other checks are done in parser as well!
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.
Logically, the value is valid for parser because parser is only supposed to parse data for its type and not validate if it is correct or not. That's the job of validator.
And I would like to have validation happening in only one place not twice. Someone using opencompose as lib might not be passing validated data so how are we gonna validate 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.
Letes move on, and go with this as it is.
We can figure out how we are going to do all validations in another 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.
So we can merge this one?
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 my other comment regarding docs (https://github.com/redhat-developer/opencompose/pull/66/files#r110106733)
than if you squash it, i think we can merge 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.
Sure on it now!
|
||
// validate replicas | ||
if s.Replicas != nil && *s.Replicas < 0 { | ||
return fmt.Errorf("Replica count is negative in service: %q", s.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.
s/is/shall not be/
|
||
tests := []struct { | ||
Name string | ||
ExpectSuccess 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.
non-consistent naming as 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.
done!
|
||
transformer := Transformer{} | ||
for _, test := range tests { | ||
|
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.
s/\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!
transformer := Transformer{} | ||
for _, test := range tests { | ||
|
||
t.Run(fmt.Sprintf("Running test: %q", test.Name), func(t *testing.T) { |
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 just name of the test
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!
A user can now provide the number of replicas of the service that are needed, using a field called `replicas` in opencompose `service` section. This is an optional field, if nothing is provided then the no default value is assumed. Fixes redhat-developer#19
all issues addressed, validation will be fixed in another pr #92
A user can now provide the number of replicas of the
service that are needed, using a field called
replicas
in opencompose
service
section.This is an optional field, if nothing is provided then
the value defaults to 1.
Fixes #19