Add Generic interface
This commit is contained in:
parent
f2b627486d
commit
5a8e090736
36 changed files with 58014 additions and 675 deletions
|
@ -30,5 +30,5 @@ script:
|
|||
# enable kubernetes/ingress in
|
||||
# coveralls.io and add cover task
|
||||
- make fmt lint vet test
|
||||
- make controllers controllers-images
|
||||
#- make controllers controllers-images
|
||||
#- make test-e2e
|
||||
|
|
|
@ -25,14 +25,16 @@ import (
|
|||
|
||||
"github.com/golang/glog"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
|
||||
"k8s.io/ingress/core/pkg/ingress"
|
||||
"k8s.io/ingress/core/pkg/ingress/defaults"
|
||||
|
||||
"errors"
|
||||
|
||||
"k8s.io/ingress/controllers/nginx/pkg/config"
|
||||
ngx_template "k8s.io/ingress/controllers/nginx/pkg/template"
|
||||
"k8s.io/ingress/controllers/nginx/pkg/version"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -75,6 +77,8 @@ Error loading new template : %v
|
|||
}
|
||||
|
||||
n.t = ngxTpl
|
||||
go n.Start()
|
||||
|
||||
return n
|
||||
}
|
||||
|
||||
|
@ -85,7 +89,7 @@ type NGINXController struct {
|
|||
binary string
|
||||
}
|
||||
|
||||
// Start ...
|
||||
// Start start a new NGINX master process running in foreground.
|
||||
func (n NGINXController) Start() {
|
||||
glog.Info("starting NGINX process...")
|
||||
cmd := exec.Command(n.binary, "-c", cfgPath)
|
||||
|
@ -99,14 +103,13 @@ func (n NGINXController) Start() {
|
|||
}
|
||||
}
|
||||
|
||||
// Stop ...
|
||||
func (n NGINXController) Stop() error {
|
||||
n.t.Close()
|
||||
return exec.Command(n.binary, "-s", "stop").Run()
|
||||
// Reload checks if the running configuration file is different
|
||||
// to the specified and reload nginx if required
|
||||
func (n NGINXController) Reload(data []byte) ([]byte, error) {
|
||||
if !n.isReloadRequired(data) {
|
||||
return nil, fmt.Errorf("Reload not required")
|
||||
}
|
||||
|
||||
// Restart ...
|
||||
func (n NGINXController) Restart(data []byte) ([]byte, error) {
|
||||
err := ioutil.WriteFile(cfgPath, data, 0644)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -120,15 +123,15 @@ func (n NGINXController) Test(file string) *exec.Cmd {
|
|||
return exec.Command(n.binary, "-t", "-c", file)
|
||||
}
|
||||
|
||||
// UpstreamDefaults returns the nginx defaults
|
||||
func (n NGINXController) UpstreamDefaults() defaults.Backend {
|
||||
// BackendDefaults returns the nginx defaults
|
||||
func (n NGINXController) BackendDefaults() defaults.Backend {
|
||||
d := config.NewDefault()
|
||||
return d.Backend
|
||||
}
|
||||
|
||||
// IsReloadRequired check if the new configuration file is different
|
||||
// from the current one.
|
||||
func (n NGINXController) IsReloadRequired(data []byte) bool {
|
||||
func (n NGINXController) isReloadRequired(data []byte) bool {
|
||||
in, err := os.Open(cfgPath)
|
||||
if err != nil {
|
||||
return false
|
||||
|
@ -167,8 +170,13 @@ func (n NGINXController) IsReloadRequired(data []byte) bool {
|
|||
}
|
||||
|
||||
// Info return build information
|
||||
func (n NGINXController) Info() string {
|
||||
return fmt.Sprintf("build version %v from repo %v commit %v", version.RELEASE, version.REPO, version.COMMIT)
|
||||
func (n NGINXController) Info() *ingress.BackendInfo {
|
||||
return &ingress.BackendInfo{
|
||||
Name: "NGINX",
|
||||
Release: version.RELEASE,
|
||||
Build: version.COMMIT,
|
||||
Repository: version.REPO,
|
||||
}
|
||||
}
|
||||
|
||||
// testTemplate checks if the NGINX configuration inside the byte array is valid
|
||||
|
@ -183,12 +191,13 @@ func (n NGINXController) testTemplate(cfg []byte) error {
|
|||
out, err := n.Test(tmpfile.Name()).CombinedOutput()
|
||||
if err != nil {
|
||||
// this error is different from the rest because it must be clear why nginx is not working
|
||||
return fmt.Errorf(`
|
||||
oe := fmt.Sprintf(`
|
||||
-------------------------------------------------------------------------------
|
||||
Error: %v
|
||||
%v
|
||||
-------------------------------------------------------------------------------
|
||||
`, err, string(out))
|
||||
return errors.New(oe)
|
||||
}
|
||||
|
||||
os.Remove(tmpfile.Name())
|
||||
|
@ -207,9 +216,9 @@ func (n NGINXController) OnUpdate(cmap *api.ConfigMap, ingressCfg ingress.Config
|
|||
var longestName int
|
||||
var serverNames int
|
||||
for _, srv := range ingressCfg.Servers {
|
||||
serverNames += len([]byte(srv.Name))
|
||||
if longestName < len(srv.Name) {
|
||||
longestName = len(srv.Name)
|
||||
serverNames += len([]byte(srv.Hostname))
|
||||
if longestName < len(srv.Hostname) {
|
||||
longestName = len(srv.Hostname)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -234,21 +243,17 @@ func (n NGINXController) OnUpdate(cmap *api.ConfigMap, ingressCfg ingress.Config
|
|||
cfg.ServerNameHashMaxSize = serverNameHashMaxSize
|
||||
}
|
||||
|
||||
conf := make(map[string]interface{})
|
||||
// adjust the size of the backlog
|
||||
conf["backlogSize"] = sysctlSomaxconn()
|
||||
conf["upstreams"] = ingressCfg.Upstreams
|
||||
conf["passthroughUpstreams"] = ingressCfg.PassthroughUpstreams
|
||||
conf["servers"] = ingressCfg.Servers
|
||||
conf["tcpUpstreams"] = ingressCfg.TCPEndpoints
|
||||
conf["udpUpstreams"] = ingressCfg.UPDEndpoints
|
||||
conf["healthzURL"] = ingressCfg.HealthzURL
|
||||
conf["defResolver"] = cfg.Resolver
|
||||
conf["sslDHParam"] = ""
|
||||
conf["customErrors"] = len(cfg.CustomHTTPErrors) > 0
|
||||
conf["cfg"] = ngx_template.StandarizeKeyNames(cfg)
|
||||
|
||||
return n.t.Write(conf, n.testTemplate)
|
||||
return n.t.Write(config.TemplateConfig{
|
||||
BacklogSize: sysctlSomaxconn(),
|
||||
Backends: ingressCfg.Backends,
|
||||
PassthrougBackends: ingressCfg.PassthroughBackends,
|
||||
Servers: ingressCfg.Servers,
|
||||
TCPBackends: ingressCfg.TCPEndpoints,
|
||||
UDPBackends: ingressCfg.UPDEndpoints,
|
||||
HealthzURI: "/healthz",
|
||||
CustomErrors: len(cfg.CustomHTTPErrors) > 0,
|
||||
Cfg: cfg,
|
||||
}, n.testTemplate)
|
||||
}
|
||||
|
||||
// http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
|
||||
|
|
|
@ -19,9 +19,10 @@ package config
|
|||
import (
|
||||
"runtime"
|
||||
|
||||
"k8s.io/ingress/core/pkg/ingress/defaults"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
"k8s.io/ingress/core/pkg/ingress"
|
||||
"k8s.io/ingress/core/pkg/ingress/defaults"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -216,8 +217,7 @@ type Configuration struct {
|
|||
WorkerProcesses int `structs:"worker-processes,omitempty"`
|
||||
}
|
||||
|
||||
// NewDefault returns the default configuration contained
|
||||
// in the file default-conf.json
|
||||
// NewDefault returns the default nginx configuration
|
||||
func NewDefault() Configuration {
|
||||
cfg := Configuration{
|
||||
BodySize: bodySize,
|
||||
|
@ -264,3 +264,15 @@ func NewDefault() Configuration {
|
|||
|
||||
return cfg
|
||||
}
|
||||
|
||||
type TemplateConfig struct {
|
||||
BacklogSize int
|
||||
Backends []*ingress.Backend
|
||||
PassthrougBackends []*ingress.SSLPassthroughBackend
|
||||
Servers []*ingress.Server
|
||||
TCPBackends []*ingress.Location
|
||||
UDPBackends []*ingress.Location
|
||||
HealthzURI string
|
||||
CustomErrors bool
|
||||
Cfg Configuration
|
||||
}
|
||||
|
|
|
@ -27,10 +27,10 @@ import (
|
|||
"github.com/mitchellh/mapstructure"
|
||||
go_camelcase "github.com/segmentio/go-camelcase"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
|
||||
"k8s.io/ingress/controllers/nginx/pkg/config"
|
||||
"k8s.io/ingress/core/pkg/ingress/defaults"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -50,9 +50,9 @@ func ReadConfig(conf *api.ConfigMap) config.Configuration {
|
|||
return config.NewDefault()
|
||||
}
|
||||
|
||||
var errors []int
|
||||
var skipUrls []string
|
||||
var whitelist []string
|
||||
errors := make([]int, 0)
|
||||
skipUrls := make([]string, 0)
|
||||
whitelist := make([]string, 0)
|
||||
|
||||
if val, ok := conf.Data[customHTTPErrors]; ok {
|
||||
delete(conf.Data, customHTTPErrors)
|
||||
|
|
|
@ -27,6 +27,7 @@ import (
|
|||
|
||||
"github.com/golang/glog"
|
||||
|
||||
"k8s.io/ingress/controllers/nginx/pkg/config"
|
||||
"k8s.io/ingress/core/pkg/ingress"
|
||||
"k8s.io/ingress/core/pkg/watch"
|
||||
)
|
||||
|
@ -73,12 +74,19 @@ func (t *Template) Close() {
|
|||
|
||||
// Write populates a buffer using a template with NGINX configuration
|
||||
// and the servers and upstreams created by Ingress rules
|
||||
func (t *Template) Write(conf map[string]interface{},
|
||||
isValidTemplate func([]byte) error) ([]byte, error) {
|
||||
|
||||
func (t *Template) Write(conf config.TemplateConfig, isValidTemplate func([]byte) error) ([]byte, error) {
|
||||
defer t.tmplBuf.Reset()
|
||||
defer t.outCmdBuf.Reset()
|
||||
|
||||
defer func() {
|
||||
if t.s < t.tmplBuf.Cap() {
|
||||
glog.V(2).Infof("adjusting template buffer size from %v to %v", t.s, t.tmplBuf.Cap())
|
||||
t.s = t.tmplBuf.Cap()
|
||||
t.tmplBuf = bytes.NewBuffer(make([]byte, 0, t.tmplBuf.Cap()))
|
||||
t.outCmdBuf = bytes.NewBuffer(make([]byte, 0, t.outCmdBuf.Cap()))
|
||||
}
|
||||
}()
|
||||
|
||||
if glog.V(3) {
|
||||
b, err := json.Marshal(conf)
|
||||
if err != nil {
|
||||
|
@ -88,12 +96,8 @@ func (t *Template) Write(conf map[string]interface{},
|
|||
}
|
||||
|
||||
err := t.tmpl.Execute(t.tmplBuf, conf)
|
||||
|
||||
if t.s < t.tmplBuf.Cap() {
|
||||
glog.V(2).Infof("adjusting template buffer size from %v to %v", t.s, t.tmplBuf.Cap())
|
||||
t.s = t.tmplBuf.Cap()
|
||||
t.tmplBuf = bytes.NewBuffer(make([]byte, 0, t.tmplBuf.Cap()))
|
||||
t.outCmdBuf = bytes.NewBuffer(make([]byte, 0, t.outCmdBuf.Cap()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// squeezes multiple adjacent empty lines to be single
|
||||
|
@ -129,7 +133,7 @@ var (
|
|||
"buildProxyPass": buildProxyPass,
|
||||
"buildRateLimitZones": buildRateLimitZones,
|
||||
"buildRateLimit": buildRateLimit,
|
||||
"getSSPassthroughUpstream": getSSPassthroughUpstream,
|
||||
"buildSSPassthroughUpstreams": buildSSPassthroughUpstreams,
|
||||
|
||||
"contains": strings.Contains,
|
||||
"hasPrefix": strings.HasPrefix,
|
||||
|
@ -139,13 +143,32 @@ var (
|
|||
}
|
||||
)
|
||||
|
||||
func getSSPassthroughUpstream(input interface{}) string {
|
||||
s, ok := input.(*ingress.Server)
|
||||
if !ok {
|
||||
return ""
|
||||
func buildSSPassthroughUpstreams(b interface{}, sslb interface{}) string {
|
||||
backends := b.([]*ingress.Backend)
|
||||
sslBackends := sslb.([]*ingress.SSLPassthroughBackend)
|
||||
buf := bytes.NewBuffer(make([]byte, 0, 10))
|
||||
|
||||
// multiple services can use the same upstream.
|
||||
// avoid duplications using a map[name]=true
|
||||
u := make(map[string]bool)
|
||||
for _, passthrough := range sslBackends {
|
||||
if u[passthrough.Backend] {
|
||||
continue
|
||||
}
|
||||
u[passthrough.Backend] = true
|
||||
fmt.Fprintf(buf, "upstream %v {\n", passthrough.Backend)
|
||||
for _, backend := range backends {
|
||||
if backend.Name == passthrough.Backend {
|
||||
for _, server := range backend.Endpoints {
|
||||
fmt.Fprintf(buf, "\t\tserver %v:%v;\n", server.Address, server.Port)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
fmt.Fprint(buf, "\t}\n\n")
|
||||
}
|
||||
|
||||
return s.Name
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// buildLocation produces the location string, if the ingress has redirects
|
||||
|
@ -184,20 +207,27 @@ func buildAuthLocation(input interface{}) string {
|
|||
// (specified through the ingress.kubernetes.io/rewrite-to annotation)
|
||||
// If the annotation ingress.kubernetes.io/add-base-url:"true" is specified it will
|
||||
// add a base tag in the head of the response from the service
|
||||
func buildProxyPass(input interface{}) string {
|
||||
location, ok := input.(*ingress.Location)
|
||||
func buildProxyPass(b interface{}, loc interface{}) string {
|
||||
backends := b.([]*ingress.Backend)
|
||||
location, ok := loc.(*ingress.Location)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
|
||||
path := location.Path
|
||||
|
||||
proto := "http"
|
||||
if location.SecureUpstream {
|
||||
|
||||
for _, backend := range backends {
|
||||
if backend.Name == location.Backend {
|
||||
if backend.Secure {
|
||||
proto = "https"
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// defProxyPass returns the default proxy_pass, just the name of the upstream
|
||||
defProxyPass := fmt.Sprintf("proxy_pass %s://%s;", proto, location.Backend.Name)
|
||||
defProxyPass := fmt.Sprintf("proxy_pass %s://%s;", proto, location.Backend)
|
||||
// if the path in the ingress rule is equals to the target: no special rewrite
|
||||
if path == location.Redirect.Target {
|
||||
return defProxyPass
|
||||
|
@ -227,13 +257,13 @@ func buildProxyPass(input interface{}) string {
|
|||
rewrite %s(.*) /$1 break;
|
||||
rewrite %s / break;
|
||||
proxy_pass %s://%s;
|
||||
%v`, path, location.Path, proto, location.Backend.Name, abu)
|
||||
%v`, path, location.Path, proto, location.Backend, abu)
|
||||
}
|
||||
|
||||
return fmt.Sprintf(`
|
||||
rewrite %s(.*) %s/$1 break;
|
||||
proxy_pass %s://%s;
|
||||
%v`, path, location.Redirect.Target, proto, location.Backend.Name, abu)
|
||||
%v`, path, location.Redirect.Target, proto, location.Backend, abu)
|
||||
}
|
||||
|
||||
// default proxy_pass
|
||||
|
|
|
@ -17,14 +17,21 @@ limitations under the License.
|
|||
package template
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"io/ioutil"
|
||||
|
||||
"k8s.io/ingress/controllers/nginx/pkg/config"
|
||||
"k8s.io/ingress/core/pkg/ingress"
|
||||
"k8s.io/ingress/core/pkg/ingress/annotations/rewrite"
|
||||
)
|
||||
|
||||
var (
|
||||
// TODO: add tests for secure endpoints
|
||||
tmplFuncTestcases = map[string]struct {
|
||||
Path string
|
||||
Target string
|
||||
|
@ -88,12 +95,77 @@ func TestBuildProxyPass(t *testing.T) {
|
|||
loc := &ingress.Location{
|
||||
Path: tc.Path,
|
||||
Redirect: rewrite.Redirect{Target: tc.Target, AddBaseURL: tc.AddBaseURL},
|
||||
Upstream: ingress.Backend{Name: "upstream-name"},
|
||||
Backend: "upstream-name",
|
||||
}
|
||||
|
||||
pp := buildProxyPass(loc)
|
||||
pp := buildProxyPass([]*ingress.Backend{}, loc)
|
||||
if !strings.EqualFold(tc.ProxyPass, pp) {
|
||||
t.Errorf("%s: expected \n'%v'\nbut returned \n'%v'", k, tc.ProxyPass, pp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTemplateWithData(t *testing.T) {
|
||||
pwd, _ := os.Getwd()
|
||||
f, err := os.Open(path.Join(pwd, "../../test/data/config.json"))
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error reading json file: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
data, err := ioutil.ReadFile(f.Name())
|
||||
if err != nil {
|
||||
t.Error("unexpected error reading json file: ", err)
|
||||
}
|
||||
var dat config.TemplateConfig
|
||||
if err := json.Unmarshal(data, &dat); err != nil {
|
||||
t.Errorf("unexpected error unmarshalling json: %v", err)
|
||||
}
|
||||
|
||||
tf, err := os.Open(path.Join(pwd, "../../rootfs/etc/nginx/template/nginx.tmpl"))
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error reading json file: %v", err)
|
||||
}
|
||||
defer tf.Close()
|
||||
|
||||
ngxTpl, err := NewTemplate(tf.Name(), func() {})
|
||||
if err != nil {
|
||||
t.Errorf("invalid NGINX template: %v", err)
|
||||
}
|
||||
|
||||
_, err = ngxTpl.Write(dat, func(b []byte) error { return nil })
|
||||
if err != nil {
|
||||
t.Errorf("invalid NGINX template: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkTemplateWithData(b *testing.B) {
|
||||
pwd, _ := os.Getwd()
|
||||
f, err := os.Open(path.Join(pwd, "../../test/data/config.json"))
|
||||
if err != nil {
|
||||
b.Errorf("unexpected error reading json file: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
data, err := ioutil.ReadFile(f.Name())
|
||||
if err != nil {
|
||||
b.Error("unexpected error reading json file: ", err)
|
||||
}
|
||||
var dat config.TemplateConfig
|
||||
if err := json.Unmarshal(data, &dat); err != nil {
|
||||
b.Errorf("unexpected error unmarshalling json: %v", err)
|
||||
}
|
||||
|
||||
tf, err := os.Open(path.Join(pwd, "../../rootfs/etc/nginx/template/nginx.tmpl"))
|
||||
if err != nil {
|
||||
b.Errorf("unexpected error reading json file: %v", err)
|
||||
}
|
||||
defer tf.Close()
|
||||
|
||||
ngxTpl, err := NewTemplate(tf.Name(), func() {})
|
||||
if err != nil {
|
||||
b.Errorf("invalid NGINX template: %v", err)
|
||||
}
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
ngxTpl.Write(dat, func(b []byte) error { return nil })
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
FROM gcr.io/google_containers/nginx-slim:0.10
|
||||
FROM gcr.io/google_containers/nginx-slim:0.11
|
||||
|
||||
RUN DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install -y \
|
||||
diffutils \
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
{{ $cfg := .cfg }}{{ $healthzURL := .healthzURL }}
|
||||
{{ $cfg := .Cfg }}{{ $healthzURI := .HealthzURI }}{{ $backends := .Backends }}
|
||||
daemon off;
|
||||
|
||||
worker_processes {{ $cfg.workerProcesses }};
|
||||
worker_processes {{ $cfg.WorkerProcesses }};
|
||||
pid /run/nginx.pid;
|
||||
worker_rlimit_nofile 131072;
|
||||
|
||||
events {
|
||||
multi_accept on;
|
||||
worker_connections {{ $cfg.maxWorkerConnections }};
|
||||
worker_connections {{ $cfg.MaxWorkerConnections }};
|
||||
use epoll;
|
||||
}
|
||||
|
||||
http {
|
||||
{{/* we use the value of the header X-Forwarded-For to be able to use the geo_ip module */}}
|
||||
{{ if $cfg.useProxyProtocol }}
|
||||
set_real_ip_from {{ $cfg.proxyRealIpCidr }};
|
||||
{{ if $cfg.UseProxyProtocol }}
|
||||
set_real_ip_from {{ $cfg.ProxyRealIpCidr }};
|
||||
real_ip_header proxy_protocol;
|
||||
{{ else }}
|
||||
real_ip_header X-Forwarded-For;
|
||||
|
@ -30,8 +30,8 @@ http {
|
|||
geoip_city /etc/nginx/GeoLiteCity.dat;
|
||||
geoip_proxy_recursive on;
|
||||
|
||||
{{ if $cfg.enableVtsStatus }}
|
||||
vhost_traffic_status_zone shared:vhost_traffic_status:{{ $cfg.vtsStatusZoneSize }};
|
||||
{{ if $cfg.EnableVtsStatus }}
|
||||
vhost_traffic_status_zone shared:vhost_traffic_status:{{ $cfg.VtsStatusZoneSize }};
|
||||
vhost_traffic_status_filter_by_set_key $geoip_country_code country::*;
|
||||
{{ end }}
|
||||
|
||||
|
@ -50,43 +50,43 @@ http {
|
|||
|
||||
reset_timedout_connection on;
|
||||
|
||||
keepalive_timeout {{ $cfg.keepAlive }}s;
|
||||
keepalive_timeout {{ $cfg.KeepAlive }}s;
|
||||
|
||||
types_hash_max_size 2048;
|
||||
server_names_hash_max_size {{ $cfg.serverNameHashMaxSize }};
|
||||
server_names_hash_bucket_size {{ $cfg.serverNameHashBucketSize }};
|
||||
map_hash_bucket_size {{ $cfg.mapHashBucketSize }};
|
||||
server_names_hash_max_size {{ $cfg.ServerNameHashMaxSize }};
|
||||
server_names_hash_bucket_size {{ $cfg.ServerNameHashBucketSize }};
|
||||
map_hash_bucket_size {{ $cfg.MapHashBucketSize }};
|
||||
|
||||
include /etc/nginx/mime.types;
|
||||
default_type text/html;
|
||||
{{ if $cfg.useGzip }}
|
||||
{{ if $cfg.UseGzip }}
|
||||
gzip on;
|
||||
gzip_comp_level 5;
|
||||
gzip_http_version 1.1;
|
||||
gzip_min_length 256;
|
||||
gzip_types {{ $cfg.gzipTypes }};
|
||||
gzip_types {{ $cfg.GzipTypes }};
|
||||
gzip_proxied any;
|
||||
{{ end }}
|
||||
|
||||
client_max_body_size "{{ $cfg.bodySize }}";
|
||||
client_max_body_size "{{ $cfg.BodySize }}";
|
||||
|
||||
log_format upstreaminfo '{{ if $cfg.useProxyProtocol }}$proxy_protocol_addr{{ else }}$remote_addr{{ end }} - '
|
||||
log_format upstreaminfo '{{ if $cfg.UseProxyProtocol }}$proxy_protocol_addr{{ else }}$remote_addr{{ end }} - '
|
||||
'[$proxy_add_x_forwarded_for] - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" '
|
||||
'$request_length $request_time [$proxy_upstream_name] $upstream_addr $upstream_response_length $upstream_response_time $upstream_status';
|
||||
|
||||
{{/* map urls that should not appear in access.log */}}
|
||||
{{/* http://nginx.org/en/docs/http/ngx_http_log_module.html#access_log */}}
|
||||
map $request $loggable {
|
||||
{{ range $reqUri := $cfg.skipAccessLogUrls }}
|
||||
{{ range $reqUri := $cfg.SkipAccessLogURLs }}
|
||||
{{ $reqUri }} 0;{{ end }}
|
||||
default 1;
|
||||
}
|
||||
|
||||
access_log /var/log/nginx/access.log upstreaminfo if=$loggable;
|
||||
error_log /var/log/nginx/error.log {{ $cfg.errorLogLevel }};
|
||||
error_log /var/log/nginx/error.log {{ $cfg.ErrorLogLevel }};
|
||||
|
||||
{{ if not (empty .defResolver) }}# Custom dns resolver.
|
||||
resolver {{ .defResolver }} valid=30s;
|
||||
{{ if not (empty $cfg.Resolver) }}# Custom dns resolver.
|
||||
resolver {{ $cfg.Resolver }} valid=30s;
|
||||
resolver_timeout 10s;
|
||||
{{ end }}
|
||||
|
||||
|
@ -126,49 +126,49 @@ http {
|
|||
server_name_in_redirect off;
|
||||
port_in_redirect off;
|
||||
|
||||
ssl_protocols {{ $cfg.sslProtocols }};
|
||||
ssl_protocols {{ $cfg.SSLProtocols }};
|
||||
|
||||
# turn on session caching to drastically improve performance
|
||||
{{ if $cfg.sslSessionCache }}
|
||||
ssl_session_cache builtin:1000 shared:SSL:{{ $cfg.sslSessionCacheSize }};
|
||||
ssl_session_timeout {{ $cfg.sslSessionTimeout }};
|
||||
{{ if $cfg.SSLSessionCache }}
|
||||
ssl_session_cache builtin:1000 shared:SSL:{{ $cfg.SSLSessionCacheSize }};
|
||||
ssl_session_timeout {{ $cfg.SSLSessionTimeout }};
|
||||
{{ end }}
|
||||
|
||||
# allow configuring ssl session tickets
|
||||
ssl_session_tickets {{ if $cfg.sslSessionTickets }}on{{ else }}off{{ end }};
|
||||
ssl_session_tickets {{ if $cfg.SSLSessionTickets }}on{{ else }}off{{ end }};
|
||||
|
||||
# slightly reduce the time-to-first-byte
|
||||
ssl_buffer_size {{ $cfg.sslBufferSize }};
|
||||
ssl_buffer_size {{ $cfg.SSLBufferSize }};
|
||||
|
||||
{{ if not (empty $cfg.sslCiphers) }}
|
||||
{{ if not (empty $cfg.SSLCiphers) }}
|
||||
# allow configuring custom ssl ciphers
|
||||
ssl_ciphers '{{ $cfg.sslCiphers }}';
|
||||
ssl_ciphers '{{ $cfg.SSLCiphers }}';
|
||||
ssl_prefer_server_ciphers on;
|
||||
{{ end }}
|
||||
|
||||
{{ if not (empty .sslDHParam) }}
|
||||
{{ if not (empty $cfg.SSLDHParam) }}
|
||||
# allow custom DH file http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_dhparam
|
||||
ssl_dhparam {{ .sslDHParam }};
|
||||
ssl_dhparam {{ $cfg.SSLDHParam }};
|
||||
{{ end }}
|
||||
|
||||
{{ if not $cfg.enableDynamicTlsRecords }}
|
||||
{{ if not $cfg.EnableDynamicTLSRecords }}
|
||||
ssl_dyn_rec_size_lo 0;
|
||||
{{ end }}
|
||||
|
||||
{{ if .customErrors }}
|
||||
{{ if .CustomErrors }}
|
||||
# Custom error pages
|
||||
proxy_intercept_errors on;
|
||||
{{ end }}
|
||||
|
||||
{{ range $errCode := $cfg.customHttpErrors }}
|
||||
{{ range $errCode := $cfg.CustomHTTPErrors }}
|
||||
error_page {{ $errCode }} = @custom_{{ $errCode }};{{ end }}
|
||||
|
||||
# In case of errors try the next upstream server before returning an error
|
||||
proxy_next_upstream error timeout invalid_header http_502 http_503 http_504{{ if $cfg.retryNonIdempotent }} non_idempotent{{ end }};
|
||||
proxy_next_upstream error timeout invalid_header http_502 http_503 http_504{{ if $cfg.RetryNonIdempotent }} non_idempotent{{ end }};
|
||||
|
||||
{{range $name, $upstream := .upstreams}}
|
||||
{{range $name, $upstream := $backends}}
|
||||
upstream {{$upstream.Name}} {
|
||||
{{ if $cfg.enableStickySessions }}
|
||||
{{ if $cfg.EnableStickySessions }}
|
||||
sticky hash=sha1 httponly;
|
||||
{{ else }}
|
||||
least_conn;
|
||||
|
@ -180,26 +180,26 @@ http {
|
|||
|
||||
{{/* build all the required rate limit zones. Each annotation requires a dedicated zone */}}
|
||||
{{/* 1MB -> 16 thousand 64-byte states or about 8 thousand 128-byte states */}}
|
||||
{{ range $zone := (buildRateLimitZones .servers) }}
|
||||
{{ range $zone := (buildRateLimitZones .Servers) }}
|
||||
{{ $zone }}
|
||||
{{ end }}
|
||||
|
||||
{{ range $server := .servers }}
|
||||
{{ range $server := .Servers }}
|
||||
server {
|
||||
server_name {{ $server.Name }};
|
||||
listen 80{{ if $cfg.useProxyProtocol }} proxy_protocol{{ end }};
|
||||
{{ if $server.SSL }}listen 442 {{ if $cfg.useProxyProtocol }}proxy_protocol{{ end }} ssl {{ if $cfg.useHttp2 }}http2{{ end }};
|
||||
server_name {{ $server.Hostname }};
|
||||
listen 80{{ if $cfg.UseProxyProtocol }} proxy_protocol{{ end }};
|
||||
{{ if not (empty $server.SSLCertificate) }}listen 442 {{ if $cfg.UseProxyProtocol }}proxy_protocol{{ end }} ssl {{ if $cfg.UseHttp2 }}http2{{ end }};
|
||||
{{/* comment PEM sha is required to detect changes in the generated configuration and force a reload */}}
|
||||
# PEM sha: {{ $server.SSLPemChecksum }}
|
||||
ssl_certificate {{ $server.SSLCertificate }};
|
||||
ssl_certificate_key {{ $server.SSLCertificate }};
|
||||
{{ end }}
|
||||
|
||||
{{ if (and $server.SSL $cfg.hsts) }}
|
||||
more_set_headers "Strict-Transport-Security: max-age={{ $cfg.hstsMaxAge }}{{ if $cfg.hstsIncludeSubdomains }}; includeSubDomains{{ end }}; preload";
|
||||
{{ if (and (not (empty $server.SSLCertificate)) $cfg.HSTS) }}
|
||||
more_set_headers "Strict-Transport-Security: max-age={{ $cfg.HSTSMaxAge }}{{ if $cfg.HSTSIncludeSubdomains }}; includeSubDomains{{ end }}; preload";
|
||||
{{ end }}
|
||||
|
||||
{{ if $cfg.enableVtsStatus }}vhost_traffic_status_filter_by_set_key $geoip_country_code country::$server_name;{{ end }}
|
||||
{{ if $cfg.EnableVtsStatus }}vhost_traffic_status_filter_by_set_key $geoip_country_code country::$server_name;{{ end }}
|
||||
|
||||
{{ range $location := $server.Locations }}
|
||||
{{ $path := buildLocation $location }}
|
||||
|
@ -240,7 +240,7 @@ http {
|
|||
auth_request {{ $authPath }};
|
||||
{{ end }}
|
||||
|
||||
{{ if (and $server.SSL $location.Redirect.SSLRedirect) }}
|
||||
{{ if (and (not (empty $server.SSLCertificate)) $location.Redirect.SSLRedirect) }}
|
||||
# enforce ssl on server side
|
||||
if ($scheme = http) {
|
||||
return 301 https://$host$request_uri;
|
||||
|
@ -256,7 +256,6 @@ http {
|
|||
auth_basic "{{ $location.BasicDigestAuth.Realm }}";
|
||||
auth_basic_user_file {{ $location.BasicDigestAuth.File }};
|
||||
{{ else }}
|
||||
#TODO: add nginx-http-auth-digest module
|
||||
auth_digest "{{ $location.BasicDigestAuth.Realm }}";
|
||||
auth_digest_user_file {{ $location.BasicDigestAuth.File }};
|
||||
{{ end }}
|
||||
|
@ -300,14 +299,14 @@ http {
|
|||
proxy_set_header Accept-Encoding "";
|
||||
{{ end }}
|
||||
|
||||
set $proxy_upstream_name "{{ $location.Backend.Name }}";
|
||||
{{ buildProxyPass $location }}
|
||||
set $proxy_upstream_name "{{ $location.Backend }}";
|
||||
{{ buildProxyPass $backends $location }}
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{ if eq $server.Name "_" }}
|
||||
{{ if eq $server.Hostname "_" }}
|
||||
# health checks in cloud providers require the use of port 80
|
||||
location {{ $healthzURL }} {
|
||||
location {{ $healthzURI }} {
|
||||
access_log off;
|
||||
return 200;
|
||||
}
|
||||
|
@ -322,6 +321,7 @@ http {
|
|||
stub_status on;
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{ template "CUSTOM_ERRORS" $cfg }}
|
||||
}
|
||||
|
||||
|
@ -332,15 +332,15 @@ http {
|
|||
# Use the port 18080 (random value just to avoid known ports) as default port for nginx.
|
||||
# Changing this value requires a change in:
|
||||
# https://github.com/kubernetes/contrib/blob/master/ingress/controllers/nginx/nginx/command.go#L104
|
||||
listen 18080 default_server reuseport backlog={{ .backlogSize }};
|
||||
listen 18080 default_server reuseport backlog={{ .BacklogSize }};
|
||||
|
||||
location {{ $healthzURL }} {
|
||||
location {{ $healthzURI }} {
|
||||
access_log off;
|
||||
return 200;
|
||||
}
|
||||
|
||||
location /nginx_status {
|
||||
{{ if $cfg.enableVtsStatus }}
|
||||
{{ if $cfg.EnableVtsStatus }}
|
||||
vhost_traffic_status_display;
|
||||
vhost_traffic_status_display_format html;
|
||||
{{ else }}
|
||||
|
@ -362,7 +362,7 @@ http {
|
|||
set $proxy_upstream_name "-";
|
||||
|
||||
location / {
|
||||
{{ if .customErrors }}
|
||||
{{ if .CustomErrors }}
|
||||
content_by_lua_block {
|
||||
openURL(503)
|
||||
}
|
||||
|
@ -376,15 +376,14 @@ http {
|
|||
stream {
|
||||
# map FQDN that requires SSL passthrough
|
||||
map $ssl_preread_server_name $stream_upstream {
|
||||
{{ range $i, $passthrough := .passthroughUpstreams }}
|
||||
{{ $passthrough.Host }} {{ $passthrough.Upstream.Name }}-{{ $i }}-pt;
|
||||
{{ range $i, $passthrough := .PassthrougBackends }}
|
||||
{{ $passthrough.Hostname }} {{ $passthrough.Backend }};
|
||||
{{ end }}
|
||||
# send SSL traffic to this nginx in a different port
|
||||
default nginx-ssl-backend;
|
||||
}
|
||||
|
||||
log_format log_stream '$remote_addr [$time_local] $protocol [$ssl_preread_server_name] [$stream_upstream] '
|
||||
'$status $bytes_sent $bytes_received $session_time';
|
||||
log_format log_stream '$remote_addr [$time_local] $protocol [$ssl_preread_server_name] [$stream_upstream] $status $bytes_sent $bytes_received $session_time';
|
||||
|
||||
access_log /var/log/nginx/access.log log_stream;
|
||||
error_log /var/log/nginx/error.log;
|
||||
|
@ -394,56 +393,20 @@ stream {
|
|||
server 127.0.0.1:442;
|
||||
}
|
||||
|
||||
{{ range $i, $passthrough := .passthroughUpstreams }}
|
||||
upstream {{ $passthrough.Name }}-{{ $i }}-pt {
|
||||
{{ range $server := $passthrough.Endpoints }}server {{ $server.Address }}:{{ $server.Port }};
|
||||
{{ end }}
|
||||
}
|
||||
{{ end }}
|
||||
{{ buildSSPassthroughUpstreams $backends .PassthrougBackends }}
|
||||
|
||||
server {
|
||||
listen 443;
|
||||
|
||||
{{ if $cfg.useProxyProtocol }}proxy_protocol on;{{ end }}
|
||||
|
||||
{{ if $cfg.UseProxyProtocol }}proxy_protocol on;{{ end }}
|
||||
proxy_pass $stream_upstream;
|
||||
ssl_preread on;
|
||||
}
|
||||
|
||||
# TCP services
|
||||
{{ range $i, $tcpServer := .tcpUpstreams }}
|
||||
upstream tcp-{{ $tcpServer.Upstream.Name }} {
|
||||
{{ range $server := $tcpServer.Upstream.Endpoints }}server {{ $server.Address }}:{{ $server.Port }};
|
||||
{{ end }}
|
||||
}
|
||||
|
||||
server {
|
||||
listen {{ $tcpServer.Path }};
|
||||
proxy_connect_timeout {{ $tcpServer.Proxy.ConnectTimeout }}s;
|
||||
proxy_timeout {{ $tcpServer.Proxy.ReadTimeout }}s;
|
||||
proxy_pass tcp-{{ $tcpServer.Upstream.Name }};
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
# UDP services
|
||||
{{ range $i, $udpServer := .udpUpstreams }}
|
||||
upstream udp-{{ $udpServer.Upstream.Name }} {
|
||||
{{ range $server := $udpServer.Upstream.Endpoints }}server {{ $server.Address }}:{{ $server.Port }};
|
||||
{{ end }}
|
||||
}
|
||||
|
||||
server {
|
||||
listen {{ $udpServer.Path }} udp;
|
||||
proxy_timeout 10s;
|
||||
proxy_responses 1;
|
||||
proxy_pass udp-{{ $udpServer.Upstream.Name }};
|
||||
}
|
||||
{{ end }}
|
||||
}
|
||||
|
||||
{{/* definition of templates to avoid repetitions */}}
|
||||
{{ define "CUSTOM_ERRORS" }}
|
||||
{{ range $errCode := .customHttpErrors }}
|
||||
{{ range $errCode := .CustomHTTPErrors }}
|
||||
location @custom_{{ $errCode }} {
|
||||
internal;
|
||||
content_by_lua_block {
|
||||
|
|
57259
controllers/nginx/test/data/config.json
Normal file
57259
controllers/nginx/test/data/config.json
Normal file
File diff suppressed because it is too large
Load diff
|
@ -23,10 +23,10 @@ import (
|
|||
"os"
|
||||
"regexp"
|
||||
|
||||
"k8s.io/ingress/core/pkg/ingress/annotations/parser"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
|
||||
"k8s.io/ingress/core/pkg/ingress/annotations/parser"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -59,10 +59,10 @@ var (
|
|||
|
||||
// BasicDigest returns authentication configuration for an Ingress rule
|
||||
type BasicDigest struct {
|
||||
Type string
|
||||
Realm string
|
||||
File string
|
||||
Secured bool
|
||||
Type string `json:"type"`
|
||||
Realm string `json:"realm"`
|
||||
File string `json:"file"`
|
||||
Secured bool `json:"secured"`
|
||||
}
|
||||
|
||||
// ParseAnnotations parses the annotations contained in the ingress
|
||||
|
|
|
@ -21,8 +21,9 @@ import (
|
|||
"net/url"
|
||||
"strings"
|
||||
|
||||
"k8s.io/ingress/core/pkg/ingress/annotations/parser"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
|
||||
"k8s.io/ingress/core/pkg/ingress/annotations/parser"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -34,9 +35,9 @@ const (
|
|||
|
||||
// External returns external authentication configuration for an Ingress rule
|
||||
type External struct {
|
||||
URL string
|
||||
Method string
|
||||
SendBody bool
|
||||
URL string `json:"url"`
|
||||
Method string `json:"method"`
|
||||
SendBody bool `json:"sendBody"`
|
||||
}
|
||||
|
||||
var (
|
||||
|
|
|
@ -19,10 +19,10 @@ package authtls
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
|
||||
"k8s.io/ingress/core/pkg/ingress/annotations/parser"
|
||||
"k8s.io/ingress/core/pkg/k8s"
|
||||
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -32,11 +32,11 @@ const (
|
|||
|
||||
// SSLCert returns external authentication configuration for an Ingress rule
|
||||
type SSLCert struct {
|
||||
Secret string
|
||||
CertFileName string
|
||||
KeyFileName string
|
||||
CAFileName string
|
||||
PemSHA string
|
||||
Secret string `json:"secret"`
|
||||
CertFileName string `json:"certFilename"`
|
||||
KeyFileName string `json:"keyFilename"`
|
||||
CAFileName string `json:"caFilename"`
|
||||
PemSHA string `json:"pemSha"`
|
||||
}
|
||||
|
||||
// ParseAnnotations parses the annotations contained in the ingress
|
||||
|
|
|
@ -17,9 +17,9 @@ limitations under the License.
|
|||
package cors
|
||||
|
||||
import (
|
||||
"k8s.io/ingress/core/pkg/ingress/annotations/parser"
|
||||
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
|
||||
"k8s.io/ingress/core/pkg/ingress/annotations/parser"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
|
@ -31,8 +31,8 @@ const (
|
|||
// Upstream returns the URL and method to use check the status of
|
||||
// the upstream server/s
|
||||
type Upstream struct {
|
||||
MaxFails int
|
||||
FailTimeout int
|
||||
MaxFails int `json:"maxFails"`
|
||||
FailTimeout int `json:"failTimeout"`
|
||||
}
|
||||
|
||||
// ParseAnnotations parses the annotations contained in the ingress
|
||||
|
|
|
@ -20,11 +20,11 @@ import (
|
|||
"errors"
|
||||
"strings"
|
||||
|
||||
"k8s.io/ingress/core/pkg/ingress/annotations/parser"
|
||||
"k8s.io/ingress/core/pkg/ingress/defaults"
|
||||
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
"k8s.io/kubernetes/pkg/util/net/sets"
|
||||
|
||||
"k8s.io/ingress/core/pkg/ingress/annotations/parser"
|
||||
"k8s.io/ingress/core/pkg/ingress/defaults"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -39,7 +39,7 @@ var (
|
|||
|
||||
// SourceRange returns the CIDR
|
||||
type SourceRange struct {
|
||||
CIDR []string
|
||||
CIDR []string `json:"cidr"`
|
||||
}
|
||||
|
||||
// ParseAnnotations parses the annotations contained in the ingress
|
||||
|
|
|
@ -32,10 +32,10 @@ const (
|
|||
|
||||
// Configuration returns the proxy timeout to use in the upstream server/s
|
||||
type Configuration struct {
|
||||
ConnectTimeout int
|
||||
SendTimeout int
|
||||
ReadTimeout int
|
||||
BufferSize string
|
||||
ConnectTimeout int `json:"conectTimeout"`
|
||||
SendTimeout int `json:"sendTimeout"`
|
||||
ReadTimeout int `json:"readTimeout"`
|
||||
BufferSize string `json:"bufferSize"`
|
||||
}
|
||||
|
||||
// ParseAnnotations parses the annotations contained in the ingress
|
||||
|
|
|
@ -20,9 +20,9 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
|
||||
"k8s.io/ingress/core/pkg/ingress/annotations/parser"
|
||||
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
|
||||
"k8s.io/ingress/core/pkg/ingress/annotations/parser"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -48,19 +48,19 @@ var (
|
|||
// Note: Is possible to specify both limits
|
||||
type RateLimit struct {
|
||||
// Connections indicates a limit with the number of connections per IP address
|
||||
Connections Zone
|
||||
Connections Zone `json:"connections"`
|
||||
// RPS indicates a limit with the number of connections per second
|
||||
RPS Zone
|
||||
RPS Zone `json:"rps"`
|
||||
}
|
||||
|
||||
// Zone returns information about the NGINX rate limit (limit_req_zone)
|
||||
// http://nginx.org/en/docs/http/ngx_http_limit_req_module.html#limit_req_zone
|
||||
type Zone struct {
|
||||
Name string
|
||||
Limit int
|
||||
Burst int
|
||||
Name string `json:"name"`
|
||||
Limit int `json:"limit"`
|
||||
Burst int `json:"burst"`
|
||||
// SharedSize amount of shared memory for the zone
|
||||
SharedSize int
|
||||
SharedSize int `json:"sharedSize"`
|
||||
}
|
||||
|
||||
// ParseAnnotations parses the annotations contained in the ingress
|
||||
|
|
|
@ -19,10 +19,10 @@ package rewrite
|
|||
import (
|
||||
"errors"
|
||||
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
|
||||
"k8s.io/ingress/core/pkg/ingress/annotations/parser"
|
||||
"k8s.io/ingress/core/pkg/ingress/defaults"
|
||||
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -34,12 +34,12 @@ const (
|
|||
// Redirect describes the per location redirect config
|
||||
type Redirect struct {
|
||||
// Target URI where the traffic must be redirected
|
||||
Target string
|
||||
Target string `json:"target"`
|
||||
// AddBaseURL indicates if is required to add a base tag in the head
|
||||
// of the responses from the upstream servers
|
||||
AddBaseURL bool
|
||||
AddBaseURL bool `json:"addBaseUrl"`
|
||||
// SSLRedirect indicates if the location section is accessible SSL only
|
||||
SSLRedirect bool
|
||||
SSLRedirect bool `json:"sslRedirect"`
|
||||
}
|
||||
|
||||
// ParseAnnotations parses the annotations contained in the ingress
|
||||
|
|
|
@ -17,9 +17,9 @@ limitations under the License.
|
|||
package secureupstream
|
||||
|
||||
import (
|
||||
"k8s.io/ingress/core/pkg/ingress/annotations/parser"
|
||||
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
|
||||
"k8s.io/ingress/core/pkg/ingress/annotations/parser"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
|
@ -19,10 +19,10 @@ package sslpassthrough
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
|
||||
"k8s.io/ingress/core/pkg/ingress/annotations/parser"
|
||||
"k8s.io/ingress/core/pkg/ingress/defaults"
|
||||
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
|
@ -21,11 +21,12 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
"k8s.io/kubernetes/pkg/client/cache"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/ingress/core/pkg/ingress"
|
||||
"k8s.io/ingress/core/pkg/ingress/annotations/parser"
|
||||
ssl "k8s.io/ingress/core/pkg/net/ssl"
|
||||
|
|
|
@ -301,7 +301,7 @@ func (ic GenericController) Check(_ *http.Request) error {
|
|||
}
|
||||
|
||||
// Info returns information about the backend
|
||||
func (ic GenericController) Info() string {
|
||||
func (ic GenericController) Info() *ingress.BackendInfo {
|
||||
return ic.cfg.Backend.Info()
|
||||
}
|
||||
|
||||
|
@ -368,31 +368,25 @@ func (ic *GenericController) sync(key interface{}) error {
|
|||
continue
|
||||
}
|
||||
passUpstreams = append(passUpstreams, &ingress.SSLPassthroughBackend{
|
||||
Upstream: loc.Backend,
|
||||
Host: server.Name,
|
||||
Backend: loc.Backend,
|
||||
Hostname: server.Hostname,
|
||||
})
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
data, err := ic.cfg.Backend.OnUpdate(cfg, ingress.Configuration{
|
||||
HealthzURL: ic.cfg.DefaultHealthzURL,
|
||||
Upstreams: upstreams,
|
||||
Backends: upstreams,
|
||||
Servers: servers,
|
||||
TCPEndpoints: ic.getTCPServices(),
|
||||
UPDEndpoints: ic.getUDPServices(),
|
||||
PassthroughUpstreams: passUpstreams,
|
||||
PassthroughBackends: passUpstreams,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !ic.cfg.Backend.IsReloadRequired(data) {
|
||||
return nil
|
||||
}
|
||||
|
||||
glog.Infof("reloading ingress backend...")
|
||||
out, err := ic.cfg.Backend.Restart(data)
|
||||
out, err := ic.cfg.Backend.Reload(data)
|
||||
if err != nil {
|
||||
incReloadErrorCount()
|
||||
glog.Errorf("unexpected failure restarting the backend: \n%v", string(out))
|
||||
|
@ -518,10 +512,7 @@ func (ic *GenericController) getStreamServices(data map[string]string, proto api
|
|||
|
||||
svcs = append(svcs, &ingress.Location{
|
||||
Path: k,
|
||||
Upstream: ingress.Backend{
|
||||
Name: fmt.Sprintf("%v-%v-%v", svcNs, svcName, port),
|
||||
Endpoints: endps,
|
||||
},
|
||||
Backend: fmt.Sprintf("%v-%v-%v", svcNs, svcName, port),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -579,7 +570,7 @@ func (ic *GenericController) getBackendServers() ([]*ingress.Backend, []*ingress
|
|||
upstreams := ic.createUpstreams(ings)
|
||||
servers := ic.createServers(ings, upstreams)
|
||||
|
||||
upsDefaults := ic.cfg.Backend.UpstreamDefaults()
|
||||
upsDefaults := ic.cfg.Backend.BackendDefaults()
|
||||
|
||||
for _, ingIf := range ings {
|
||||
ing := ingIf.(*extensions.Ingress)
|
||||
|
@ -596,11 +587,6 @@ func (ic *GenericController) getBackendServers() ([]*ingress.Backend, []*ingress
|
|||
glog.V(5).Infof("error reading rate limit annotation in Ingress %v/%v: %v", ing.GetNamespace(), ing.GetName(), err)
|
||||
}
|
||||
|
||||
secUpstream, err := secureupstream.ParseAnnotations(ing)
|
||||
if err != nil {
|
||||
glog.V(5).Infof("error reading secure upstream in Ingress %v/%v: %v", ing.GetNamespace(), ing.GetName(), err)
|
||||
}
|
||||
|
||||
locRew, err := rewrite.ParseAnnotations(upsDefaults, ing)
|
||||
if err != nil {
|
||||
glog.V(5).Infof("error parsing rewrite annotations for Ingress rule %v/%v: %v", ing.GetNamespace(), ing.GetName(), err)
|
||||
|
@ -666,7 +652,7 @@ func (ic *GenericController) getBackendServers() ([]*ingress.Backend, []*ingress
|
|||
len(ing.Spec.TLS) == 0 &&
|
||||
host != defServerName {
|
||||
glog.V(3).Infof("ingress rule %v/%v does not contains HTTP or TLS rules. using default backend", ing.Namespace, ing.Name)
|
||||
server.Locations[0].Upstream = *defBackend
|
||||
server.Locations[0].Backend = defBackend.Name
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -690,19 +676,19 @@ func (ic *GenericController) getBackendServers() ([]*ingress.Backend, []*ingress
|
|||
addLoc = false
|
||||
|
||||
if !loc.IsDefBackend {
|
||||
glog.V(3).Infof("avoiding replacement of ingress rule %v/%v location %v upstream %v (%v)", ing.Namespace, ing.Name, loc.Path, ups.Name, loc.Backend.Name)
|
||||
glog.V(3).Infof("avoiding replacement of ingress rule %v/%v location %v upstream %v (%v)", ing.Namespace, ing.Name, loc.Path, ups.Name, loc.Backend)
|
||||
break
|
||||
}
|
||||
|
||||
glog.V(3).Infof("replacing ingress rule %v/%v location %v upstream %v (%v)", ing.Namespace, ing.Name, loc.Path, ups.Name, loc.Backend.Name)
|
||||
loc.Backend = *ups
|
||||
glog.V(3).Infof("replacing ingress rule %v/%v location %v upstream %v (%v)", ing.Namespace, ing.Name, loc.Path, ups.Name, loc.Backend)
|
||||
loc.Backend = ups.Name
|
||||
loc.IsDefBackend = false
|
||||
loc.BasicDigestAuth = *nginxAuth
|
||||
loc.RateLimit = *rl
|
||||
loc.Redirect = *locRew
|
||||
loc.SecureUpstream = secUpstream
|
||||
//loc.SecureUpstream = secUpstream
|
||||
loc.Whitelist = *wl
|
||||
loc.Backend = *ups
|
||||
loc.Backend = ups.Name
|
||||
loc.EnableCORS = eCORS
|
||||
loc.ExternalAuth = ra
|
||||
loc.Proxy = *prx
|
||||
|
@ -712,15 +698,15 @@ func (ic *GenericController) getBackendServers() ([]*ingress.Backend, []*ingress
|
|||
}
|
||||
// is a new location
|
||||
if addLoc {
|
||||
glog.V(3).Infof("adding location %v in ingress rule %v/%v upstream", nginxPath, ing.Namespace, ing.Name, ups.Name)
|
||||
glog.V(3).Infof("adding location %v in ingress rule %v/%v upstream %v", nginxPath, ing.Namespace, ing.Name, ups.Name)
|
||||
server.Locations = append(server.Locations, &ingress.Location{
|
||||
Path: nginxPath,
|
||||
Upstream: *ups,
|
||||
Backend: ups.Name,
|
||||
IsDefBackend: false,
|
||||
BasicDigestAuth: *nginxAuth,
|
||||
RateLimit: *rl,
|
||||
Redirect: *locRew,
|
||||
SecureUpstream: secUpstream,
|
||||
//SecureUpstream: secUpstream,
|
||||
Whitelist: *wl,
|
||||
EnableCORS: eCORS,
|
||||
ExternalAuth: ra,
|
||||
|
@ -776,10 +762,15 @@ func (ic *GenericController) createUpstreams(data []interface{}) map[string]*ing
|
|||
upstreams := make(map[string]*ingress.Backend)
|
||||
upstreams[defUpstreamName] = ic.getDefaultUpstream()
|
||||
|
||||
upsDefaults := ic.cfg.Backend.UpstreamDefaults()
|
||||
upsDefaults := ic.cfg.Backend.BackendDefaults()
|
||||
for _, ingIf := range data {
|
||||
ing := ingIf.(*extensions.Ingress)
|
||||
|
||||
secUpstream, err := secureupstream.ParseAnnotations(ing)
|
||||
if err != nil {
|
||||
glog.V(5).Infof("error reading secure upstream in Ingress %v/%v: %v", ing.GetNamespace(), ing.GetName(), err)
|
||||
}
|
||||
|
||||
hz := healthcheck.ParseAnnotations(upsDefaults, ing)
|
||||
|
||||
var defBackend string
|
||||
|
@ -817,7 +808,9 @@ func (ic *GenericController) createUpstreams(data []interface{}) map[string]*ing
|
|||
|
||||
glog.V(3).Infof("creating upstream %v", name)
|
||||
upstreams[name] = newUpstream(name)
|
||||
|
||||
if !upstreams[name].Secure {
|
||||
upstreams[name].Secure = secUpstream
|
||||
}
|
||||
svcKey := fmt.Sprintf("%v/%v", ing.GetNamespace(), path.Backend.ServiceName)
|
||||
endp, err := ic.serviceEndpoints(svcKey, path.Backend.ServicePort.String(), hz)
|
||||
if err != nil {
|
||||
|
@ -871,18 +864,18 @@ func (ic *GenericController) serviceEndpoints(svcKey, backendPort string,
|
|||
|
||||
func (ic *GenericController) createServers(data []interface{}, upstreams map[string]*ingress.Backend) map[string]*ingress.Server {
|
||||
servers := make(map[string]*ingress.Server)
|
||||
ngxProxy := *proxy.ParseAnnotations(ic.cfg.Backend.UpstreamDefaults(), nil)
|
||||
ngxProxy := *proxy.ParseAnnotations(ic.cfg.Backend.BackendDefaults(), nil)
|
||||
|
||||
upsDefaults := ic.cfg.Backend.UpstreamDefaults()
|
||||
upsDefaults := ic.cfg.Backend.BackendDefaults()
|
||||
|
||||
// default server
|
||||
servers[defServerName] = &ingress.Server{
|
||||
Name: defServerName,
|
||||
Hostname: defServerName,
|
||||
Locations: []*ingress.Location{
|
||||
{
|
||||
Path: rootLocation,
|
||||
IsDefBackend: true,
|
||||
Upstream: *ic.getDefaultUpstream(),
|
||||
Backend: ic.getDefaultUpstream().Name,
|
||||
Proxy: ngxProxy,
|
||||
},
|
||||
}}
|
||||
|
@ -906,12 +899,12 @@ func (ic *GenericController) createServers(data []interface{}, upstreams map[str
|
|||
continue
|
||||
}
|
||||
servers[host] = &ingress.Server{
|
||||
Name: host,
|
||||
Hostname: host,
|
||||
Locations: []*ingress.Location{
|
||||
{
|
||||
Path: rootLocation,
|
||||
IsDefBackend: true,
|
||||
Upstream: *ic.getDefaultUpstream(),
|
||||
Backend: ic.getDefaultUpstream().Name,
|
||||
Proxy: ngxProxy,
|
||||
},
|
||||
}, SSLPassthrough: sslpt}
|
||||
|
@ -929,15 +922,13 @@ func (ic *GenericController) createServers(data []interface{}, upstreams map[str
|
|||
}
|
||||
|
||||
// only add certificate if the server does not have one previously configured
|
||||
if len(ing.Spec.TLS) > 0 && !servers[host].SSL {
|
||||
if len(ing.Spec.TLS) > 0 && servers[host].SSLCertificate != "" {
|
||||
key := fmt.Sprintf("%v/%v", ing.Namespace, ing.Spec.TLS[0].SecretName)
|
||||
bc, exists := ic.sslCertTracker.Get(key)
|
||||
if exists {
|
||||
cert := bc.(*ingress.SSLCert)
|
||||
if isHostValid(host, cert) {
|
||||
servers[host].SSL = true
|
||||
servers[host].SSLCertificate = cert.PemFileName
|
||||
//servers[host].SSLCertificateKey = cert.PemFileName
|
||||
servers[host].SSLPemChecksum = cert.PemSHA
|
||||
}
|
||||
}
|
||||
|
@ -950,7 +941,7 @@ func (ic *GenericController) createServers(data []interface{}, upstreams map[str
|
|||
ic.recorder.Eventf(ing, api.EventTypeWarning, "MAPPING", "error: rules with Spec.Backend are allowed only with hostnames")
|
||||
continue
|
||||
}
|
||||
servers[host].Locations[0].Upstream = *backendUpstream
|
||||
servers[host].Locations[0].Backend = backendUpstream.Name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1050,7 +1041,6 @@ func (ic GenericController) Stop() error {
|
|||
// Start starts the Ingress controller.
|
||||
func (ic GenericController) Start() {
|
||||
glog.Infof("starting Ingress controller")
|
||||
go ic.cfg.Backend.Start()
|
||||
|
||||
go ic.ingController.Run(ic.stopCh)
|
||||
go ic.endpController.Run(ic.stopCh)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package controller
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
@ -12,15 +13,15 @@ import (
|
|||
"github.com/golang/glog"
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"k8s.io/ingress/core/pkg/ingress"
|
||||
"k8s.io/ingress/core/pkg/k8s"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
||||
"k8s.io/kubernetes/pkg/client/restclient"
|
||||
"k8s.io/kubernetes/pkg/healthz"
|
||||
kubectl_util "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
|
||||
"k8s.io/ingress/core/pkg/ingress"
|
||||
"k8s.io/ingress/core/pkg/k8s"
|
||||
)
|
||||
|
||||
// NewIngressController returns a configured Ingress controller
|
||||
|
@ -160,7 +161,8 @@ func registerHandlers(enableProfiling bool, port int, ic *GenericController) {
|
|||
|
||||
mux.HandleFunc("/build", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
fmt.Fprintf(w, ic.Info())
|
||||
b, _ := json.Marshal(ic.Info())
|
||||
w.Write(b)
|
||||
})
|
||||
|
||||
mux.HandleFunc("/stop", func(w http.ResponseWriter, r *http.Request) {
|
||||
|
|
|
@ -24,11 +24,11 @@ import (
|
|||
|
||||
"github.com/golang/glog"
|
||||
|
||||
"k8s.io/ingress/core/pkg/ingress/annotations/service"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
podutil "k8s.io/kubernetes/pkg/api/pod"
|
||||
"k8s.io/kubernetes/pkg/labels"
|
||||
|
||||
"k8s.io/ingress/core/pkg/ingress/annotations/service"
|
||||
)
|
||||
|
||||
// checkSvcForUpdate verifies if one of the running pods for a service contains
|
||||
|
|
|
@ -19,10 +19,10 @@ package controller
|
|||
import (
|
||||
"strings"
|
||||
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
|
||||
"k8s.io/ingress/core/pkg/ingress"
|
||||
"k8s.io/ingress/core/pkg/ingress/annotations/parser"
|
||||
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
)
|
||||
|
||||
// newDefaultServer return an BackendServer to be use as default server that returns 503.
|
||||
|
|
63
core/pkg/ingress/doc.go
Normal file
63
core/pkg/ingress/doc.go
Normal file
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package ingress
|
||||
|
||||
// This package contains the interface is required to implement to build an Ingress controller
|
||||
// A dummy implementation could be
|
||||
//
|
||||
// func main() {
|
||||
// dc := newDummyController()
|
||||
// controller.NewIngressController(dc)
|
||||
// glog.Infof("shutting down Ingress controller...")
|
||||
// }
|
||||
//
|
||||
//where newDummyController returns and implementation of the Controller interface:
|
||||
//
|
||||
// func newDummyController() ingress.Controller {
|
||||
// return &DummyController{}
|
||||
// }
|
||||
//
|
||||
// type DummyController struct {
|
||||
// }
|
||||
//
|
||||
// func (dc DummyController) Reload(data []byte) ([]byte, error) {
|
||||
// err := ioutil.WriteFile("/arbitrary-path", data, 0644)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
//
|
||||
// return exec.Command("some command", "--reload").CombinedOutput()
|
||||
// }
|
||||
//
|
||||
// func (dc DummyController) Test(file string) *exec.Cmd {
|
||||
// return exec.Command("some command", "--config-file", file)
|
||||
// }
|
||||
//
|
||||
// func (dc DummyController) OnUpdate(*api.ConfigMap, Configuration) ([]byte, error) {
|
||||
// return []byte(`<string containing a configuration file>`)
|
||||
// }
|
||||
//
|
||||
// func (dc DummyController) BackendDefaults() defaults.Backend {
|
||||
// return ingress.NewStandardDefaults()
|
||||
// }
|
||||
//
|
||||
// func (dc DummyController) Info() *BackendInfo {
|
||||
// Name: "dummy",
|
||||
// Release: "0.0.0",
|
||||
// Build: "git-00000000",
|
||||
// Repository: "git://foo.bar.com",
|
||||
// }
|
85
core/pkg/ingress/sort_ingress.go
Normal file
85
core/pkg/ingress/sort_ingress.go
Normal file
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package ingress
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
)
|
||||
|
||||
// BackendByNameServers sorts upstreams by name
|
||||
type BackendByNameServers []*Backend
|
||||
|
||||
func (c BackendByNameServers) Len() int { return len(c) }
|
||||
func (c BackendByNameServers) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
|
||||
func (c BackendByNameServers) Less(i, j int) bool {
|
||||
|
||||
return c[i].Name < c[j].Name
|
||||
}
|
||||
|
||||
// EndpointByAddrPort sorts endpoints by address and port
|
||||
type EndpointByAddrPort []Endpoint
|
||||
|
||||
func (c EndpointByAddrPort) Len() int { return len(c) }
|
||||
func (c EndpointByAddrPort) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
|
||||
func (c EndpointByAddrPort) Less(i, j int) bool {
|
||||
iName := c[i].Address
|
||||
jName := c[j].Address
|
||||
if iName != jName {
|
||||
return iName < jName
|
||||
}
|
||||
|
||||
iU := c[i].Port
|
||||
jU := c[j].Port
|
||||
return iU < jU
|
||||
}
|
||||
|
||||
// ServerByName sorts servers by name
|
||||
type ServerByName []*Server
|
||||
|
||||
func (c ServerByName) Len() int { return len(c) }
|
||||
func (c ServerByName) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
|
||||
func (c ServerByName) Less(i, j int) bool {
|
||||
return c[i].Hostname < c[j].Hostname
|
||||
}
|
||||
|
||||
// LocationByPath sorts location by path in descending order
|
||||
// Location / is the last one
|
||||
type LocationByPath []*Location
|
||||
|
||||
func (c LocationByPath) Len() int { return len(c) }
|
||||
func (c LocationByPath) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
|
||||
func (c LocationByPath) Less(i, j int) bool {
|
||||
return c[i].Path > c[j].Path
|
||||
}
|
||||
|
||||
// SSLCert describes a SSL certificate to be used in a server
|
||||
type SSLCert struct {
|
||||
api.ObjectMeta `json:"metadata,omitempty"`
|
||||
// CAFileName contains the path to the file with the root certificate
|
||||
CAFileName string `json:"caFileName"`
|
||||
// PemFileName contains the path to the file with the certificate and key concatenated
|
||||
PemFileName string `json:"pemFileName"`
|
||||
// PemSHA contains the sha1 of the pem file.
|
||||
// This is used to detect changes in the secret that contains the certificates
|
||||
PemSHA string `json:"pemSha"`
|
||||
// CN contains all the common names defined in the SSL certificate
|
||||
CN []string `json:"cn"`
|
||||
}
|
||||
|
||||
// GetObjectKind implements the ObjectKind interface as a noop
|
||||
func (s SSLCert) GetObjectKind() unversioned.ObjectKind { return unversioned.EmptyObjectKind }
|
|
@ -23,17 +23,17 @@ import (
|
|||
|
||||
"github.com/golang/glog"
|
||||
|
||||
cache_store "k8s.io/ingress/core/pkg/cache"
|
||||
"k8s.io/ingress/core/pkg/k8s"
|
||||
strings "k8s.io/ingress/core/pkg/strings"
|
||||
"k8s.io/ingress/core/pkg/task"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
||||
"k8s.io/kubernetes/pkg/client/leaderelection"
|
||||
"k8s.io/kubernetes/pkg/labels"
|
||||
"k8s.io/kubernetes/pkg/util/wait"
|
||||
|
||||
cache_store "k8s.io/ingress/core/pkg/cache"
|
||||
"k8s.io/ingress/core/pkg/k8s"
|
||||
strings "k8s.io/ingress/core/pkg/strings"
|
||||
"k8s.io/ingress/core/pkg/task"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
243
core/pkg/ingress/types.go
Normal file
243
core/pkg/ingress/types.go
Normal file
|
@ -0,0 +1,243 @@
|
|||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package ingress
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
|
||||
"k8s.io/ingress/core/pkg/ingress/annotations/auth"
|
||||
"k8s.io/ingress/core/pkg/ingress/annotations/authreq"
|
||||
"k8s.io/ingress/core/pkg/ingress/annotations/authtls"
|
||||
"k8s.io/ingress/core/pkg/ingress/annotations/ipwhitelist"
|
||||
"k8s.io/ingress/core/pkg/ingress/annotations/proxy"
|
||||
"k8s.io/ingress/core/pkg/ingress/annotations/ratelimit"
|
||||
"k8s.io/ingress/core/pkg/ingress/annotations/rewrite"
|
||||
"k8s.io/ingress/core/pkg/ingress/defaults"
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultSSLDirectory defines the location where the SSL certificates will be generated
|
||||
// This directory contains all the SSL certificates that are specified in Ingress rules.
|
||||
// The name of each file is <namespace>-<secret name>.pem. The content is the concatenated
|
||||
// certificate and key.
|
||||
DefaultSSLDirectory = "/ingress-controller/ssl"
|
||||
)
|
||||
|
||||
// Controller holds the methods to handle an Ingress backend
|
||||
// TODO (#18): Make sure this is sufficiently supportive of other backends.
|
||||
type Controller interface {
|
||||
// Reload 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, there should be not change in the running configuration or
|
||||
// the given byte array.
|
||||
Reload(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
|
||||
// OnUpdate callback invoked from the sync queue https://k8s.io/ingress/core/blob/master/pkg/ingress/controller/controller.go#L387
|
||||
// when an update occurs. This is executed frequently because Ingress
|
||||
// controllers watches changes in:
|
||||
// - Ingresses: main work
|
||||
// - Secrets: referenced from Ingress rules with TLS configured
|
||||
// - ConfigMaps: where the controller reads custom configuration
|
||||
// - Services: referenced from Ingress rules and required to obtain
|
||||
// information about ports and annotations
|
||||
// - Endpoints: referenced from Services and what the backend uses
|
||||
// to route traffic
|
||||
// Any update to services, endpoints, secrets (only those referenced from Ingress)
|
||||
// and ingress trigger the execution.
|
||||
// Notifications of type Add, Update and Delete:
|
||||
// https://github.com/kubernetes/kubernetes/blob/master/pkg/client/cache/controller.go#L164
|
||||
//
|
||||
// ConfigMap content of --configmap
|
||||
// Configuration returns the translation from Ingress rules containing
|
||||
// information about all the upstreams (service endpoints ) "virtual"
|
||||
// servers (FQDN) and all the locations inside each server. Each
|
||||
// location contains information about all the annotations were configured
|
||||
// https://k8s.io/ingress/core/blob/master/pkg/ingress/types.go#L83
|
||||
// The backend returns the contents of the configuration file or an error
|
||||
// with the reason why was not possible to generate the file.
|
||||
//
|
||||
// The returned configuration is then passed to test, and then to reload
|
||||
// if there is no errors.
|
||||
OnUpdate(*api.ConfigMap, Configuration) ([]byte, error)
|
||||
// BackendDefaults returns the minimum settings required to configure the
|
||||
// communication to endpoints
|
||||
BackendDefaults() defaults.Backend
|
||||
// Info returns information about the ingress controller
|
||||
Info() *BackendInfo
|
||||
}
|
||||
|
||||
// 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
|
||||
type BackendInfo struct {
|
||||
// Name returns the name of the backend implementation
|
||||
Name string `json:"name"`
|
||||
// Release returns the running version (semver)
|
||||
Release string `json:"release"`
|
||||
// Build returns information about the git commit
|
||||
Build string `json:"build"`
|
||||
// Repository return information about the git repository
|
||||
Repository string `json:"repository"`
|
||||
}
|
||||
|
||||
// Configuration holds the definition of all the parts required to describe all
|
||||
// ingresses reachable by the ingress controller (using a filter by namespace)
|
||||
type Configuration struct {
|
||||
// Backends are a list of backends used by all the Ingress rules in the
|
||||
// ingress controller. This list includes the default backend
|
||||
Backends []*Backend `json:"namespace"`
|
||||
// Servers
|
||||
Servers []*Server `json:"servers"`
|
||||
// TCPEndpoints contain endpoints for tcp streams handled by this backend
|
||||
// +optional
|
||||
TCPEndpoints []*Location `json:"tcpEndpoints,omitempty"`
|
||||
// UPDEndpoints contain endpoints for udp streams handled by this backend
|
||||
// +optional
|
||||
UPDEndpoints []*Location `json:"udpEndpoints,omitempty"`
|
||||
// PassthroughBackend contains the backends used for SSL passthrough.
|
||||
// It contains information about the associated Server Name Indication (SNI).
|
||||
// +optional
|
||||
PassthroughBackends []*SSLPassthroughBackend `json:"passthroughBackends,omitempty"`
|
||||
}
|
||||
|
||||
// Backend describes one or more remote server/s (endpoints) associated with a service
|
||||
type Backend struct {
|
||||
// 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
|
||||
// The endpoint/s must provide a TLS connection.
|
||||
// The certificate used in the endpoint cannot be a self signed certificate
|
||||
// TODO: add annotation to allow the load of ca certificate
|
||||
Secure bool `json:"secure"`
|
||||
// Endpoints contains the list of endpoints currently running
|
||||
Endpoints []Endpoint `json:"endpoints"`
|
||||
}
|
||||
|
||||
// Endpoint describes a kubernetes endpoint in an backend
|
||||
type Endpoint struct {
|
||||
// Address IP address of the endpoint
|
||||
Address string `json:"address"`
|
||||
// Port number of the TCP port
|
||||
Port string `json:"port"`
|
||||
// MaxFails returns the number of unsuccessful attempts to communicate
|
||||
// allowed before this should be considered dow.
|
||||
// Setting 0 indicates that the check is performed by a Kubernetes probe
|
||||
MaxFails int `json:"maxFails"`
|
||||
// FailTimeout returns the time in seconds during which the specified number
|
||||
// of unsuccessful attempts to communicate with the server should happen
|
||||
// to consider the endpoint unavailable
|
||||
FailTimeout int `json:"failTimeout"`
|
||||
}
|
||||
|
||||
// Server describes a website
|
||||
type Server struct {
|
||||
// Hostname returns the FQDN of the server
|
||||
Hostname string `json:"hostname"`
|
||||
// SSLPassthrough indicates if the TLS termination is realized in
|
||||
// the server or in the remote endpoint
|
||||
SSLPassthrough bool `json:"sslPassthrough"`
|
||||
// SSLCertificate path to the SSL certificate on disk
|
||||
SSLCertificate string `json:"sslCertificate"`
|
||||
// SSLPemChecksum returns the checksum of the certificate file on disk.
|
||||
// There is no restriction in the hash generator. This checksim can be
|
||||
// used to determine if the secret changed without the use of file
|
||||
// system notifications
|
||||
SSLPemChecksum string `json:"sslPemChecksum"`
|
||||
// Locations list of URIs configured in the server.
|
||||
Locations []*Location `json:"locations,omitempty"`
|
||||
}
|
||||
|
||||
// Location describes an URI inside a server.
|
||||
// Also contains additional information about annotations in the Ingress.
|
||||
//
|
||||
// Important:
|
||||
// The implementation of annotations is optional
|
||||
//
|
||||
// In some cases when more than one annotations is defined a particular order in the execution
|
||||
// is required.
|
||||
// The chain in the execution order of annotations should be:
|
||||
// - CertificateAuth
|
||||
// - Whitelist
|
||||
// - RateLimit
|
||||
// - BasicDigestAuth
|
||||
// - ExternalAuth
|
||||
// - Redirect
|
||||
type Location struct {
|
||||
// Path is an extended POSIX regex as defined by IEEE Std 1003.1,
|
||||
// (i.e this follows the egrep/unix syntax, not the perl syntax)
|
||||
// matched against the path of an incoming request. Currently it can
|
||||
// contain characters disallowed from the conventional "path"
|
||||
// part of a URL as defined by RFC 3986. Paths must begin with
|
||||
// a '/'. If unspecified, the path defaults to a catch all sending
|
||||
// traffic to the backend.
|
||||
Path string `json:"path"`
|
||||
// IsDefBackend indicates if service specified in the Ingress
|
||||
// contains active endpoints or not. Returning true means the location
|
||||
// uses the default backend.
|
||||
IsDefBackend bool `json:"isDefBackend"`
|
||||
// Backend describes the name of the backend to use.
|
||||
Backend string `json:"backend"`
|
||||
// BasicDigestAuth returns authentication configuration for
|
||||
// an Ingress rule.
|
||||
// +optional
|
||||
BasicDigestAuth auth.BasicDigest `json:"basicDigestAuth,omitempty"`
|
||||
// EnableCORS indicates if path must support CORS
|
||||
// +optional
|
||||
EnableCORS bool `json:"enableCors,omitempty"`
|
||||
// ExternalAuth indicates the access to this location requires
|
||||
// authentication using an external provider
|
||||
// +optional
|
||||
ExternalAuth authreq.External `json:"externalAuth,omitempty"`
|
||||
// RateLimit describes a limit in the number of connections per IP
|
||||
// address or connections per second.
|
||||
// The Redirect annotation precedes RateLimit
|
||||
// +optional
|
||||
RateLimit ratelimit.RateLimit `json:"rateLimit,omitempty"`
|
||||
// Redirect describes the redirection this location.
|
||||
// +optional
|
||||
Redirect rewrite.Redirect `json:"redirect,omitempty"`
|
||||
// Whitelist indicates only connections from certain client
|
||||
// addresses or networks are allowed.
|
||||
// +optional
|
||||
Whitelist ipwhitelist.SourceRange `json:"whitelist,omitempty"`
|
||||
// Proxy contains information about timeouts and buffer sizes
|
||||
// to be used in connections against endpoints
|
||||
// +optional
|
||||
Proxy proxy.Configuration `json:"proxy,omitempty"`
|
||||
// CertificateAuth indicates the access to this location requires
|
||||
// external authentication
|
||||
// +optional
|
||||
CertificateAuth authtls.SSLCert `json:"certificateAuth,omitempty"`
|
||||
}
|
||||
|
||||
// SSLPassthroughBackend describes a SSL upstream server configured
|
||||
// as passthrough (no TLS termination in the ingress controller)
|
||||
// The endpoints must provide the TLS termination exposing the required SSL certificate.
|
||||
// The ingress controller only pipes the underlying TCP connection
|
||||
type SSLPassthroughBackend struct {
|
||||
// Backend describes the endpoints to use.
|
||||
Backend string `json:"namespace,omitempty"`
|
||||
// Hostname returns the FQDN of the server
|
||||
Hostname string `json:"hostname"`
|
||||
}
|
|
@ -21,6 +21,7 @@ import (
|
|||
"crypto/x509"
|
||||
"encoding/hex"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
@ -92,7 +93,8 @@ func AddOrUpdateCertAndKey(name string, cert, key, ca []byte) (*ingress.SSLCert,
|
|||
|
||||
_, err := pemCert.Verify(opts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to verify certificate chain: \n\t%s\n", err)
|
||||
oe := fmt.Sprintf("failed to verify certificate chain: \n\t%s\n", err)
|
||||
return nil, errors.New(oe)
|
||||
}
|
||||
|
||||
caName := fmt.Sprintf("ca-%v.pem", name)
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
[[ $DEBUG ]] && set -x
|
||||
|
||||
set -eof pipefail
|
||||
|
||||
# include env
|
||||
. hack/e2e-internal/e2e-env.sh
|
||||
|
||||
echo "Destroying running docker containers..."
|
||||
# do not failt if the container is not running
|
||||
docker rm -f kubelet || true
|
||||
docker rm -f apiserver || true
|
||||
docker rm -f etcd || true
|
|
@ -1,21 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
[[ $DEBUG ]] && set -x
|
||||
|
||||
export ETCD_VERSION=3.0.14
|
||||
export K8S_VERSION=1.4.5
|
||||
|
||||
export PWD=`pwd`
|
||||
export BASEDIR="$(dirname ${BASH_SOURCE})"
|
||||
export KUBECTL="${BASEDIR}/kubectl"
|
||||
export GOOS="${GOOS:-linux}"
|
||||
|
||||
if [ ! -e ${KUBECTL} ]; then
|
||||
echo "kubectl binary is missing. downloading..."
|
||||
curl -sSL http://storage.googleapis.com/kubernetes-release/release/v${K8S_VERSION}/bin/${GOOS}/amd64/kubectl -o ${KUBECTL}
|
||||
chmod u+x ${KUBECTL}
|
||||
fi
|
||||
|
||||
${KUBECTL} config set-cluster travis --server=http://0.0.0.0:8080
|
||||
${KUBECTL} config set-context travis --cluster=travis
|
||||
${KUBECTL} config use-context travis
|
|
@ -1,11 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
[[ $DEBUG ]] && set -x
|
||||
|
||||
set -eof pipefail
|
||||
|
||||
# include env
|
||||
. hack/e2e-internal/e2e-env.sh
|
||||
|
||||
echo "Kubernetes information:"
|
||||
${KUBECTL} version
|
|
@ -1,55 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
[[ $DEBUG ]] && set -x
|
||||
|
||||
set -eof pipefail
|
||||
|
||||
# include env
|
||||
. hack/e2e-internal/e2e-env.sh
|
||||
|
||||
echo "Starting etcd..."
|
||||
docker run -d \
|
||||
--net=host \
|
||||
--name=etcd \
|
||||
quay.io/coreos/etcd:v$ETCD_VERSION
|
||||
|
||||
echo "Starting kubernetes..."
|
||||
|
||||
docker run -d --name=apiserver \
|
||||
--net=host \
|
||||
--pid=host \
|
||||
--privileged=true \
|
||||
gcr.io/google_containers/hyperkube:v${K8S_VERSION} \
|
||||
/hyperkube apiserver \
|
||||
--insecure-bind-address=0.0.0.0 \
|
||||
--service-cluster-ip-range=10.0.0.1/24 \
|
||||
--etcd_servers=http://127.0.0.1:4001 \
|
||||
--v=2
|
||||
|
||||
docker run -d --name=kubelet \
|
||||
--volume=/:/rootfs:ro \
|
||||
--volume=/sys:/sys:ro \
|
||||
--volume=/dev:/dev \
|
||||
--volume=/var/lib/docker/:/var/lib/docker:rw \
|
||||
--volume=/var/lib/kubelet/:/var/lib/kubelet:rw \
|
||||
--volume=/var/run:/var/run:rw \
|
||||
--net=host \
|
||||
--pid=host \
|
||||
--privileged=true \
|
||||
gcr.io/google_containers/hyperkube:v${K8S_VERSION} \
|
||||
/hyperkube kubelet \
|
||||
--containerized \
|
||||
--hostname-override="0.0.0.0" \
|
||||
--address="0.0.0.0" \
|
||||
--cluster_dns=10.0.0.10 --cluster_domain=cluster.local \
|
||||
--api-servers=http://localhost:8080 \
|
||||
--config=/etc/kubernetes/manifests-multi
|
||||
|
||||
echo "waiting until api server is available..."
|
||||
until curl -o /dev/null -sIf http://0.0.0.0:8080; do \
|
||||
sleep 10;
|
||||
done;
|
||||
|
||||
echo "Kubernetes started"
|
||||
echo "Kubernetes information:"
|
||||
${KUBECTL} version
|
|
@ -1,3 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
echo "running ginkgo"
|
285
hack/e2e.go
285
hack/e2e.go
|
@ -1,285 +0,0 @@
|
|||
/*
|
||||
Copyright 2014 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// e2e.go runs the e2e test suite. No non-standard package dependencies; call with "go run".
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
build = flag.Bool("build", true, "Build the backends images indicated by the env var BACKENDS required to run e2e tests.")
|
||||
up = flag.Bool("up", true, "Creates a kubernetes cluster using hyperkube (containerized kubelet).")
|
||||
down = flag.Bool("down", true, "destroys the created cluster.")
|
||||
test = flag.Bool("test", true, "Run Ginkgo tests.")
|
||||
dump = flag.String("dump", "", "If set, dump cluster logs to this location on test or cluster-up failure")
|
||||
testArgs = flag.String("test-args", "", "Space-separated list of arguments to pass to Ginkgo test runner.")
|
||||
deployment = flag.String("deployment", "bash", "up/down mechanism")
|
||||
verbose = flag.Bool("v", false, "If true, print all command output.")
|
||||
)
|
||||
|
||||
func appendError(errs []error, err error) []error {
|
||||
if err != nil {
|
||||
return append(errs, err)
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
func validWorkingDirectory() error {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not get pwd: %v", err)
|
||||
}
|
||||
acwd, err := filepath.Abs(cwd)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to convert %s to an absolute path: %v", cwd, err)
|
||||
}
|
||||
if !strings.Contains(filepath.Base(acwd), "ingress-controller") {
|
||||
return fmt.Errorf("must run from git root directory: %v", acwd)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type TestCase struct {
|
||||
XMLName xml.Name `xml:"testcase"`
|
||||
ClassName string `xml:"classname,attr"`
|
||||
Name string `xml:"name,attr"`
|
||||
Time float64 `xml:"time,attr"`
|
||||
Failure string `xml:"failure,omitempty"`
|
||||
}
|
||||
|
||||
type TestSuite struct {
|
||||
XMLName xml.Name `xml:"testsuite"`
|
||||
Failures int `xml:"failures,attr"`
|
||||
Tests int `xml:"tests,attr"`
|
||||
Time float64 `xml:"time,attr"`
|
||||
Cases []TestCase
|
||||
}
|
||||
|
||||
var suite TestSuite
|
||||
|
||||
func xmlWrap(name string, f func() error) error {
|
||||
start := time.Now()
|
||||
err := f()
|
||||
duration := time.Since(start)
|
||||
c := TestCase{
|
||||
Name: name,
|
||||
ClassName: "e2e.go",
|
||||
Time: duration.Seconds(),
|
||||
}
|
||||
if err != nil {
|
||||
c.Failure = err.Error()
|
||||
suite.Failures++
|
||||
}
|
||||
suite.Cases = append(suite.Cases, c)
|
||||
suite.Tests++
|
||||
return err
|
||||
}
|
||||
|
||||
func writeXML(start time.Time) {
|
||||
suite.Time = time.Since(start).Seconds()
|
||||
out, err := xml.MarshalIndent(&suite, "", " ")
|
||||
if err != nil {
|
||||
log.Fatalf("Could not marshal XML: %s", err)
|
||||
}
|
||||
path := filepath.Join(*dump, "junit_runner.xml")
|
||||
f, err := os.Create(path)
|
||||
if err != nil {
|
||||
log.Fatalf("Could not create file: %s", err)
|
||||
}
|
||||
defer f.Close()
|
||||
if _, err := f.WriteString(xml.Header); err != nil {
|
||||
log.Fatalf("Error writing XML header: %s", err)
|
||||
}
|
||||
if _, err := f.Write(out); err != nil {
|
||||
log.Fatalf("Error writing XML data: %s", err)
|
||||
}
|
||||
log.Printf("Saved XML output to %s.", path)
|
||||
}
|
||||
|
||||
func main() {
|
||||
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
||||
flag.Parse()
|
||||
|
||||
if err := validWorkingDirectory(); err != nil {
|
||||
log.Fatalf("Called from invalid working directory: %v", err)
|
||||
}
|
||||
|
||||
deploy, err := getDeployer()
|
||||
if err != nil {
|
||||
log.Fatalf("Error creating deployer: %v", err)
|
||||
}
|
||||
|
||||
if err := run(deploy); err != nil {
|
||||
log.Fatalf("Something went wrong: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func run(deploy deployer) error {
|
||||
if *dump != "" {
|
||||
defer writeXML(time.Now())
|
||||
}
|
||||
|
||||
if *build {
|
||||
if err := xmlWrap("Build", Build); err != nil {
|
||||
return fmt.Errorf("error building: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
if *up {
|
||||
if err := xmlWrap("TearDown", deploy.Down); err != nil {
|
||||
return fmt.Errorf("error tearing down previous cluster: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
var errs []error
|
||||
|
||||
if *up {
|
||||
// If we tried to bring the cluster up, make a courtesy
|
||||
// attempt to bring it down so we're not leaving resources around.
|
||||
//
|
||||
// TODO: We should try calling deploy.Down exactly once. Though to
|
||||
// stop the leaking resources for now, we want to be on the safe side
|
||||
// and call it explictly in defer if the other one is not called.
|
||||
if *down {
|
||||
defer xmlWrap("Deferred TearDown", deploy.Down)
|
||||
}
|
||||
// Start the cluster using this version.
|
||||
if err := xmlWrap("Up", deploy.Up); err != nil {
|
||||
return fmt.Errorf("starting e2e cluster: %s", err)
|
||||
}
|
||||
if *dump != "" {
|
||||
cmd := exec.Command("./cluster/kubectl.sh", "--match-server-version=false", "get", "nodes", "-oyaml")
|
||||
b, err := cmd.CombinedOutput()
|
||||
if *verbose {
|
||||
log.Printf("kubectl get nodes:\n%s", string(b))
|
||||
}
|
||||
if err == nil {
|
||||
if err := ioutil.WriteFile(filepath.Join(*dump, "nodes.yaml"), b, 0644); err != nil {
|
||||
errs = appendError(errs, fmt.Errorf("error writing nodes.yaml: %v", err))
|
||||
}
|
||||
} else {
|
||||
errs = appendError(errs, fmt.Errorf("error running get nodes: %v", err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if *test {
|
||||
if err := xmlWrap("IsUp", deploy.IsUp); err != nil {
|
||||
errs = appendError(errs, err)
|
||||
} else {
|
||||
errs = appendError(errs, Test())
|
||||
}
|
||||
}
|
||||
|
||||
if len(errs) > 0 && *dump != "" {
|
||||
errs = appendError(errs, xmlWrap("DumpClusterLogs", func() error {
|
||||
return DumpClusterLogs(*dump)
|
||||
}))
|
||||
}
|
||||
|
||||
if *down {
|
||||
errs = appendError(errs, xmlWrap("TearDown", deploy.Down))
|
||||
}
|
||||
|
||||
if len(errs) != 0 {
|
||||
return fmt.Errorf("encountered %d errors: %v", len(errs), errs)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func Build() error {
|
||||
// The build-release script needs stdin to ask the user whether
|
||||
// it's OK to download the docker image.
|
||||
cmd := exec.Command("make", "backends", "backends-images", "backends-push")
|
||||
cmd.Stdin = os.Stdin
|
||||
if err := finishRunning("build-release", cmd); err != nil {
|
||||
return fmt.Errorf("error building: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type deployer interface {
|
||||
Up() error
|
||||
IsUp() error
|
||||
SetupKubecfg() error
|
||||
Down() error
|
||||
}
|
||||
|
||||
func getDeployer() (deployer, error) {
|
||||
switch *deployment {
|
||||
case "bash":
|
||||
return bash{}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("Unknown deployment strategy %q", *deployment)
|
||||
}
|
||||
}
|
||||
|
||||
type bash struct{}
|
||||
|
||||
func (b bash) Up() error {
|
||||
return finishRunning("up", exec.Command("./hack/e2e-internal/e2e-up.sh"))
|
||||
}
|
||||
|
||||
func (b bash) IsUp() error {
|
||||
return finishRunning("get status", exec.Command("./hack/e2e-internal/e2e-status.sh"))
|
||||
}
|
||||
|
||||
func (b bash) SetupKubecfg() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b bash) Down() error {
|
||||
return finishRunning("teardown", exec.Command("./hack/e2e-internal/e2e-down.sh"))
|
||||
}
|
||||
|
||||
func DumpClusterLogs(location string) error {
|
||||
log.Printf("Dumping cluster logs to: %v", location)
|
||||
return finishRunning("dump cluster logs", exec.Command("./hack/e2e-internal/log-dump.sh", location))
|
||||
}
|
||||
|
||||
func Test() error {
|
||||
if *testArgs == "" {
|
||||
*testArgs = "--ginkgo.focus=\\[Feature:Ingress\\]"
|
||||
}
|
||||
return finishRunning("Ginkgo tests", exec.Command("./hack/e2e-internal/ginkgo-e2e.sh", strings.Fields(*testArgs)...))
|
||||
}
|
||||
|
||||
func finishRunning(stepName string, cmd *exec.Cmd) error {
|
||||
if *verbose {
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
}
|
||||
log.Printf("Running: %v", stepName)
|
||||
defer func(start time.Time) {
|
||||
log.Printf("Step '%s' finished in %s", stepName, time.Since(start))
|
||||
}(time.Now())
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
return fmt.Errorf("error running %v: %v", stepName, err)
|
||||
}
|
||||
return nil
|
||||
}
|
Loading…
Reference in a new issue