-
Notifications
You must be signed in to change notification settings - Fork 8.3k
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
Split implementations from generic code #15
Conversation
d263e52
to
969bab6
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'm confused about the interface. I thought we wanted to define update/reload methods to which we pass a list of structs that contain all the ep/service/nodeport information required to deserialize into valid nginx/haproxy/gce conf? but this interface only has start/stop etc. Is this just an intermediate step?
@@ -325,6 +337,12 @@ Amongst others [ELBs in AWS](http://docs.aws.amazon.com/ElasticLoadBalancing/lat | |||
Please check the [proxy-protocol](examples/proxy-protocol/) example | |||
|
|||
|
|||
## Service Integration |
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.
Didn't we decide to delete READMEs for now to avoid drift and confusion?
@@ -0,0 +1,22 @@ | |||
package = "lua-resty-http" |
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 clarify why we need any of the lua stuff?
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. The lua dependencies are required when you enable custom errors (is not possible to create a request in nginx)
|
||
// Interface holds the methods to handle an Ingress backend | ||
type Interface interface { | ||
Start() |
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 add godocs. Comments from the other pr still valid, it's weird that this interface doesn't take any arguments or return 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.
Don't we need some sort of Update/Sync() method so the backend can write out a config with new endpoints before reload?
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 the correct interface https://github.com/kubernetes/ingress/pull/15/files#diff-9cef6a58a4eaf42843bd0db5b4a2f018R40
2c6ccda
to
1f57a94
Compare
@bprashanth ping |
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'd like to iterate on the interface first, left some initial comments. Would be nice if just types.go was in a different commit.
DefaultSSLDirectory = "/ingress-controller/ssl" | ||
) | ||
|
||
// Controller holds the methods to handle an Ingress backend |
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.
Naming this controller is confusing because it isn't the controller right? it's the backend.
Suggest a comment like:
Backend is the interface between the generic controller and various ingress backends.
type Controller interface { | ||
// Start executes a command to start the backend. | ||
// The command must run in foreground. | ||
Start() |
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 does foreground mean in this context? I mean, if I'm a cloudprovider, is Start supposed to just hang?
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.
Do we need both a start and a reload?
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.
Do we need both a start and a reload?
yes because we need to track the status of the backend command execution (usually a reload command in nginx,haproxy or caddy signal the master process to spawn new child and terminate the old ones after the existing connections end)
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 does foreground mean in this context? I mean, if I'm a cloudprovider, is Start supposed to just hang?
yes? I'm not sure how to deal in a generic way. Please advice how this should be handled
// The command must run in foreground. | ||
Start() | ||
// Stop stops the backend | ||
Stop() 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.
Please clarify that this is a terminal state, i.e after a stop invocation, you will only get killed not restarted (right? becuase we only call it on sigterm and then have terminationGracePeriodSeconds to clean up after which we get a sigkill).
Stop() error | ||
// Restart signal the backend with a new configuration file returning | ||
// the combined output of Stdout and Stderr | ||
Restart(data []byte) ([]byte, 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.
You need to be more specific about what the byte arrays are, because byte arrays are confusing in general. Also this method is central to the integration so I'd rather over document it than under document it.
Restart takes a byte array representing the new loadbalancer configuration, and
returns a byte array containing any output/errors from the backend. Before returning,
the backend must load the configuration in the given array into the loadbalancer and
restart it, or fail with an error and message string. If reloading fails, the config should
be reverted to the last known good configuration, NOT the given byte array.
2 more questions:
- why return a byte array instead of a string?
- how do we differentiate failure to reload vs something else like failure to write config to disk or cloudprovider?
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.
- why return a byte array instead of a string?
no particular reason.
- how do we differentiate failure to reload vs something else like failure to write config to disk or cloudprovider?
I think the reason is irrelevant. The important part is to NOT modify the running configuration file in case of any error.
Restart(data []byte) ([]byte, error) | ||
// Tests returns the commands to execute that verifies if the configuration file is valid | ||
// Example: nginx -t -c <file> | ||
Test(file string) *exec.Cmd |
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 interface is weird, why not just give it the same byte array we give to Restart and have it return an 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.
good point. The reason is because the backend (nginx,haproxy,caddy) execute a command to test the proposed new configuration without replacing the existing one (this creates a temporal file)
|
||
var ( | ||
// DefaultSSLDirectory defines the location where the SSL certificates will be generated | ||
DefaultSSLDirectory = "/ingress-controller/ssl" |
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 make this a flag (a TODO is fine)? Can you also clarify what certs are generated here exactly? are your user secrets stored here? or is this the default snakeoil that the controller uses?
Info() string | ||
} | ||
|
||
// Configuration contains the pieces |
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 elaborate
// Configuration contains the pieces | ||
type Configuration struct { | ||
HealthzURL string | ||
Upstreams []*Upstream |
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 either need to:
- Be crystal clear about what an upstream is in docs and examples
- Replace upstream with something else
Maybe we can just use backends/endpoints/server etc (something more natural in this context)?
// Upstream describes one or more remote server/s (endpoints) associated with a service | ||
type Upstream struct { | ||
// Secure indicates if the communication with the endpoint must use TLS | ||
Secure 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.
Why not just call this HSTS?
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 indicates the protocol to be used between nginx and the endpoint, not from the user.
I will clarify this
// Name represents an unique api.Service name formatted as <namespace>-<name>-<port> | ||
Name string | ||
// Backends contains the list of endpoints | ||
Backends []UpstreamServer |
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.
Maybe we can call this endpoints and use backens for upstream?
2e19988
to
7576f10
Compare
done. Last commit contains types.go Edit: I'll wait to finish the interface before refactoring the code :) |
|
||
// BackendInfo returns information about the backend. | ||
// This fields contains information that helps to track issues or to | ||
// map the running ingress controller to source code |
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 all fields in types.go please add a json tag like https://github.com/kubernetes/kubernetes/blob/master/pkg/api/types.go#L80 and mark the ones that are optional as omitempty
. Name for example, is that just informational? i.e requires 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.
Please also add a godoc for every public field or method in this file.
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
FailTimeout int | ||
} | ||
|
||
// Server describes a virtual server |
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 clarify what a "virtual" server exactly is, or if there's docs somewhere please point to that
// Server describes a virtual server | ||
type Server struct { | ||
// Name contains the FQDN | ||
Name 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.
why not just call this hostname to avoid confusion with other Name
fields?
// SSLPassthrough indicates if the TLS termination is realized in the server or in the remote endpoint | ||
SSLPassthrough bool | ||
|
||
SSLCertificate 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.
Is this the entier cert as a string, or a path?
|
||
SSLCertificate string | ||
//SSLCertificateKey string | ||
SSLPemChecksum 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.
please clarify how this checksum is computed
Backends []*Backend | ||
Servers []*Server | ||
TCPEndpoints []*Location | ||
UPDEndpoints []*Location |
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: can you reflow the structs so they're
Configuration
Server
Location
Backend
So people reading the code see a natural progression?
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
HealthzURL string | ||
Backends []*Backend | ||
Servers []*Server | ||
TCPEndpoints []*Location |
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 somehow get rid of these, and use Servers as the one contruct that leads us to endpoitns ? I mean, a server without any virtual host information + protocol=TCP is assumed to contain a localtion for a tcp endpoint, and the same for udp?
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'd consider clear is if this struct boiled down to a list of servers, each of which contained a list of locations, each of which had a set of endpoints.
SecureBackend bool | ||
EnableCORS bool | ||
Path string | ||
Backend Backend |
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 that we have a list of backensd in Configuration
as well as a Backend in here.
DefaultSSLDirectory = "/ingress-controller/ssl" | ||
) | ||
|
||
// Controller holds the methods to handle an Ingress backend |
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 add
// TODO (#18): Make sure this is sufficiently supportive of other backends.
Also please add a doc.go in this package with some text description of how to use the interface
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
5f126ec
to
08d4ba4
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.
Almost there, just a bunch of comment and clarity updates. I think the api needs to be crystal clear, and even though we're iterating I'd like to get a decent first cut so please bear with me :)
Repository string `json:"repository"` | ||
} | ||
|
||
// Configuration holds the definition of all the parts required to describe |
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 finish the sentence, describe a single Ingress, or all ingresses in the cluster?
// Setting 0 indicates that the check is performed by a Kubernetes probe | ||
MaxFails int `json:"maxFails"` | ||
|
||
FailTimeout int `json:"failTimeout"` |
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.
godoc?
// SSLPassthroughBackend describes a SSL upstream server configured | ||
// as passthrough (no TLS termination in the ingress controller) | ||
type SSLPassthroughBackend struct { | ||
Backend `json:"namespace,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.
godoc?
|
||
// SSLCert describes a SSL certificate to be used in a server | ||
type SSLCert struct { | ||
api.ObjectMeta `json:"metadata,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.
why embed an api object into a custom type?
// This fields contains information that helps to track issues or to | ||
// map the running ingress controller to source code | ||
type BackendInfo struct { | ||
// Name returns the name of the backend |
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 comment could use some clarification since it's required not optional, what does name imply? does it follow some format, or can it be a random hash/alphanumerics etc
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
|
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 add some comments about what this package contains, eg: https://github.com/kubernetes/kubernetes/blob/master/pkg/api/doc.go#L19
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
Path string `json:"path"` | ||
// IsDefBackend indicates if service specified in the Ingress | ||
// contains active endpoints or not. | ||
IsDefBackend bool `json:"isDefBackend"` |
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 explain how the name matches this comment?
IsDefBackend makes me think of default backned, but the comment makes me think of unready endpoints? What should the backend implementation do if IsDefBackend=false?
// authentication using an external provider | ||
// +optional | ||
ExternalAuth authreq.External `json:"externalAuth,omitempty"` | ||
// RateLimit describes |
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 finish the sentence, I guess it describes a rate limit for this specific path?
// +optional | ||
RateLimit ratelimit.RateLimit `json:"rateLimit,omitempty"` | ||
// Redirect describes the redirection this location | ||
// +optional |
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 clarify, if set, the path should redirect to the set location? is this also subject to the ratelimit field?
// Name represents an unique api.Service name formatted as <namespace>-<name>-<port> | ||
Name string `json:"name"` | ||
// This indicates if the communication protocol between the backend and the endpoint is HTTP or HTTPS | ||
// Allowing the use of HTTPS |
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 set to true, the ingress backend uses https for all communication to the endpoints (is this true?) including health checks. The endpoint needs to provide certificates with a valid hostname/ip (right? or is any self signed cert ok? if so please elaborate).
@bprashanth |
Assume you're going to change as part of this pr, let me know if you'd rather send a follow up |
Yes and fix the test. This must provide a working controller (please do not merge) |
049da97
to
807afde
Compare
Re: your previous comment, tests passed, good to merge? |
@bprashanth not yet :)
ok |
|
||
// Check returns if the nginx healthz endpoint is returning ok (status code 200) | ||
func (ic GenericController) Check(_ *http.Request) error { | ||
res, err := http.Get("http://127.0.0.1:18080/healthz") |
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.
@aledbf this seems to be very specific to the nginx-based ingress controller, yet is in core/pkg, which I assume is supposed to be generic?
Could you please explain why this is so?
I don't mean this to be criticism, I'm new to this, and just trying to understand this codebase. Is this an example implementation - to be overriden elsewhere, or are all ingress controller running an nginx internally, or is this meant to go away in the future, or am I completely misunderstanding what "generic" means in this context?
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.
@porridge you are right. This should use an URL defined by the backend
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 an example implementation
GenericController provides the boilerplate and should not contain specific information about the backend (like nginx). The idea is that if you want to write an ingress controller for XX it should be similar to https://github.com/kubernetes/ingress/blob/master/controllers/nginx/pkg/cmd/controller/nginx.go (only the template part)
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 mean this to be criticism, I'm new to this, and just trying to understand this codebase.
Please keep doing this
proxy_responses 1; | ||
proxy_pass udp-{{ $udpServer.Upstream.Name }}; | ||
} | ||
{{ end }} |
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.
@aledbf sorry for bumping up old stuff, but I can't figure out why TCP services and UDP services were removed in this commit.
In current master
the TCP & UDP services are present in template config and are passed to template rendering, but missed in nginx.tmpl
at all.
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.
@tyranron yes, I removed the stream section because it requires changes.
This is the PR I need to open to fix that. https://github.com/kubernetes/ingress/compare/master...aledbf:fix-tcp-stream?expand=1
I will do this after #95 and #123
remove lic from dockerfile
No description provided.