-
Notifications
You must be signed in to change notification settings - Fork 40
Conversation
560f32b
to
cb2297f
Compare
docs/file-reference.md
Outdated
@@ -30,6 +30,8 @@ services: | |||
- port: 8080 | |||
targetPort: 80 | |||
endpoint: minikube.external/foo | |||
portMappings: |
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 doesn't make sense in this example, as there is nothing running on port 8081
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
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 if this makes sense, why would you expose the same port twice?
I think that all our examples like this have to work and make sense.
I don't think that showing one example with all Kedge keys has any benefit.
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 agree, but I think we should cover all the fields in file-reference.md so we can show them in use. Where do you suggest we add that?
I think we should have an example which shows all the fields added/modified by kedge in file-reference, it does not have to be a working example, but something that has all the fields and acts as a quick reference on how and where to use that field.
As for this, should I remove 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.
I think that if we show an example with all the fields it will be just hard to read and it won't provide any benefits.
I think that far better is to have multiple examples highlighting given shortcut in the scenario that is best for it. We should show how it should be used, in the scenarios we think is the best for given shortcut and highlight our intent behind it.
Having multiple examples in files-reference.md will be also mess, so we can just reference examples in docs/examples/
In this case, we can just link to docs/examples/portMappings/httpd.yaml with some short explanation.
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
- <port:targetPort/protocol> | ||
``` | ||
|
||
The only field mandatory here is "port". |
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 specify that you are talking about port
part in the portMappings
. When I read this for the first time I thought you are talking about port
in Service.
How about this?
'The only mandatory part of the portMappings
is port
'
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.
makes sense, updated
docs/file-reference.md
Outdated
@@ -30,6 +30,8 @@ services: | |||
- port: 8080 | |||
targetPort: 80 | |||
endpoint: minikube.external/foo | |||
portMappings: | |||
- 8888:80/TCPf |
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/TCPf/TCP/
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.
whoops, fixed
ce16871
to
4b2f2ab
Compare
Needs rebase, but otherwise this PR LGTM |
7701726
to
b888cb3
Compare
@kadel rebased |
docs/examples/portMappings/README.md
Outdated
- name: httpd | ||
portMappings: | ||
- 8080:80/TCP | ||
``` |
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 also mention under known issues or some kinda note saying if you are using portMappings
you cannot use the shortcut expose
for creating ingress
?
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 that a known issue? endpoint
shortcut is only supported with ports:
field, and portMappings:
is at the same level and not nested under ports
. It's not something that's not supported, it's something that's not part of the syntax of portMappings
.
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 just thought it would be better if we be explicit about it.
containers: | ||
- image: centos/httpd | ||
services: | ||
- name: httpd |
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: extra indentation in this section!
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.
fixed
pkg/spec/populators.go
Outdated
api_v1 "k8s.io/client-go/pkg/api/v1" | ||
"strconv" |
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 arrange the imports similar to the comment here #149 (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.
fixed
pkg/spec/populators.go
Outdated
@@ -150,6 +167,93 @@ func populateEnvFrom(c Container, cms []ConfigMapMod, secrets []SecretMod) (Cont | |||
return c, nil | |||
} | |||
|
|||
// Parse the string the get the port, targetPort and protocol | |||
// information, and then return the resulting ServicePort object | |||
func parsePortMapping(pm string) (*api_v1.ServicePort, 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.
the placement of this function is wrong can, you place it in resources.go
or util.go
? Because this is file of all the populators
!
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.
well, this is parsing the port mapping and populating a ServicePort. What do we really mean by a populator? Will help in moving this around.
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 function is just parsing, and not populating so logically it should reside somewhere but not with all other populatiors.
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.
ack, moved to resources.go
pkg/spec/populators_test.go
Outdated
} | ||
} | ||
|
||
func TestParsePortMapping(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.
the placement of this function is wrong can, you place it in
resources.go
orutil.go
? Because this is file of all thepopulators
!
Since the code will move so should the 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.
moved
pkg/spec/populators.go
Outdated
// Only populate if the port name is not already specified | ||
if len(servicePorts[i].Name) == 0 { | ||
servicePorts[i].Name = serviceName + "-" + strconv.FormatInt(int64(servicePorts[i].Port), 10) | ||
fmt.Println(servicePorts[i].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.
Also remove this debug statement!
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.
whoops, removed
pkg/spec/populators_test.go
Outdated
@@ -21,6 +21,9 @@ import ( | |||
"sort" | |||
"testing" | |||
|
|||
"fmt" |
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 arrange the imports similar to the comment here #149 (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.
fixed
pkg/spec/populators_test.go
Outdated
switch test.success { | ||
case true: | ||
if err != nil { | ||
t.Errorf("Expected test to pass but got an error -\n%v", 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.
this is test failure situtation and you are in a function of a test, so use t.Fatal
.
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 was actually confused why are doing a t.Fatalf
for rest of the tests.
t.Error
marks the function as having failed but continues execution. (FWIW, 2,611 code results in kubernetes/kubernetes)t.Fatal
marks the function as having failed and stops its execution. (FWIW, 596 code results in kubernetes/kubernetes)
There are other checks which should continue to happen in the function if one test fails so the resulting test report is more comprehensive.
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.
Each test here runs in separate function when you do t.Run()
so you have to do t.Fatal()
or else t.Error()
and return
.
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 are other checks which should continue to happen in the function if one test fails so the resulting test report is more comprehensive.
One test fail won't affect other tests, they will run as usual, since each test is running in its own test 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.
No, t.Errorf
does not require to return.
One test fail won't affect other tests, they will run as usual, since each test is running in its own test function.
It does not affect other tests, but the further checks for the same test do not happen when t.Fatal
is used. This cuts down the test reports and does not give a full report of failing 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.
What is the guarantee that the results we get after a failing condition are correct ?
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 not about a guarantee, this is about getting the most extensive test report to troubleshoot the test failures. It does not make sense to fix one test, and then run tests again to find out that another one is failing, and so on.
It's also the used convention, rightfully so, in Kubernetes as well.
pkg/spec/populators_test.go
Outdated
} | ||
} | ||
|
||
if !reflect.DeepEqual(sp, test.servicePort) { |
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 need this check only when tests pass which is case true
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.
For a general testing pattern, if we add a case where we need to test the function output even when an error occurs, we will miss that if we check for this inside case: true
.
e.g.
{
name: "something",
portMapping: "1337:1338::1339/TCP",
servicePort: &api_v1.ServicePort{},
success: false,
},
I think we should keep it outside.
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 can make changes when we need it like that, right now we don'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.
unsure what we lose by keeping it this way
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 good to add things when we need them!
pkg/spec/populators_test.go
Outdated
} | ||
case false: | ||
if err == nil { | ||
t.Errorf("Expected %v to fail, but test passed!", test.portMapping) |
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 test failure situtation and you are in a function of a test, so use t.Fatal
.
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 above
Since this is a feature, can you add a e2e test which has two ports specified and the test checks if both of them are serving ? |
This commit adds a shortcut to ServiceSpec called portMappings. This lets us set the port, targetPort and the protocol for a service in a single line. This is parsed and converted to a Kubernetes ServicePort object. portMappings is an array of `port:targetPort/protocol` definitions, so the syntax looks like - portMappings: - <port:targetPort/protocol> - <port:targetPort/protocol> Also, tests are added to test the added behavior. The logic of auto populating names for the services has been moved from fix.go to populators.go, because now the auto population happens once the []ServicePort is populated from the both the inputs, ServiceSpecMod.Ports and ServiceSpecMod.PortMappings Fixes kedgeproject#216
84efe7f
to
304c6f4
Compare
} | ||
|
||
if !reflect.DeepEqual(sp, test.servicePort) { | ||
t.Errorf("Expected ServicePort to be -\n%v\nBut got -\n%v", spew.Sprint(test.servicePort), spew.Sprint(sp)) |
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.
Forked the discussion from #242 (comment)
Consider a case if the test is expected to pass, but it fails then the test should not go any further than
t.Errorf("Expected test to pass but got an error -\n%v", err)
which is line #3315
because we are sure that it will surely throw error at line #324 which is
t.Errorf("Expected ServicePort to be -\n%v\nBut got -\n%v", spew.Sprint(test.servicePort), spew.Sprint(sp))
because the variable sp
is nil
because of failed test. So if we know further in path if a test is surely gonna fail, I don't think there is point in continuing atleast in this case. Because that won't be comprehensive test output but useless verbose output on a sure thing that is always gonna happen.
For e.g. consider:
$ go test -v github.com/kedgeproject/kedge/pkg/spec -run TestParsePortMapping
=== RUN TestParsePortMapping
=== RUN TestParsePortMapping/Nothing_is_passed,_not_even_port
=== RUN TestParsePortMapping/Only_'port'_is_passed
=== RUN TestParsePortMapping/port:targetPort_is_passed
=== RUN TestParsePortMapping/port/protocol_is_passed
=== RUN TestParsePortMapping/port:targetPort/protocol_is_passed
=== RUN TestParsePortMapping/Invalid_protocol_(neither_TCP_nor_UDP)_is_passed
=== RUN TestParsePortMapping/Multiple_protocols_passed,_multiple_'/'_test
=== RUN TestParsePortMapping/Non_int_port_is_passed
=== RUN TestParsePortMapping/Non_int_targetPort_is_passed
=== RUN TestParsePortMapping/More_than_2_ports_passed,_multiple_':'_test
--- FAIL: TestParsePortMapping (0.00s)
--- PASS: TestParsePortMapping/Nothing_is_passed,_not_even_port (0.00s)
--- PASS: TestParsePortMapping/Only_'port'_is_passed (0.00s)
--- PASS: TestParsePortMapping/port:targetPort_is_passed (0.00s)
--- PASS: TestParsePortMapping/port/protocol_is_passed (0.00s)
--- FAIL: TestParsePortMapping/port:targetPort/protocol_is_passed (0.00s)
resources_test.go:315: Expected test to pass but got an error -
port is not an int: strconv.ParseInt: parsing "": invalid syntax
resources_test.go:324: Expected ServicePort to be -
<*>&ServicePort{Name:,Protocol:UDP,Port:1337,TargetPort:1338,NodePort:0,}
But got -
<nil>
--- PASS: TestParsePortMapping/Invalid_protocol_(neither_TCP_nor_UDP)_is_passed (0.00s)
--- PASS: TestParsePortMapping/Multiple_protocols_passed,_multiple_'/'_test (0.00s)
--- PASS: TestParsePortMapping/Non_int_port_is_passed (0.00s)
--- PASS: TestParsePortMapping/Non_int_targetPort_is_passed (0.00s)
--- PASS: TestParsePortMapping/More_than_2_ports_passed,_multiple_':'_test (0.00s)
FAIL
exit status 1
FAIL github.com/kedgeproject/kedge/pkg/spec 0.044s
So for failed test only this is sufficient
resources_test.go:315: Expected test to pass but got an error -
port is not an int: strconv.ParseInt: parsing "": invalid syntax
We don't need to have the next set of lines:
resources_test.go:324: Expected ServicePort to be -
<*>&ServicePort{Name:,Protocol:UDP,Port:1337,TargetPort:1338,NodePort:0,}
But got -
<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.
A function like the following is being tested -
func a() {
behavior x
behavior y
behavior z
}
We write a test -
func TestA() {
t.Run("test a()", func(t *testing.T) {
t.Fatal("fail if behavior x fails")
t.Fatal("fail if behavior y fails")
t.Fatal("fail if behavior z fails")
}
}
Now, when behavior x fails, behavior y and behavior z will not be tested, in our test report we will only get failure for behavior x. When we fix that, we will then get an error for behavior y, then after fixing that we get the report for behavior z.
OTOH, if we use -
func TestA() {
t.Run("test a()", func(t *testing.T) {
t.Error("fail if behavior x fails")
t.Error("fail if behavior y fails")
t.Error("fail if behavior z fails")
}
}
In this case we will get test failure reports for all the behaviors x, y and z at the same time and we can go ahead and do the fixes simultaneously.
The point of a test report is to find all the failures happening in the tests and making the report as extensive as we can so we can get what's breaking what, instead not to find failures in an iterative manner.
docs/examples/portMappings/README.md
Outdated
services: | ||
- name: httpd | ||
portMappings: | ||
- 8080:80/TCP |
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.
Similar to #242 (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.
changed
api_v1 "k8s.io/client-go/pkg/api/v1" | ||
|
||
"github.com/pkg/errors" |
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 arrange the imports similar to the comment here #149 (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.
There are no kedge imports here, these are in the order of stdlib first, then third party. What are you suggesting?
"reflect" | ||
"sort" | ||
"testing" | ||
|
||
api_v1 "k8s.io/client-go/pkg/api/v1" | ||
|
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: s/\n//
can you arrange the imports similar to the comment here #149 (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.
This is similar to what's done here - https://github.com/kedgeproject/kedge/blob/master/pkg/spec/resources.go#L19-L38 - what's wrong?
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 this is a feature, can you add a e2e test which has two ports specified and the test checks if both of them are serving ?
This commit adds a shortcut to ServiceSpec called portMappings.
This lets us set the port, targetPort and the protocol for a
service in a single line. This is parsed and converted to a
Kubernetes ServicePort object.
portMappings is an array of
port:targetPort/protocol
definitions, so the syntax looks like -
portMappings:
Also, tests are added to test the added behavior.
The logic of auto populating names for the services has been moved
from fix.go to populators.go, because now the auto population
happens once the []ServicePort is populated from the both the
inputs, ServiceSpecMod.Ports and ServiceSpecMod.PortMappings
Fixes #216