From 9693d1b988ec5f84a30fc854cbf6fa023bf2c6b8 Mon Sep 17 00:00:00 2001 From: Fernandez Ludovic Date: Wed, 21 Mar 2018 21:04:25 +0100 Subject: [PATCH] refactor: review. --- Gopkg.lock | 108 +++++-- servicefabric.go | 2 +- servicefabric_config.go | 223 +++++++------ servicefabric_labelfuncs.go | 18 -- servicefabric_test.go | 628 +++++++++++++----------------------- servicefabric_tmpl.go | 320 +++++++++--------- 6 files changed, 586 insertions(+), 713 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index 5f69864..c7935b4 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -2,10 +2,10 @@ [[projects]] + branch = "master" name = "github.com/BurntSushi/toml" packages = ["."] - revision = "b26d9c308763d68093482582cea63d69be07a0f0" - version = "v0.3.0" + revision = "a368813c5e648fee92e5f6c30e3944ff9d5e8895" [[projects]] name = "github.com/Masterminds/semver" @@ -20,9 +20,10 @@ version = "v2.14.1" [[projects]] - name = "github.com/Sirupsen/logrus" - packages = ["."] - revision = "10f801ebc38b33738c9d17d50860f484a0988ff5" + branch = "master" + name = "github.com/abronan/valkeyrie" + packages = ["store"] + revision = "063d875e3c5fd734fa2aa12fac83829f62acfc70" [[projects]] name = "github.com/aokoli/goutils" @@ -38,33 +39,58 @@ [[projects]] name = "github.com/containous/flaeg" + packages = [ + ".", + "parse" + ] + revision = "b4c2f060875361c070ed2bc300c5929b82f5fa2e" + version = "v1.1.2" + +[[projects]] + branch = "master" + name = "github.com/containous/mux" packages = ["."] - revision = "4b6f66624dce34a226f080b414c4705ea8731bc5" - version = "v1.0.0" + revision = "06ccd3e75091eb659b1d720cda0e16bc7057954c" [[projects]] branch = "master" name = "github.com/containous/traefik" - packages = ["autogen/gentemplates","job","log","provider","provider/label","safe","tls","tls/generate","types"] - revision = "06d528a2bdc5398a4552f78d2304d9c390b7ccf6" + packages = [ + "autogen/gentemplates", + "job", + "log", + "provider", + "provider/label", + "safe", + "tls", + "tls/generate", + "types" + ] + revision = "329c576f445aa5c7b310f4bc465e704b67c7c2b1" + +[[projects]] + name = "github.com/davecgh/go-spew" + packages = ["spew"] + revision = "346938d642f2ec3594ed81d874461961cd0faa76" + version = "v1.1.0" [[projects]] - name = "github.com/docker/libkv" - packages = ["store"] - revision = "aabc039ad04deb721e234f99cd1b4aa28ac71a40" - version = "v0.2.1" + name = "github.com/gorilla/context" + packages = ["."] + revision = "1ea25387ff6f684839d82767c1733ff4d4d15d0a" + version = "v1.1" [[projects]] - branch = "master" name = "github.com/huandu/xstrings" packages = ["."] - revision = "37469d0c81a7910b49d64a0d308ded4823e90937" + revision = "2bf18b218c51864a87384c06996e40ff9dcff8e1" + version = "v1.0.0" [[projects]] name = "github.com/imdario/mergo" packages = ["."] - revision = "7fe0c75c13abdee74b09fcacef5ea1c6bba6a874" - version = "0.2.4" + revision = "163f41321a19dd09362d4c63cc2489db2015f1f4" + version = "0.3.2" [[projects]] name = "github.com/jjcollinge/servicefabric" @@ -78,38 +104,66 @@ revision = "45c278ab3607870051a2ea9040bb85fcb8557481" [[projects]] + name = "github.com/pmezard/go-difflib" + packages = ["difflib"] + revision = "792786c7400a136282c1664665ae0a8db921c6c2" + version = "v1.0.0" + +[[projects]] + branch = "master" name = "github.com/ryanuber/go-glob" packages = ["."] - revision = "572520ed46dbddaed19ea3d9541bdd0494163693" - version = "v0.1" + revision = "256dc444b735e061061cf46c809487313d5b0065" [[projects]] name = "github.com/satori/go.uuid" packages = ["."] - revision = "879c5887cd475cd7864858769793b2ceb0d44feb" - version = "v1.1.0" + revision = "f58768cc1a7a7e77a3bd49e98cdd21419399b6a3" + version = "v1.2.0" + +[[projects]] + name = "github.com/sirupsen/logrus" + packages = ["."] + revision = "c155da19408a8799da419ed3eeb0cb5db0ad5dbc" + version = "v1.0.5" + +[[projects]] + name = "github.com/stretchr/testify" + packages = [ + "assert", + "require" + ] + revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71" + version = "v1.2.1" [[projects]] branch = "master" name = "golang.org/x/crypto" - packages = ["pbkdf2","scrypt"] - revision = "0fcca4842a8d74bfddc2c96a073bd2a4d2a7a2e8" + packages = [ + "pbkdf2", + "scrypt", + "ssh/terminal" + ] + revision = "80db560fac1fb3e6ac81dbc7f8ae4c061f5257bd" [[projects]] branch = "master" name = "golang.org/x/net" packages = ["context"] - revision = "d866cfc389cec985d6fda2859936a575a55a3ab6" + revision = "6078986fec03a1dcc236c34816c71b0e05018fda" [[projects]] branch = "master" name = "golang.org/x/sys" - packages = ["unix"] - revision = "28a7276518d399b9634904daad79e18b44d481bc" + packages = [ + "unix", + "windows" + ] + revision = "641605214e7dab930817f68e2fef560efbb033e5" [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "80f3928471a7e1bc924cb3ed1dada3b44ce2c5a29f4e024aeb6ef336f8d67b79" + inputs-digest = "439e87370bde2964292e65861a25a490fecd3b52b366357861b4d9e65dd34098" solver-name = "gps-cdcl" solver-version = 1 diff --git a/servicefabric.go b/servicefabric.go index 0e55c21..b86aa32 100644 --- a/servicefabric.go +++ b/servicefabric.go @@ -190,7 +190,7 @@ func hasHTTPEndpoint(instanceData *sf.ReplicaItemBase) bool { func getLabels(sfClient sfClient, service *sf.ServiceItem, app *sf.ApplicationItem) (map[string]string, error) { labels, err := sfClient.GetServiceExtensionMap(service, app, traefikServiceFabricExtensionKey) if err != nil { - log.Errorf("Error retreiving serviceExtensionMap: %v", err) + log.Errorf("Error retrieving serviceExtensionMap: %v", err) return nil, err } diff --git a/servicefabric_config.go b/servicefabric_config.go index d4f4e03..e05ec4e 100644 --- a/servicefabric_config.go +++ b/servicefabric_config.go @@ -34,80 +34,24 @@ func (p *Provider) buildConfiguration(sfClient sfClient) (*types.Configuration, "filterServicesByLabelValue": filterServicesByLabelValue, // FIXME unused // Backend functions - "getWeight": getFuncServiceStringLabel(label.TraefikWeight, label.DefaultWeight), - "getProtocol": getFuncServiceStringLabel(label.TraefikProtocol, label.DefaultProtocol), - "hasHealthCheckLabels": hasFuncService(label.TraefikBackendHealthCheckPath), - "getHealthCheckPath": getFuncServiceStringLabel(label.TraefikBackendHealthCheckPath, ""), - "getHealthCheckPort": getFuncServiceStringLabel(label.TraefikBackendHealthCheckPort, "0"), - "getHealthCheckInterval": getFuncServiceStringLabel(label.TraefikBackendHealthCheckInterval, ""), - "hasCircuitBreakerLabel": hasFuncService(label.TraefikBackendCircuitBreakerExpression), - "getCircuitBreakerExpression": getFuncServiceStringLabel(label.TraefikBackendCircuitBreakerExpression, label.DefaultCircuitBreakerExpression), - "hasLoadBalancerLabel": hasLoadBalancerLabel, - "getLoadBalancerMethod": getFuncServiceStringLabel(label.TraefikBackendLoadBalancerMethod, label.DefaultBackendLoadBalancerMethod), - "hasMaxConnLabels": hasMaxConnLabels, - "getMaxConnAmount": getFuncServiceStringLabel(label.TraefikBackendMaxConnAmount, string(math.MaxInt64)), - "getMaxConnExtractorFunc": getFuncServiceStringLabel(label.TraefikBackendMaxConnExtractorFunc, label.DefaultBackendMaxconnExtractorFunc), - "getSticky": getFuncBoolLabel(label.TraefikBackendLoadBalancerSticky, false), - "hasStickinessLabel": hasFuncService(label.TraefikBackendLoadBalancerStickiness), - "getStickinessCookieName": getFuncServiceStringLabel(label.TraefikBackendLoadBalancerStickinessCookieName, label.DefaultBackendLoadbalancerStickinessCookieName), + "getWeight": getFuncServiceStringLabel(label.TraefikWeight, label.DefaultWeight), + "getProtocol": getFuncServiceStringLabel(label.TraefikProtocol, label.DefaultProtocol), + "getMaxConn": getMaxConn, + "getHealthCheck": getHealthCheck, + "getCircuitBreaker": getCircuitBreaker, + "getLoadBalancer": getLoadBalancer, // Frontend Functions "getPriority": getFuncServiceStringLabel(label.TraefikFrontendPriority, label.DefaultFrontendPriority), "getPassHostHeader": getFuncServiceStringLabel(label.TraefikFrontendPassHostHeader, label.DefaultPassHostHeader), "getPassTLSCert": getFuncBoolLabel(label.TraefikFrontendPassTLSCert, false), - "hasEntryPoints": hasFuncService(label.TraefikFrontendEntryPoints), "getEntryPoints": getFuncServiceSliceStringLabel(label.TraefikFrontendEntryPoints), - "hasBasicAuth": hasFuncService(label.TraefikFrontendAuthBasic), "getBasicAuth": getFuncServiceSliceStringLabel(label.TraefikFrontendAuthBasic), "getWhitelistSourceRange": getFuncServiceSliceStringLabel(label.TraefikFrontendWhitelistSourceRange), - "hasRedirect": hasRedirect, - "getRedirectEntryPoint": getFuncServiceStringLabel(label.TraefikFrontendRedirectEntryPoint, label.DefaultFrontendRedirectEntryPoint), - "getRedirectRegex": getFuncServiceStringLabel(label.TraefikFrontendRedirectRegex, ""), - "getRedirectReplacement": getFuncServiceStringLabel(label.TraefikFrontendRedirectReplacement, ""), "getFrontendRules": getFuncServiceLabelWithPrefix(label.TraefikFrontendRule), - // Headers - "hasHeaders": hasHeaders, - "hasRequestHeaders": hasFuncService(label.TraefikFrontendRequestHeaders), - "getRequestHeaders": getFuncMapLabel(label.TraefikFrontendRequestHeaders), - "hasResponseHeaders": hasFuncService(label.TraefikFrontendResponseHeaders), - "getResponseHeaders": getFuncMapLabel(label.TraefikFrontendResponseHeaders), - "hasAllowedHostsHeaders": hasFuncService(label.TraefikFrontendAllowedHosts), - "getAllowedHostsHeaders": getFuncServiceSliceStringLabel(label.TraefikFrontendAllowedHosts), - "hasHostsProxyHeaders": hasFuncService(label.TraefikFrontendHostsProxyHeaders), - "getHostsProxyHeaders": getFuncServiceSliceStringLabel(label.TraefikFrontendHostsProxyHeaders), - "hasSSLRedirectHeaders": hasFuncService(label.TraefikFrontendSSLRedirect), - "getSSLRedirectHeaders": getFuncBoolLabel(label.TraefikFrontendSSLRedirect, false), - "hasSSLTemporaryRedirectHeaders": hasFuncService(label.TraefikFrontendSSLTemporaryRedirect), - "getSSLTemporaryRedirectHeaders": getFuncBoolLabel(label.TraefikFrontendSSLTemporaryRedirect, false), - "hasSSLHostHeaders": hasFuncService(label.TraefikFrontendSSLHost), - "getSSLHostHeaders": getFuncServiceSliceStringLabel(label.TraefikFrontendSSLHost), - "hasSSLProxyHeaders": hasFuncService(label.TraefikFrontendSSLProxyHeaders), - "getSSLProxyHeaders": getFuncMapLabel(label.TraefikFrontendSSLProxyHeaders), - "hasSTSSecondsHeaders": hasFuncService(label.TraefikFrontendSTSSeconds), - "getSTSSecondsHeaders": getFuncInt64Label(label.TraefikFrontendSTSSeconds, 0), - "hasSTSIncludeSubdomainsHeaders": hasFuncService(label.TraefikFrontendSTSIncludeSubdomains), - "getSTSIncludeSubdomainsHeaders": getFuncBoolLabel(label.TraefikFrontendSTSIncludeSubdomains, false), - "hasSTSPreloadHeaders": hasFuncService(label.TraefikFrontendSTSPreload), - "getSTSPreloadHeaders": getFuncBoolLabel(label.TraefikFrontendSTSPreload, false), - "hasForceSTSHeaderHeaders": hasFuncService(label.TraefikFrontendForceSTSHeader), - "getForceSTSHeaderHeaders": getFuncBoolLabel(label.TraefikFrontendForceSTSHeader, false), - "hasFrameDenyHeaders": hasFuncService(label.TraefikFrontendFrameDeny), - "getFrameDenyHeaders": getFuncBoolLabel(label.TraefikFrontendFrameDeny, false), - "hasCustomFrameOptionsValueHeaders": hasFuncService(label.TraefikFrontendCustomFrameOptionsValue), - "getCustomFrameOptionsValueHeaders": getFuncServiceSliceStringLabel(label.TraefikFrontendCustomFrameOptionsValue), - "hasContentTypeNosniffHeaders": hasFuncService(label.TraefikFrontendContentTypeNosniff), - "getContentTypeNosniffHeaders": getFuncBoolLabel(label.TraefikFrontendContentTypeNosniff, false), - "hasBrowserXSSFilterHeaders": hasFuncService(label.TraefikFrontendBrowserXSSFilter), - "getBrowserXSSFilterHeaders": getFuncBoolLabel(label.TraefikFrontendBrowserXSSFilter, false), - "hasContentSecurityPolicyHeaders": hasFuncService(label.TraefikFrontendContentSecurityPolicy), - "getContentSecurityPolicyHeaders": getFuncServiceSliceStringLabel(label.TraefikFrontendContentSecurityPolicy), - "hasPublicKeyHeaders": hasFuncService(label.TraefikFrontendPublicKey), - "getPublicKeyHeaders": getFuncServiceSliceStringLabel(label.TraefikFrontendPublicKey), - "hasReferrerPolicyHeaders": hasFuncService(label.TraefikFrontendReferrerPolicy), - "getReferrerPolicyHeaders": getFuncServiceSliceStringLabel(label.TraefikFrontendReferrerPolicy), - "hasIsDevelopmentHeaders": hasFuncService(label.TraefikFrontendIsDevelopment), - "getIsDevelopmentHeaders": getFuncBoolLabel(label.TraefikFrontendIsDevelopment, false), + "getHeaders": getHeaders, + "getRedirect": getRedirect, // SF Service Grouping "getGroupedServices": getFuncServicesGroupedByLabel(traefikSFGroupName), @@ -136,34 +80,6 @@ func isStateless(service ServiceItemExtended) bool { return service.ServiceKind == "Stateless" } -func hasRedirect(service ServiceItemExtended) bool { - return label.Has(service.Labels, label.TraefikFrontendRedirectEntryPoint) || - label.Has(service.Labels, label.TraefikFrontendRedirectReplacement) && label.Has(service.Labels, label.TraefikFrontendRedirectRegex) -} - -func hasLoadBalancerLabel(service ServiceItemExtended) bool { - method := label.Has(service.Labels, label.TraefikBackendLoadBalancerMethod) - sticky := label.Has(service.Labels, label.TraefikBackendLoadBalancerSticky) - stickiness := label.Has(service.Labels, label.TraefikBackendLoadBalancerStickiness) - cookieName := label.Has(service.Labels, label.TraefikBackendLoadBalancerStickinessCookieName) - return method || sticky || stickiness || cookieName -} - -func hasMaxConnLabels(service ServiceItemExtended) bool { - mca := label.Has(service.Labels, label.TraefikBackendMaxConnAmount) - mcef := label.Has(service.Labels, label.TraefikBackendMaxConnExtractorFunc) - return mca && mcef -} - -func hasHeaders(service ServiceItemExtended) bool { - for key := range service.Labels { - if strings.HasPrefix(key, label.TraefikFrontendHeaders) { - return true - } - } - return false -} - func getBackendName(service ServiceItemExtended, partition PartitionItemExtended) string { return provider.Normalize(service.Name + partition.PartitionInformation.ID) } @@ -280,3 +196,126 @@ func decodeEndpointData(endpointData string) (map[string]string, error) { return endpoints, nil } + +func getHeaders(service ServiceItemExtended) *types.Headers { + headers := &types.Headers{ + CustomRequestHeaders: label.GetMapValue(service.Labels, label.TraefikFrontendRequestHeaders), + CustomResponseHeaders: label.GetMapValue(service.Labels, label.TraefikFrontendResponseHeaders), + SSLProxyHeaders: label.GetMapValue(service.Labels, label.TraefikFrontendSSLProxyHeaders), + AllowedHosts: label.GetSliceStringValue(service.Labels, label.TraefikFrontendAllowedHosts), + HostsProxyHeaders: label.GetSliceStringValue(service.Labels, label.TraefikFrontendHostsProxyHeaders), + STSSeconds: label.GetInt64Value(service.Labels, label.TraefikFrontendSTSSeconds, 0), + SSLRedirect: label.GetBoolValue(service.Labels, label.TraefikFrontendSSLRedirect, false), + SSLTemporaryRedirect: label.GetBoolValue(service.Labels, label.TraefikFrontendSSLTemporaryRedirect, false), + STSIncludeSubdomains: label.GetBoolValue(service.Labels, label.TraefikFrontendSTSIncludeSubdomains, false), + STSPreload: label.GetBoolValue(service.Labels, label.TraefikFrontendSTSPreload, false), + ForceSTSHeader: label.GetBoolValue(service.Labels, label.TraefikFrontendForceSTSHeader, false), + FrameDeny: label.GetBoolValue(service.Labels, label.TraefikFrontendFrameDeny, false), + ContentTypeNosniff: label.GetBoolValue(service.Labels, label.TraefikFrontendContentTypeNosniff, false), + BrowserXSSFilter: label.GetBoolValue(service.Labels, label.TraefikFrontendBrowserXSSFilter, false), + IsDevelopment: label.GetBoolValue(service.Labels, label.TraefikFrontendIsDevelopment, false), + SSLHost: label.GetStringValue(service.Labels, label.TraefikFrontendSSLHost, ""), + CustomFrameOptionsValue: label.GetStringValue(service.Labels, label.TraefikFrontendCustomFrameOptionsValue, ""), + ContentSecurityPolicy: label.GetStringValue(service.Labels, label.TraefikFrontendContentSecurityPolicy, ""), + PublicKey: label.GetStringValue(service.Labels, label.TraefikFrontendPublicKey, ""), + ReferrerPolicy: label.GetStringValue(service.Labels, label.TraefikFrontendReferrerPolicy, ""), + CustomBrowserXSSValue: label.GetStringValue(service.Labels, label.TraefikFrontendCustomBrowserXSSValue, ""), + } + + if !headers.HasSecureHeadersDefined() && !headers.HasCustomHeadersDefined() { + return nil + } + + return headers +} + +func getRedirect(service ServiceItemExtended) *types.Redirect { + permanent := label.GetBoolValue(service.Labels, label.TraefikFrontendRedirectPermanent, false) + + if label.Has(service.Labels, label.TraefikFrontendRedirectEntryPoint) { + return &types.Redirect{ + EntryPoint: label.GetStringValue(service.Labels, label.TraefikFrontendRedirectEntryPoint, ""), + Permanent: permanent, + } + } + + if label.Has(service.Labels, label.TraefikFrontendRedirectRegex) && + label.Has(service.Labels, label.TraefikFrontendRedirectReplacement) { + return &types.Redirect{ + Regex: label.GetStringValue(service.Labels, label.TraefikFrontendRedirectRegex, ""), + Replacement: label.GetStringValue(service.Labels, label.TraefikFrontendRedirectReplacement, ""), + Permanent: permanent, + } + } + + return nil +} + +func getMaxConn(service ServiceItemExtended) *types.MaxConn { + amount := label.GetInt64Value(service.Labels, label.TraefikBackendMaxConnAmount, math.MinInt64) + extractorFunc := label.GetStringValue(service.Labels, label.TraefikBackendMaxConnExtractorFunc, label.DefaultBackendMaxconnExtractorFunc) + + if amount == math.MinInt64 || len(extractorFunc) == 0 { + return nil + } + + return &types.MaxConn{ + Amount: amount, + ExtractorFunc: extractorFunc, + } +} + +func getHealthCheck(service ServiceItemExtended) *types.HealthCheck { + path := label.GetStringValue(service.Labels, label.TraefikBackendHealthCheckPath, "") + if len(path) == 0 { + return nil + } + + port := label.GetIntValue(service.Labels, label.TraefikBackendHealthCheckPort, label.DefaultBackendHealthCheckPort) + interval := label.GetStringValue(service.Labels, label.TraefikBackendHealthCheckInterval, "") + + return &types.HealthCheck{ + Path: path, + Port: port, + Interval: interval, + } +} + +func getCircuitBreaker(service ServiceItemExtended) *types.CircuitBreaker { + circuitBreaker := label.GetStringValue(service.Labels, label.TraefikBackendCircuitBreakerExpression, "") + if len(circuitBreaker) == 0 { + return nil + } + return &types.CircuitBreaker{Expression: circuitBreaker} +} + +func getLoadBalancer(service ServiceItemExtended) *types.LoadBalancer { + if !label.HasPrefix(service.Labels, label.TraefikBackendLoadBalancer) { + return nil + } + + method := label.GetStringValue(service.Labels, label.TraefikBackendLoadBalancerMethod, label.DefaultBackendLoadBalancerMethod) + + lb := &types.LoadBalancer{ + Method: method, + Sticky: getSticky(service), + } + + if label.GetBoolValue(service.Labels, label.TraefikBackendLoadBalancerStickiness, false) { + cookieName := label.GetStringValue(service.Labels, label.TraefikBackendLoadBalancerStickinessCookieName, label.DefaultBackendLoadbalancerStickinessCookieName) + lb.Stickiness = &types.Stickiness{CookieName: cookieName} + } + + return lb +} + +// TODO: Deprecated +// replaced by Stickiness +// Deprecated +func getSticky(service ServiceItemExtended) bool { + if label.Has(service.Labels, label.TraefikBackendLoadBalancerSticky) { + log.Warnf("Deprecated configuration found: %s. Please use %s.", label.TraefikBackendLoadBalancerSticky, label.TraefikBackendLoadBalancerStickiness) + } + + return label.GetBoolValue(service.Labels, label.TraefikBackendLoadBalancerSticky, false) +} diff --git a/servicefabric_labelfuncs.go b/servicefabric_labelfuncs.go index af53d06..14112e4 100644 --- a/servicefabric_labelfuncs.go +++ b/servicefabric_labelfuncs.go @@ -14,18 +14,6 @@ const ( traefikSFEnableLabelOverridesDefault = true ) -func getFuncInt64Label(labelName string, defaultValue int64) func(service ServiceItemExtended) int64 { - return func(service ServiceItemExtended) int64 { - return label.GetInt64Value(service.Labels, labelName, defaultValue) - } -} - -func getFuncMapLabel(labelName string) func(service ServiceItemExtended) map[string]string { - return func(service ServiceItemExtended) map[string]string { - return label.GetMapValue(service.Labels, labelName) - } -} - func getFuncBoolLabel(labelName string, defaultValue bool) func(service ServiceItemExtended) bool { return func(service ServiceItemExtended) bool { return label.GetBoolValue(service.Labels, labelName, defaultValue) @@ -52,12 +40,6 @@ func hasService(service ServiceItemExtended, labelName string) bool { return label.Has(service.Labels, labelName) } -func hasFuncService(labelName string) func(service ServiceItemExtended) bool { - return func(service ServiceItemExtended) bool { - return label.Has(service.Labels, labelName) - } -} - func getFuncServiceLabelWithPrefix(labelName string) func(service ServiceItemExtended) map[string]string { return func(service ServiceItemExtended) map[string]string { return getServiceLabelsWithPrefix(service, labelName) diff --git a/servicefabric_test.go b/servicefabric_test.go index 335560d..b1f2b6d 100644 --- a/servicefabric_test.go +++ b/servicefabric_test.go @@ -3,7 +3,6 @@ package servicefabric import ( "context" "encoding/json" - "errors" "testing" "time" @@ -11,6 +10,8 @@ import ( "github.com/containous/traefik/safe" "github.com/containous/traefik/types" sf "github.com/jjcollinge/servicefabric" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) var apps = &sf.ApplicationItemsPage{ @@ -29,6 +30,7 @@ var apps = &sf.ApplicationItemsPage{ }, }, } + var services = &sf.ServiceItemsPage{ ContinuationToken: nil, Items: []sf.ServiceItem{ @@ -68,6 +70,7 @@ var partitions = &sf.PartitionItemsPage{ }, }, } + var instances = &sf.InstanceItemsPage{ ContinuationToken: nil, Items: []sf.InstanceItem{ @@ -82,7 +85,8 @@ var instances = &sf.InstanceItemsPage{ }, ID: "1", }, - { //Include a failed service in test data + // Include a failed service in test data + { ReplicaItemBase: &sf.ReplicaItemBase{ Address: `{"Endpoints":{"":"http://anotheraddress:8081"}}`, HealthState: "Error", @@ -103,7 +107,6 @@ var labels = map[string]string{ // TestUpdateconfig - This test ensures the provider returns a configuration message to // the configuration channel when run. func TestUpdateConfig(t *testing.T) { - client := &clientMock{ applications: apps, services: services, @@ -121,9 +124,7 @@ func TestUpdateConfig(t *testing.T) { defer pool.Stop() err := provider.updateConfig(configurationChan, pool, client, time.Millisecond*100) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) timeout := make(chan string, 1) go func() { @@ -139,85 +140,45 @@ func TestUpdateConfig(t *testing.T) { } } -func requestConfig(provider Provider, client *clientMock) (types.Configuration, error) { - config, err := provider.buildConfiguration(client) - - if err != nil { - return types.Configuration{}, err - } - - if config == nil { - return types.Configuration{}, errors.New("Returned nil config") - } - - return *config, nil -} - // TestServicesPresentInConfig tests that the basic services provide by SF // are return in the configuration object func TestServicesPresentInConfig(t *testing.T) { provider := Provider{} + client := &clientMock{ - applications: apps, - services: services, - partitions: partitions, - replicas: nil, - instances: instances, - // getServicelabelsResult: labels, + applications: apps, + services: services, + partitions: partitions, + replicas: nil, + instances: instances, getServiceExtensionMapResult: labels, expectedPropertyName: services.Items[0].ID, } - config, err := requestConfig(provider, client) - if err != nil { - t.Error(err) - } - - testCases := []struct { - desc string - check func(types.Configuration) bool - }{ - { - desc: "Has 1 Frontend", - check: func(c types.Configuration) bool { return len(c.Frontends) == 1 }, - }, - { - desc: "Has 1 backend", - check: func(c types.Configuration) bool { return len(c.Backends) == 1 }, - }, - { - desc: "Backend for 'fabric:/TestApplication/TestService' exists", - check: func(c types.Configuration) bool { - _, exists := config.Backends["fabric:/TestApplication/TestService"] - return exists - }, - }, - { - desc: "Backend has 1 server", - check: func(c types.Configuration) bool { - backend := config.Backends["fabric:/TestApplication/TestService"] - return len(backend.Servers) == 1 + config, err := provider.buildConfiguration(client) + require.NoError(t, err) + + require.NotNil(t, config, "configuration") + + expected := &types.Configuration{ + Backends: map[string]*types.Backend{ + "fabric:/TestApplication/TestService": { + Servers: map[string]types.Server{ + "1": { + URL: "http://localhost:8081", + Weight: 1, + }, + }, }, }, - { - desc: "Backend server has url 'http://localhost:8081'", - check: func(c types.Configuration) bool { - backend := config.Backends["fabric:/TestApplication/TestService"] - return backend.Servers["1"].URL == "http://localhost:8081" + Frontends: map[string]*types.Frontend{ + "frontend-fabric:/TestApplication/TestService": { + Backend: "fabric:/TestApplication/TestService", + PassHostHeader: true, }, }, } - - for _, test := range testCases { - test := test - t.Run(test.desc, func(t *testing.T) { - t.Parallel() - - if !test.check(config) { - t.Errorf("Check failed: %v", getJSON(config)) - } - }) - } + assert.Equal(t, expected, config) } // nolint: gocyclo @@ -225,7 +186,7 @@ func TestFrontendLabelConfig(t *testing.T) { testCases := []struct { desc string labels map[string]string - validate func(types.Frontend) bool + validate func(*testing.T, *types.Frontend) }{ { desc: "Has passHostHeader enabled", @@ -233,7 +194,9 @@ func TestFrontendLabelConfig(t *testing.T) { label.TraefikEnable: "true", label.TraefikFrontendPassHostHeader: "true", }, - validate: func(f types.Frontend) bool { return f.PassHostHeader }, + validate: func(t *testing.T, f *types.Frontend) { + assert.True(t, f.PassHostHeader) + }, }, { desc: "Has passHostHeader disabled", @@ -241,7 +204,9 @@ func TestFrontendLabelConfig(t *testing.T) { label.TraefikEnable: "true", label.TraefikFrontendPassHostHeader: "false", }, - validate: func(f types.Frontend) bool { return !f.PassHostHeader }, + validate: func(t *testing.T, f *types.Frontend) { + assert.False(t, f.PassHostHeader) + }, }, { desc: "Has whitelistSourceRange set", @@ -249,11 +214,11 @@ func TestFrontendLabelConfig(t *testing.T) { label.TraefikEnable: "true", label.TraefikFrontendWhitelistSourceRange: "10.0.0.1, 10.0.0.2", }, - validate: func(f types.Frontend) bool { - if len(f.WhitelistSourceRange) != 2 { - return false - } - return f.WhitelistSourceRange[0] == "10.0.0.1" && f.WhitelistSourceRange[1] == "10.0.0.2" + validate: func(t *testing.T, f *types.Frontend) { + assert.Len(t, f.WhitelistSourceRange, 2) + + expected := []string{"10.0.0.1", "10.0.0.2"} + assert.EqualValues(t, expected, f.WhitelistSourceRange) }, }, { @@ -262,26 +227,34 @@ func TestFrontendLabelConfig(t *testing.T) { label.TraefikEnable: "true", label.TraefikFrontendPriority: "13", }, - validate: func(f types.Frontend) bool { return f.Priority == 13 }, + validate: func(t *testing.T, f *types.Frontend) { + assert.Equal(t, f.Priority, 13) + }, }, { desc: "Has basicAuth set", labels: map[string]string{ label.TraefikEnable: "true", - label.TraefikFrontendAuthBasic: "USER1:HASH1, USER2:HASH2", + label.TraefikFrontendAuthBasic: "USER1:HASH1, USER1:HASH1", }, - validate: func(f types.Frontend) bool { - return len(f.BasicAuth) == 2 && f.BasicAuth[0] == "USER1:HASH1" + validate: func(t *testing.T, f *types.Frontend) { + assert.Len(t, f.BasicAuth, 2) + + expected := []string{"USER1:HASH1", "USER1:HASH1"} + assert.EqualValues(t, expected, f.BasicAuth) }, }, { - desc: "Has entrypoints set", + desc: "Has frontend entry points set", labels: map[string]string{ label.TraefikEnable: "true", label.TraefikFrontendEntryPoints: "Barry, Bob", }, - validate: func(f types.Frontend) bool { - return len(f.EntryPoints) == 2 && f.EntryPoints[0] == "Barry" && f.EntryPoints[1] == "Bob" + validate: func(t *testing.T, f *types.Frontend) { + assert.Len(t, f.EntryPoints, 2) + + expected := []string{"Barry", "Bob"} + assert.EqualValues(t, expected, f.EntryPoints) }, }, { @@ -290,7 +263,9 @@ func TestFrontendLabelConfig(t *testing.T) { label.TraefikEnable: "true", label.TraefikFrontendPassTLSCert: "true", }, - validate: func(f types.Frontend) bool { return f.PassTLSCert }, + validate: func(t *testing.T, f *types.Frontend) { + assert.True(t, f.PassTLSCert) + }, }, { desc: "Has passTLSCert disabled", @@ -298,7 +273,9 @@ func TestFrontendLabelConfig(t *testing.T) { label.TraefikEnable: "true", label.TraefikFrontendPassTLSCert: "false", }, - validate: func(f types.Frontend) bool { return !f.PassTLSCert }, + validate: func(t *testing.T, f *types.Frontend) { + assert.False(t, f.PassTLSCert) + }, }, { desc: "Has rule set", @@ -306,184 +283,64 @@ func TestFrontendLabelConfig(t *testing.T) { label.TraefikEnable: "true", label.TraefikFrontendRule + ".default": "Path: /", }, - validate: func(f types.Frontend) bool { - return len(f.Routes) == 1 && f.Routes[label.TraefikFrontendRule+".default"].Rule == "Path: /" - }, - }, - { - desc: "Has SSLRedirectHeaders set", - labels: map[string]string{ - label.TraefikEnable: "true", - label.TraefikFrontendSSLRedirect: "true", - }, - validate: func(f types.Frontend) bool { - return f.Headers.SSLRedirect - }, - }, - { - desc: "Has Temporary SSLRedirectHeaders set", - labels: map[string]string{ - label.TraefikEnable: "true", - label.TraefikFrontendSSLTemporaryRedirect: "true", - }, - validate: func(f types.Frontend) bool { - return f.Headers.SSLTemporaryRedirect - }, - }, - //Todo: Is this behaviour correct "bob.bob.com" => "[bob.bob.com]"? - { - desc: "Has SSLHostHeaders set", - labels: map[string]string{ - label.TraefikEnable: "true", - label.TraefikFrontendSSLHost: "bob.bob.com", - }, - validate: func(f types.Frontend) bool { - return f.Headers.SSLHost == "[bob.bob.com]" - }, - }, - { - desc: "Has STSSecondsHeaders set", - labels: map[string]string{ - label.TraefikEnable: "true", - label.TraefikFrontendSTSSeconds: "1337", - }, - validate: func(f types.Frontend) bool { - return f.Headers.STSSeconds == 1337 - }, - }, - { - desc: "Has STSIncludeSubdomainsHeaders set", - labels: map[string]string{ - label.TraefikEnable: "true", - label.TraefikFrontendSTSIncludeSubdomains: "true", - }, - validate: func(f types.Frontend) bool { - return f.Headers.STSIncludeSubdomains - }, - }, - { - desc: "Has STSPreloadHeaders set", - labels: map[string]string{ - label.TraefikEnable: "true", - label.TraefikFrontendSTSPreload: "true", - }, - validate: func(f types.Frontend) bool { - return f.Headers.STSPreload - }, - }, - { - desc: "Has hasForceSTSHeaderHeaders set", - labels: map[string]string{ - label.TraefikEnable: "true", - label.TraefikFrontendForceSTSHeader: "true", - }, - validate: func(f types.Frontend) bool { - return f.Headers.ForceSTSHeader - }, - }, - { - desc: "Has hasForceSTSHeaderHeaders set", - labels: map[string]string{ - label.TraefikEnable: "true", - label.TraefikFrontendForceSTSHeader: "true", - }, - validate: func(f types.Frontend) bool { - return f.Headers.ForceSTSHeader - }, - }, - { - desc: "Has FrameDeny enabled", - labels: map[string]string{ - label.TraefikEnable: "true", - label.TraefikFrontendFrameDeny: "true", - }, - validate: func(f types.Frontend) bool { return f.Headers.FrameDeny }, - }, - { - desc: "Has FrameDeny disabled", - labels: map[string]string{ - label.TraefikEnable: "true", - label.TraefikFrontendFrameDeny: "false", + validate: func(t *testing.T, f *types.Frontend) { + assert.Len(t, f.Routes, 1) + + expected := map[string]types.Route{ + label.TraefikFrontendRule + ".default": { + Rule: "Path: /", + }, + } + assert.Equal(t, expected, f.Routes) }, - validate: func(f types.Frontend) bool { return !f.Headers.FrameDeny }, }, - //Todo: Is this behaviour correct "SAMEORIGIN" => "[SAMEORIGIN]"? { - desc: "hasCustomFrameOptionsValueHeaders", + desc: "Has Headers set", labels: map[string]string{ label.TraefikEnable: "true", + label.TraefikFrontendSSLRedirect: "true", + label.TraefikFrontendSSLTemporaryRedirect: "true", + label.TraefikFrontendSSLHost: "bob.bob.com", + label.TraefikFrontendSTSSeconds: "1337", + label.TraefikFrontendSTSIncludeSubdomains: "true", + label.TraefikFrontendSTSPreload: "true", + label.TraefikFrontendForceSTSHeader: "true", + label.TraefikFrontendFrameDeny: "true", label.TraefikFrontendCustomFrameOptionsValue: "SAMEORIGIN", - }, - validate: func(f types.Frontend) bool { return f.Headers.CustomFrameOptionsValue == "[SAMEORIGIN]" }, - }, - { - desc: "Has ContentTypeNosniffHeaders set", - labels: map[string]string{ - label.TraefikEnable: "true", - label.TraefikFrontendContentTypeNosniff: "true", - }, - validate: func(f types.Frontend) bool { - return f.Headers.ContentTypeNosniff - }, - }, - { - desc: "Has BrowserXSSFilterHeaders set", - labels: map[string]string{ - label.TraefikEnable: "true", - label.TraefikFrontendBrowserXSSFilter: "true", - }, - validate: func(f types.Frontend) bool { - return f.Headers.BrowserXSSFilter - }, - }, - { - desc: "Has ContentSecurityPolicyHeaders set", - labels: map[string]string{ - label.TraefikEnable: "true", - label.TraefikFrontendContentSecurityPolicy: "plugin-types image/png application/pdf; sandbox", - }, - validate: func(f types.Frontend) bool { - return f.Headers.ContentSecurityPolicy == "[plugin-types image/png application/pdf; sandbox]" - }, - }, - { - desc: "Has PublicKeyHeaders set", - labels: map[string]string{ - label.TraefikEnable: "true", - label.TraefikFrontendPublicKey: "somekeydata", - }, - validate: func(f types.Frontend) bool { - return f.Headers.PublicKey == "[somekeydata]" - }, - }, - { - desc: "Has ReferrerPolicyHeaders set", - labels: map[string]string{ - label.TraefikEnable: "true", - label.TraefikFrontendReferrerPolicy: "same-origin", - }, - validate: func(f types.Frontend) bool { - return f.Headers.ReferrerPolicy == "[same-origin]" - }, - }, - { - desc: "Has IsDevelopmentHeaders set", - labels: map[string]string{ - label.TraefikEnable: "true", - label.TraefikFrontendIsDevelopment: "true", - }, - validate: func(f types.Frontend) bool { - return f.Headers.IsDevelopment - }, - }, - { - desc: "Has AllowedhostHeaders set", - labels: map[string]string{ - label.TraefikEnable: "true", - label.TraefikFrontendAllowedHosts: "host1, host2", - }, - validate: func(f types.Frontend) bool { - return f.Headers.AllowedHosts[0] == "host1" + label.TraefikFrontendContentTypeNosniff: "true", + label.TraefikFrontendBrowserXSSFilter: "true", + label.TraefikFrontendContentSecurityPolicy: "plugin-types image/png application/pdf; sandbox", + label.TraefikFrontendPublicKey: "somekeydata", + label.TraefikFrontendReferrerPolicy: "same-origin", + label.TraefikFrontendIsDevelopment: "true", + label.TraefikFrontendAllowedHosts: "host1, host2", + label.TraefikFrontendSSLProxyHeaders: "X-Testing:testing||X-Testing2:testing2", + }, + validate: func(t *testing.T, f *types.Frontend) { + expected := &types.Headers{ + SSLProxyHeaders: map[string]string{ + "X-Testing": "testing", + "X-Testing2": "testing2", + }, + AllowedHosts: []string{"host1", "host2"}, + HostsProxyHeaders: nil, + SSLRedirect: true, + SSLTemporaryRedirect: true, + SSLHost: "bob.bob.com", + STSSeconds: 1337, + STSIncludeSubdomains: true, + STSPreload: true, + ForceSTSHeader: true, + FrameDeny: true, + CustomFrameOptionsValue: "SAMEORIGIN", + ContentTypeNosniff: true, + BrowserXSSFilter: true, + ContentSecurityPolicy: "plugin-types image/png application/pdf; sandbox", + PublicKey: "somekeydata", + ReferrerPolicy: "same-origin", + IsDevelopment: true, + } + assert.Equal(t, expected, f.Headers) }, }, { @@ -492,8 +349,14 @@ func TestFrontendLabelConfig(t *testing.T) { label.TraefikEnable: "true", label.TraefikFrontendRequestHeaders: "X-Testing:testing||X-Testing2:testing2", }, - validate: func(f types.Frontend) bool { - return len(f.Headers.CustomRequestHeaders) == 2 && f.Headers.CustomRequestHeaders["X-Testing"] == "testing" + validate: func(t *testing.T, f *types.Frontend) { + require.NotNil(t, f.Headers, "headers") + + expected := map[string]string{ + "X-Testing": "testing", + "X-Testing2": "testing2", + } + assert.Equal(t, expected, f.Headers.CustomRequestHeaders) }, }, { @@ -502,18 +365,14 @@ func TestFrontendLabelConfig(t *testing.T) { label.TraefikEnable: "true", label.TraefikFrontendResponseHeaders: "X-Testing:testing||X-Testing2:testing2", }, - validate: func(f types.Frontend) bool { - return len(f.Headers.CustomResponseHeaders) == 2 && f.Headers.CustomResponseHeaders["X-Testing"] == "testing" - }, - }, - { - desc: "Has SSLProxyHeaders set", - labels: map[string]string{ - label.TraefikEnable: "true", - label.TraefikFrontendSSLProxyHeaders: "X-Testing:testing||X-Testing2:testing2", - }, - validate: func(f types.Frontend) bool { - return len(f.Headers.SSLProxyHeaders) == 2 && f.Headers.SSLProxyHeaders["X-Testing"] == "testing" + validate: func(t *testing.T, f *types.Frontend) { + require.NotNil(t, f.Headers, "headers") + + expected := map[string]string{ + "X-Testing": "testing", + "X-Testing2": "testing2", + } + assert.Equal(t, expected, f.Headers.CustomResponseHeaders) }, }, } @@ -522,7 +381,9 @@ func TestFrontendLabelConfig(t *testing.T) { test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() + provider := Provider{} + client := &clientMock{ applications: apps, services: services, @@ -532,22 +393,17 @@ func TestFrontendLabelConfig(t *testing.T) { getServiceExtensionMapResult: test.labels, } - config, err := requestConfig(provider, client) - if err != nil { - t.Error(err) - } + config, err := provider.buildConfiguration(client) + require.NoError(t, err) - if config.Frontends == nil || len(config.Frontends) != 1 { - t.Error("No frontends present in the config") - } + assert.NotEmpty(t, config.Frontends, "No frontends present in the configuration") - for _, frontend := range config.Frontends { - if frontend == nil { - t.Error("Frontend is nil") - } - if !test.validate(*frontend) { + for fname, frontend := range config.Frontends { + require.NotNil(t, frontend, "Frontend %s is nil", fname) + + test.validate(t, frontend) + if t.Failed() { t.Log(getJSON(frontend)) - t.Fail() } } }) @@ -559,29 +415,34 @@ func TestBackendLabelConfig(t *testing.T) { testCases := []struct { desc string labels map[string]string - validate func(types.Backend) bool + validate func(*testing.T, *types.Backend) }{ { - desc: "Has DRR Loadbalencer", + desc: "Has DRR LoadBalancer", labels: map[string]string{ label.TraefikEnable: "true", label.TraefikBackendLoadBalancerMethod: "drr", }, - validate: func(b types.Backend) bool { return b.LoadBalancer.Method == "drr" }, + validate: func(t *testing.T, b *types.Backend) { + require.NotNil(t, b.LoadBalancer, "LoadBalancer") + assert.Equal(t, "drr", b.LoadBalancer.Method) + }, }, { - desc: "Has healthcheck set", + desc: "Has health check set", labels: map[string]string{ label.TraefikEnable: "true", label.TraefikBackendHealthCheckPath: "/hc", label.TraefikBackendHealthCheckPort: "9000", label.TraefikBackendHealthCheckInterval: "1337s", }, - validate: func(b types.Backend) bool { - if b.HealthCheck == nil { - return false + validate: func(t *testing.T, b *types.Backend) { + expected := &types.HealthCheck{ + Path: "/hc", + Port: 9000, + Interval: "1337s", } - return b.HealthCheck.Path == "/hc" && b.HealthCheck.Interval == "1337s" + assert.Equal(t, expected, b.HealthCheck) }, }, { @@ -590,31 +451,23 @@ func TestBackendLabelConfig(t *testing.T) { label.TraefikEnable: "true", label.TraefikBackendCircuitBreakerExpression: "NetworkErrorRatio() > 0.5", }, - validate: func(b types.Backend) bool { - if b.CircuitBreaker == nil { - return false + validate: func(t *testing.T, b *types.Backend) { + expected := &types.CircuitBreaker{ + Expression: "NetworkErrorRatio() > 0.5", } - return b.CircuitBreaker.Expression == "NetworkErrorRatio() > 0.5" + assert.Equal(t, expected, b.CircuitBreaker) }, }, - // { - // desc: "Has stickiness loadbalencer set with cookie name", - // labels: map[string]string{ - // label.TraefikEnable: "true", - // label.TraefikBackendLoadBalancerStickiness: "true", - // label.TraefikBackendLoadBalancerStickinessCookieName: "stickycookie", - // }, - // validate: func(b types.Backend) bool { - // return b.LoadBalancer.Stickiness != nil && b.LoadBalancer.Stickiness.CookieName == "stickycookie" - // }, - // }, { desc: "Has stickiness cookie set", labels: map[string]string{ label.TraefikEnable: "true", label.TraefikBackendLoadBalancerStickiness: "true", }, - validate: func(b types.Backend) bool { return b.LoadBalancer.Stickiness != nil }, + validate: func(t *testing.T, b *types.Backend) { + require.NotNil(t, b.LoadBalancer, "LoadBalancer") + assert.NotNil(t, b.LoadBalancer.Stickiness, "Stickiness") + }, }, { desc: "Has maxconn amount and extractor func", @@ -623,11 +476,12 @@ func TestBackendLabelConfig(t *testing.T) { label.TraefikBackendMaxConnAmount: "1337", label.TraefikBackendMaxConnExtractorFunc: "request.header.TEST_HEADER", }, - validate: func(b types.Backend) bool { - if b.MaxConn == nil { - return false + validate: func(t *testing.T, b *types.Backend) { + expected := &types.MaxConn{ + Amount: 1337, + ExtractorFunc: "request.header.TEST_HEADER", } - return b.MaxConn.Amount == 1337 && b.MaxConn.ExtractorFunc == "request.header.TEST_HEADER" + assert.Equal(t, expected, b.MaxConn) }, }, } @@ -636,7 +490,9 @@ func TestBackendLabelConfig(t *testing.T) { test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() + provider := Provider{} + client := &clientMock{ applications: apps, services: services, @@ -645,25 +501,18 @@ func TestBackendLabelConfig(t *testing.T) { instances: instances, getServiceExtensionMapResult: test.labels, } - config, err := requestConfig(provider, client) - if err != nil { - t.Error(err) - } - if err != nil { - t.Error(err) - } - if len(config.Backends) != 1 { - t.Error("No backends present in the config") - } + config, err := provider.buildConfiguration(client) + require.NoError(t, err) - for _, backend := range config.Backends { - if backend == nil { - t.Error("backend is nil") - } - if !test.validate(*backend) { + assert.NotEmpty(t, config.Backends, "No backends present in the configuration") + + for bname, backend := range config.Backends { + require.NotNil(t, backend, "Backend %s is nil", bname) + + test.validate(t, backend) + if t.Failed() { t.Log(getJSON(backend)) - t.Fail() } } }) @@ -675,6 +524,7 @@ func TestDisableLabelOverrides(t *testing.T) { label.TraefikEnable: "true", traefikSFEnableLabelOverrides: "false", } + propertyLabels := map[string]string{ "shouldnotexist": "true", } @@ -691,23 +541,13 @@ func TestDisableLabelOverrides(t *testing.T) { } res, err := getLabels(client, &services.Items[0], &apps.Items[0]) - - if err != nil { - t.Log(err) - t.FailNow() - } + require.NoError(t, err) _, exists := res["shouldnotexist"] - if exists { - t.Fail() - } + assert.False(t, exists) } func TestGroupedServicesFrontends(t *testing.T) { - groupName := "groupedbackends" - groupWeight := "154" - - provider := Provider{} client := &clientMock{ applications: apps, services: services, @@ -716,48 +556,33 @@ func TestGroupedServicesFrontends(t *testing.T) { instances: instances, getServiceExtensionMapResult: map[string]string{ label.TraefikEnable: "true", - traefikSFGroupName: groupName, - traefikSFGroupWeight: groupWeight, + traefikSFGroupName: "groupedbackends", + traefikSFGroupWeight: "154", }, } - config, err := requestConfig(provider, client) - if err != nil { - t.Error(err) - } - if err != nil { - t.Error(err) - } - if len(config.Frontends) != 2 { - t.Log(getJSON(config)) - t.Log("Incorrect count of frontends present in the config") - t.FailNow() - } + provider := Provider{} - if len(config.Backends) != 2 { - t.Log(getJSON(config)) - t.Log("Incorrect count of backends present in the config") - t.FailNow() - } + config, err := provider.buildConfiguration(client) + require.NoError(t, err) - frontend, exists := config.Frontends[groupName] + require.NotNil(t, config, "configuration") - if !exists { - t.Log(getJSON(config)) - t.Log("Missing frontend for grouped service") - t.FailNow() + expectedFrontends := map[string]*types.Frontend{ + "frontend-fabric:/TestApplication/TestService": { + Backend: "fabric:/TestApplication/TestService", + PassHostHeader: true, + }, + "groupedbackends": { + Backend: "groupedbackends", + Priority: 50, + }, } - if frontend.Priority == 50 && frontend.Backend == groupName { - t.Log("Frontend exists for group") - } + assert.Equal(t, expectedFrontends, config.Frontends) } func TestGroupedServicesBackends(t *testing.T) { - groupName := "groupedbackends" - groupWeight := "154" - - provider := Provider{} client := &clientMock{ applications: apps, services: services, @@ -766,44 +591,37 @@ func TestGroupedServicesBackends(t *testing.T) { instances: instances, getServiceExtensionMapResult: map[string]string{ label.TraefikEnable: "true", - traefikSFGroupName: groupName, - traefikSFGroupWeight: groupWeight, + traefikSFGroupName: "groupedbackends", + traefikSFGroupWeight: "154", }, } - config, err := requestConfig(provider, client) - if err != nil { - t.Error(err) - } - if err != nil { - t.Error(err) - } - if len(config.Backends) != 2 { - t.Log(getJSON(config)) - t.Log("Incorrect count of backends present in the config") - t.FailNow() - } + provider := Provider{} - backend, exists := config.Backends[groupName] - if !exists { - t.Log(getJSON(config)) - t.Log("Missing backend for grouped service") - t.FailNow() - } + config, err := provider.buildConfiguration(client) + require.NoError(t, err) - if len(backend.Servers) != 1 { - t.Log(getJSON(config)) - t.Log("Incorrect number of backend servers on grouped service") - t.FailNow() - } + require.NotNil(t, config, "configuration") - for _, server := range backend.Servers { - if server.Weight != 154 { - t.Log(getJSON(config)) - t.Log("Incorrect weight on grouped service") - t.FailNow() - } + expected := map[string]*types.Backend{ + "fabric:/TestApplication/TestService": { + Servers: map[string]types.Server{ + "1": { + URL: "http://localhost:8081", + Weight: 1, + }, + }, + }, + "groupedbackends": { + Servers: map[string]types.Server{ + "TestApplication/TestService-1": { + URL: "http://localhost:8081", + Weight: 154, + }, + }, + }, } + assert.Equal(t, expected, config.Backends) } func TestIsPrimary(t *testing.T) { diff --git a/servicefabric_tmpl.go b/servicefabric_tmpl.go index 2ae094a..7cfc966 100644 --- a/servicefabric_tmpl.go +++ b/servicefabric_tmpl.go @@ -2,228 +2,208 @@ package servicefabric const tmpl = ` [backends] - {{range $aggName, $aggServices := getGroupedServices .Services }} - [backends."{{$aggName}}"] - {{range $service := $aggServices}} - {{range $partition := $service.Partitions}} - {{range $instance := $partition.Instances}} - [backends."{{$aggName}}".servers."{{$service.ID}}-{{$instance.ID}}"] - url = "{{getDefaultEndpoint $instance}}" - weight = {{getGroupedWeight $service}} - {{end}} - {{end}} - {{end}} - {{end}} - {{range $service := .Services}} - {{if isEnabled $service}} - {{range $partition := $service.Partitions}} - {{if isStateless $service}} - [backends."{{$service.Name}}"] - [backends."{{$service.Name}}".LoadBalancer] - {{if hasLoadBalancerLabel $service}} - method = "{{getLoadBalancerMethod $service }}" - {{end}} +{{range $aggName, $aggServices := getGroupedServices .Services }} + [backends."{{ $aggName }}"] + {{range $service := $aggServices }} + {{range $partition := $service.Partitions }} + {{range $instance := $partition.Instances }} + [backends."{{ $aggName }}".servers."{{ $service.ID }}-{{ $instance.ID }}"] + url = "{{ getDefaultEndpoint $instance }}" + weight = {{ getGroupedWeight $service }} + {{end}} + {{end}} + {{end}} +{{end}} - {{if hasHealthCheckLabels $service}} - [backends."{{$service.Name}}".healthcheck] - path = "{{getHealthCheckPath $service}}" - interval = "{{getHealthCheckInterval $service }}" - port = {{getHealthCheckPort $service}} - {{end}} +{{range $service := .Services }} + {{if isEnabled $service }} + {{range $partition := $service.Partitions }} + + {{if isStateless $service }} + + {{ $backendName := $service.Name }} + [backends."{{ $backendName }}"] - {{if hasStickinessLabel $service}} - [backends."{{$service.Name}}".LoadBalancer.stickiness] + {{ $circuitBreaker := getCircuitBreaker $service }} + {{if $circuitBreaker }} + [backends."{{ $backendName }}".circuitBreaker] + expression = "{{ $circuitBreaker.Expression }}" {{end}} - sticky = {{getSticky $service}} - {{if hasStickinessLabel $service}} - [backends."{{$service.Name}}".loadBalancer.stickiness] - cookieName = "{{getStickinessCookieName $service}}" + {{ $loadBalancer := getLoadBalancer $service }} + {{if $loadBalancer }} + [backends."{{ $backendName }}".loadBalancer] + method = "{{ $loadBalancer.Method }}" + sticky = {{ $loadBalancer.Sticky }} + {{if $loadBalancer.Stickiness }} + [backends."{{ $backendName }}".loadBalancer.stickiness] + cookieName = "{{ $loadBalancer.Stickiness.CookieName }}" + {{end}} {{end}} - {{if hasCircuitBreakerLabel $service}} - [backends."{{$service.Name}}".circuitBreaker] - expression = "{{getCircuitBreakerExpression $service}}" + {{ $maxConn := getMaxConn $service }} + {{if $maxConn }} + [backends."{{ $backendName }}".maxConn] + extractorFunc = "{{ $maxConn.ExtractorFunc }}" + amount = {{ $maxConn.Amount }} {{end}} - {{if hasMaxConnLabels $service}} - [backends."{{$service.Name}}".maxConn] - amount = {{getMaxConnAmount $service}} - extractorFunc = "{{getMaxConnExtractorFunc $service}}" + {{ $healthCheck := getHealthCheck $service }} + {{if $healthCheck }} + [backends."{{ $backendName }}".healthCheck] + path = "{{ $healthCheck.Path }}" + port = {{ $healthCheck.Port }} + interval = "{{ $healthCheck.Interval }}" {{end}} {{range $instance := $partition.Instances}} - [backends."{{$service.Name}}".servers."{{$instance.ID}}"] - url = "{{getDefaultEndpoint $instance}}" - weight = {{getLabelValue $service "backend.weight" "1"}} + [backends."{{ $service.Name }}".servers."{{ $instance.ID }}"] + url = "{{ getDefaultEndpoint $instance }}" + weight = {{ getLabelValue $service "backend.weight" "1" }} {{end}} + {{else if isStateful $service}} + {{range $replica := $partition.Replicas}} {{if isPrimary $replica}} - - {{$backendName := getBackendName $service $partition}} - [backends."{{$backendName}}".servers."{{$replica.ID}}"] - url = "{{getDefaultEndpoint $replica}}" - weight = 1 + {{ $backendName := getBackendName $service $partition }} + [backends."{{ $backendName }}".servers."{{ $replica.ID }}"] + url = "{{ getDefaultEndpoint $replica }}" + weight = 1 [backends."{{$backendName}}".LoadBalancer] - method = "drr" - - [backends."{{$backendName}}".circuitbreaker] - expression = "NetworkErrorRatio() > 0.5" + method = "drr" {{end}} {{end}} + {{end}} + {{end}} {{end}} {{end}} [frontends] -{{range $groupName, $groupServices := getGroupedServices .Services}} - {{$service := index $groupServices 0}} - [frontends."{{$groupName}}"] - backend = "{{$groupName}}" - +{{range $groupName, $groupServices := getGroupedServices .Services }} + {{ $service := index $groupServices 0 }} + [frontends."{{ $groupName }}"] + backend = "{{ $groupName }}" priority = 50 - {{range $key, $value := getFrontendRules $service}} - [frontends."{{$groupName}}".routes."{{$key}}"] - rule = "{{$value}}" - {{end}} + {{range $key, $value := getFrontendRules $service }} + [frontends."{{ $groupName }}".routes."{{ $key }}"] + rule = "{{ $value }}" + {{end}} {{end}} -{{range $service := .Services}} - {{if isEnabled $service}} - {{$frontend := $service.Name}} - {{if isStateless $service}} - - [frontends."frontend-{{$frontend}}"] - backend = "{{$service.Name}}" - - passHostHeader = {{getPassHostHeader $service }} - - passTLSCert = {{getPassTLSCert $service}} - - {{if getWhitelistSourceRange $service}} - whitelistSourceRange = [{{range getWhitelistSourceRange $service}} - "{{.}}", - {{end}}] - {{end}} - priority = {{ getPriority $service }} +{{range $service := .Services }} + {{if isEnabled $service }} + {{ $frontendName := $service.Name }} - {{if hasBasicAuth $service}} - basicAuth = [{{range getBasicAuth $service }} - "{{.}}", - {{end}}] - {{end}} + {{if isStateless $service }} - {{if hasEntryPoints $service}} - entryPoints = [{{range getEntryPoints $service}} - "{{.}}", - {{end}}] - {{end}} - - {{ if hasHeaders $service}} - [frontends."frontend-{{$frontend}}".headers] - {{if hasSSLRedirectHeaders $service}} - SSLRedirect = {{getSSLRedirectHeaders $service}} - {{end}} - {{if hasSSLTemporaryRedirectHeaders $service}} - SSLTemporaryRedirect = {{getSSLTemporaryRedirectHeaders $service}} - {{end}} - {{if hasSSLHostHeaders $service}} - SSLHost = "{{getSSLHostHeaders $service}}" - {{end}} - {{if hasSTSSecondsHeaders $service}} - STSSeconds = {{getSTSSecondsHeaders $service}} - {{end}} - {{if hasSTSIncludeSubdomainsHeaders $service}} - STSIncludeSubdomains = {{getSTSIncludeSubdomainsHeaders $service}} - {{end}} - {{if hasSTSPreloadHeaders $service}} - STSPreload = {{getSTSPreloadHeaders $service}} - {{end}} - {{if hasForceSTSHeaderHeaders $service}} - ForceSTSHeader = {{getForceSTSHeaderHeaders $service}} - {{end}} - {{if hasFrameDenyHeaders $service}} - FrameDeny = {{getFrameDenyHeaders $service}} - {{end}} - {{if hasCustomFrameOptionsValueHeaders $service}} - CustomFrameOptionsValue = "{{getCustomFrameOptionsValueHeaders $service}}" - {{end}} - {{if hasContentTypeNosniffHeaders $service}} - ContentTypeNosniff = {{getContentTypeNosniffHeaders $service}} - {{end}} - {{if hasBrowserXSSFilterHeaders $service}} - BrowserXSSFilter = {{getBrowserXSSFilterHeaders $service}} - {{end}} - {{if hasContentSecurityPolicyHeaders $service}} - ContentSecurityPolicy = "{{getContentSecurityPolicyHeaders $service}}" - {{end}} - {{if hasPublicKeyHeaders $service}} - PublicKey = "{{getPublicKeyHeaders $service}}" - {{end}} - {{if hasReferrerPolicyHeaders $service}} - ReferrerPolicy = "{{getReferrerPolicyHeaders $service}}" - {{end}} - {{if hasIsDevelopmentHeaders $service}} - IsDevelopment = {{getIsDevelopmentHeaders $service}} - {{end}} + [frontends."frontend-{{ $frontendName }}"] + backend = "{{ $service.Name }}" + passHostHeader = {{ getPassHostHeader $service }} + passTLSCert = {{ getPassTLSCert $service }} + priority = {{ getPriority $service }} - {{if hasAllowedHostsHeaders $service}} - AllowedHosts = [{{range getAllowedHostsHeaders $service}} + {{ $entryPoints := getEntryPoints $service }} + {{if $entryPoints }} + entryPoints = [{{range $entryPoints }} "{{.}}", {{end}}] - {{end}} + {{end}} - {{if hasHostsProxyHeaders $service}} - HostsProxyHeaders = [{{range getHostsProxyHeaders $service}} + {{ $whitelistSourceRange := getWhitelistSourceRange $service }} + {{if $whitelistSourceRange }} + whitelistSourceRange = [{{range $whitelistSourceRange }} "{{.}}", {{end}}] - {{end}} + {{end}} - {{if hasRequestHeaders $service}} - [frontends."frontend-{{$frontend}}".headers.customRequestHeaders] - {{range $k, $v := getRequestHeaders $service}} - {{$k}} = "{{$v}}" + {{ $basicAuth := getBasicAuth $service }} + {{if $basicAuth }} + basicAuth = [{{range $basicAuth }} + "{{.}}", + {{end}}] + {{end}} + + {{ $headers := getHeaders $service }} + {{if $headers }} + [frontends."frontend-{{ $frontendName }}".headers] + SSLRedirect = {{ $headers.SSLRedirect }} + SSLTemporaryRedirect = {{ $headers.SSLTemporaryRedirect }} + SSLHost = "{{ $headers.SSLHost }}" + STSSeconds = {{ $headers.STSSeconds }} + STSIncludeSubdomains = {{ $headers.STSIncludeSubdomains }} + STSPreload = {{ $headers.STSPreload }} + ForceSTSHeader = {{ $headers.ForceSTSHeader }} + FrameDeny = {{ $headers.FrameDeny }} + CustomFrameOptionsValue = "{{ $headers.CustomFrameOptionsValue }}" + ContentTypeNosniff = {{ $headers.ContentTypeNosniff }} + BrowserXSSFilter = {{ $headers.BrowserXSSFilter }} + CustomBrowserXSSValue = "{{ $headers.CustomBrowserXSSValue }}" + ContentSecurityPolicy = "{{ $headers.ContentSecurityPolicy }}" + PublicKey = "{{ $headers.PublicKey }}" + ReferrerPolicy = "{{ $headers.ReferrerPolicy }}" + IsDevelopment = {{ $headers.IsDevelopment }} + + {{if $headers.AllowedHosts }} + AllowedHosts = [{{range $headers.AllowedHosts }} + "{{.}}", + {{end}}] {{end}} - {{end}} - {{if hasResponseHeaders $service}} - [frontends."frontend-{{$frontend}}".headers.customResponseHeaders] - {{range $k, $v := getResponseHeaders $service}} - {{$k}} = "{{$v}}" + {{if $headers.HostsProxyHeaders }} + HostsProxyHeaders = [{{range $headers.HostsProxyHeaders }} + "{{.}}", + {{end}}] {{end}} - {{end}} - {{if hasSSLProxyHeaders $service}} - [frontends."frontend-{{$frontend}}".headers.SSLProxyHeaders] - {{range $k, $v := getSSLProxyHeaders $service}} - {{$k}} = "{{$v}}" + {{if $headers.CustomRequestHeaders }} + [frontends."frontend-{{ $frontendName }}".headers.customRequestHeaders] + {{range $k, $v := $headers.CustomRequestHeaders }} + {{$k}} = "{{$v}}" + {{end}} + {{end}} + + {{if $headers.CustomResponseHeaders }} + [frontends."frontend-{{ $frontendName }}".headers.customResponseHeaders] + {{range $k, $v := $headers.CustomResponseHeaders }} + {{$k}} = "{{$v}}" + {{end}} + {{end}} + + {{if $headers.SSLProxyHeaders }} + [frontends."frontend-{{ $frontendName }}".headers.SSLProxyHeaders] + {{range $k, $v := $headers.SSLProxyHeaders }} + {{$k}} = "{{$v}}" + {{end}} {{end}} {{end}} - {{end}} - {{range $key, $value := getFrontendRules $service}} - [frontends."frontend-{{$frontend}}".routes."{{$key}}"] - rule = "{{$value}}" - {{end}} + {{range $key, $value := getFrontendRules $service }} + [frontends."frontend-{{ $frontendName }}".routes."{{ $key }}"] + rule = "{{ $value }}" + {{end}} {{else if isStateful $service}} - {{range $partition := $service.Partitions}} - {{$partitionId := $partition.PartitionInformation.ID}} - {{if hasLabel $service "frontend.rule"}} - [frontends."{{$service.Name}}/{{$partitionId}}"] - backend = "{{getBackendName $service.Name $partition}}" - [frontends."{{$service.Name}}/{{$partitionId}}".routes.default] - rule = {{getLabelValue $service "frontend.rule.partition.$partitionId" ""}} + {{range $partition := $service.Partitions }} + {{ $partitionId := $partition.PartitionInformation.ID }} + {{if hasLabel $service "frontend.rule" }} + [frontends."{{ $service.Name }}/{{ $partitionId }}"] + backend = "{{ getBackendName $service.Name $partition }}" + + [frontends."{{ $service.Name }}/{{ $partitionId }}".routes.default] + rule = {{ getLabelValue $service "frontend.rule.partition.$partitionId" "" }} + {{end}} {{end}} + {{end}} + {{end}} {{end}} -{{end}} `