Skip to content
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

Merged
merged 5 commits into from
Nov 24, 2016

Conversation

aledbf
Copy link
Member

@aledbf aledbf commented Nov 11, 2016

No description provided.

Copy link
Contributor

@bprashanth bprashanth left a 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
Copy link
Contributor

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"
Copy link
Contributor

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?

Copy link
Member Author

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()
Copy link
Contributor

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.

Copy link
Contributor

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?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aledbf aledbf force-pushed the merge-aledbf branch 4 times, most recently from 2c6ccda to 1f57a94 Compare November 12, 2016 18:45
@aledbf
Copy link
Member Author

aledbf commented Nov 15, 2016

@bprashanth ping

@aledbf aledbf changed the title WIP: Split implementations from generic code Split implementations from generic code Nov 15, 2016
Copy link
Contributor

@bprashanth bprashanth left a 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
Copy link
Contributor

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()
Copy link
Contributor

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?

Copy link
Contributor

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?

Copy link
Member Author

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)

Copy link
Member Author

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
Copy link
Contributor

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)
Copy link
Contributor

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:

  1. why return a byte array instead of a string?
  2. how do we differentiate failure to reload vs something else like failure to write config to disk or cloudprovider?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. why return a byte array instead of a string?

no particular reason.

  1. 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
Copy link
Contributor

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?

Copy link
Member Author

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"
Copy link
Contributor

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
Copy link
Contributor

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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We either need to:

  1. Be crystal clear about what an upstream is in docs and examples
  2. 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
Copy link
Contributor

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?

Copy link
Member Author

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
Copy link
Contributor

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?

@aledbf aledbf force-pushed the merge-aledbf branch 2 times, most recently from 2e19988 to 7576f10 Compare November 16, 2016 19:18
@aledbf
Copy link
Member Author

aledbf commented Nov 16, 2016

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.

done. Last commit contains types.go

Edit: I'll wait to finish the interface before refactoring the code :)
(meaning this will not pass the tests)


// 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
Copy link
Contributor

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

Copy link
Contributor

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.

Copy link
Member Author

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
Copy link
Contributor

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
Copy link
Contributor

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
Copy link
Contributor

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
Copy link
Contributor

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
Copy link
Contributor

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?

Copy link
Member Author

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
Copy link
Contributor

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?

Copy link
Contributor

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
Copy link
Contributor

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
Copy link
Contributor

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

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Contributor

@bprashanth bprashanth left a 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
Copy link
Contributor

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"`
Copy link
Contributor

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"`
Copy link
Contributor

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"`
Copy link
Contributor

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
Copy link
Contributor

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.
*/

Copy link
Contributor

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

Copy link
Member Author

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"`
Copy link
Contributor

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
Copy link
Contributor

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
Copy link
Contributor

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
Copy link
Contributor

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).

@aledbf
Copy link
Member Author

aledbf commented Nov 19, 2016

@bprashanth I give up.

@aledbf aledbf closed this Nov 19, 2016
@aledbf aledbf reopened this Nov 22, 2016
@k8s-ci-robot k8s-ci-robot added the cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. label Nov 22, 2016
@bprashanth
Copy link
Contributor

Assume you're going to change as part of this pr, let me know if you'd rather send a follow up

@aledbf
Copy link
Member Author

aledbf commented Nov 23, 2016

Assume you're going to change as part of this pr

Yes and fix the test. This must provide a working controller

(please do not merge)

@aledbf aledbf force-pushed the merge-aledbf branch 5 times, most recently from 049da97 to 807afde Compare November 23, 2016 23:20
@bprashanth
Copy link
Contributor

Re: your previous comment, tests passed, good to merge?
Please squash the "fix x" commits so we don't end up with a partial revert by mistake

@aledbf
Copy link
Member Author

aledbf commented Nov 24, 2016

@bprashanth not yet :)
Fixing last issue.

Please squash the "fix x" commits so we don't end up with a partial revert by mistake

ok

@aledbf aledbf merged commit 850af93 into kubernetes:master Nov 24, 2016

// 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")
Copy link
Member

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?

Copy link
Member Author

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

Copy link
Member Author

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)

Copy link
Member Author

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 }}

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.

Copy link
Member Author

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

haoqing0110 referenced this pull request in stolostron/management-ingress Mar 5, 2021
haoqing0110 referenced this pull request in stolostron/management-ingress Mar 5, 2021
Alvaro-Campesino added a commit to Alvaro-Campesino/ingress-nginx-k8s that referenced this pull request Jan 19, 2023
Alvaro-Campesino added a commit to Alvaro-Campesino/ingress-nginx-k8s that referenced this pull request Jan 24, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. lgtm "Looks good to me", indicates that a PR is ready to be merged.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants