diff --git a/adapter/config/default_config.go b/adapter/config/default_config.go index 3e3c917ef6..396daf4eaf 100644 --- a/adapter/config/default_config.go +++ b/adapter/config/default_config.go @@ -75,6 +75,8 @@ var defaultConfig = &Config{ ListenerPort: 9090, SecuredListenerHost: "0.0.0.0", SecuredListenerPort: 9095, + NewListenerHost: "0.0.0.0", + NewListenerPort: 9096, ClusterTimeoutInSeconds: 20, EnforcerResponseTimeoutInSeconds: 20, MaximumResourcePathLengthInKB: -1, @@ -160,6 +162,11 @@ var defaultConfig = &Config{ SSLCertSANHostname: "", }, }, + Varnish: varnish{ + Enabled: true, + Host: "varnish", + Port: 80, + }, Enforcer: enforcer{ Management: management{ Username: "admin", diff --git a/adapter/config/types.go b/adapter/config/types.go index 8d5a93ef72..8059e0c50d 100644 --- a/adapter/config/types.go +++ b/adapter/config/types.go @@ -80,6 +80,7 @@ type Config struct { GlobalAdapter globalAdapter `toml:"globalAdapter"` Analytics analytics `toml:"analytics"` Tracing tracing + Varnish varnish } // Adapter related Configurations @@ -112,6 +113,8 @@ type envoy struct { ListenerPort uint32 SecuredListenerHost string SecuredListenerPort uint32 + NewListenerHost string + NewListenerPort uint32 ClusterTimeoutInSeconds time.Duration EnforcerResponseTimeoutInSeconds time.Duration `default:"20"` KeyStore keystore @@ -148,6 +151,12 @@ type rateLimit struct { SSLCertSANHostname string } +type varnish struct{ + Enabled bool + Host string + Port uint32 +} + type xRateLimitHeaders struct { Enabled bool RFCVersion string diff --git a/adapter/internal/oasparser/config_generator.go b/adapter/internal/oasparser/config_generator.go index ec6d4b258b..fab76d436b 100644 --- a/adapter/internal/oasparser/config_generator.go +++ b/adapter/internal/oasparser/config_generator.go @@ -65,6 +65,16 @@ func GetGlobalClusters() ([]*clusterv3.Cluster, []*corev3.Address) { } } + if conf.Varnish.Enabled { + varnishCluster, varnishEP, errVarnish := envoy.CreateVarnishCluster() + if errVarnish == nil { + clusters = append(clusters, varnishCluster) + endpoints = append(endpoints, varnishEP...) + } else { + logger.LoggerOasparser.Fatalf("Failed to initialize Varnish cluster. Hence terminating the adapter. Error: %s", errVarnish) + } + } + if conf.Tracing.Enabled && conf.Tracing.Type != envoyconf.TracerTypeAzure { logger.LoggerOasparser.Debugln("Creating global cluster - Tracing") if c, e, err := envoyconf.CreateTracingCluster(conf); err == nil { diff --git a/adapter/internal/oasparser/envoyconf/constants.go b/adapter/internal/oasparser/envoyconf/constants.go index 751493a462..ca89af7063 100644 --- a/adapter/internal/oasparser/envoyconf/constants.go +++ b/adapter/internal/oasparser/envoyconf/constants.go @@ -23,6 +23,7 @@ const ( tracingClusterName string = "wso2_cc_trace" extAuthzHTTPCluster string = "ext_authz_http_cluster" rateLimitClusterName string = "rate-limit" + varnishClusterName string = "varnish" ) const ( @@ -58,6 +59,7 @@ const ( defaultRdsConfigName string = "default" defaultHTTPListenerName string = "HTTPListener" defaultHTTPSListenerName string = "HTTPSListener" + newHTTPListenerName string = "NewHTTPListener" defaultAccessLogPath string = "/tmp/envoy.access.log" defaultListenerSecretConfigName string = "DefaultListenerSecret" ) @@ -105,6 +107,7 @@ const ( ) const ( httpsURLType string = "https" + httpURLType string = "http" wssURLType string = "wss" httpMethodHeader string = ":method" ) diff --git a/adapter/internal/oasparser/envoyconf/http_filters.go b/adapter/internal/oasparser/envoyconf/http_filters.go index b6a945f87b..1cfa145135 100644 --- a/adapter/internal/oasparser/envoyconf/http_filters.go +++ b/adapter/internal/oasparser/envoyconf/http_filters.go @@ -75,6 +75,16 @@ func getHTTPFilters() []*hcmv3.HttpFilter { return httpFilters } +// get router filter in a array +func getHTTPRouterFilters() []*hcmv3.HttpFilter { + router := getRouterHTTPFilter() + + httpRouterFilters := []*hcmv3.HttpFilter{ + router, + } + return httpRouterFilters +} + // getRouterHTTPFilter gets router http filter. func getRouterHTTPFilter() *hcmv3.HttpFilter { diff --git a/adapter/internal/oasparser/envoyconf/listener.go b/adapter/internal/oasparser/envoyconf/listener.go index 751b4ccbf3..38c02cc939 100644 --- a/adapter/internal/oasparser/envoyconf/listener.go +++ b/adapter/internal/oasparser/envoyconf/listener.go @@ -36,6 +36,7 @@ import ( "github.com/wso2/product-microgateway/adapter/config" logger "github.com/wso2/product-microgateway/adapter/internal/loggers" + config_access_logv3 "github.com/envoyproxy/go-control-plane/envoy/config/accesslog/v3" ) // CreateRoutesConfigForRds generates the default RouteConfiguration. @@ -78,56 +79,14 @@ func CreateListenersWithRds() []*listenerv3.Listener { func createListeners(conf *config.Config) []*listenerv3.Listener { httpFilters := getHTTPFilters() upgradeFilters := getUpgradeFilters() + router := getHTTPRouterFilters() accessLogs := getAccessLogs() var filters []*listenerv3.Filter + var customFilters []*listenerv3.Filter var listeners []*listenerv3.Listener - manager := &hcmv3.HttpConnectionManager{ - CodecType: hcmv3.HttpConnectionManager_AUTO, - StatPrefix: httpConManagerStartPrefix, - // WebSocket upgrades enabled from the HCM - UpgradeConfigs: []*hcmv3.HttpConnectionManager_UpgradeConfig{{ - UpgradeType: "websocket", - Enabled: &wrappers.BoolValue{Value: true}, - Filters: upgradeFilters, - }}, - RouteSpecifier: &hcmv3.HttpConnectionManager_Rds{ - Rds: &hcmv3.Rds{ - RouteConfigName: defaultRdsConfigName, - ConfigSource: &corev3.ConfigSource{ - ConfigSourceSpecifier: &corev3.ConfigSource_Ads{ - Ads: &corev3.AggregatedConfigSource{}, - }, - ResourceApiVersion: corev3.ApiVersion_V3, - }, - }, - }, - HttpFilters: httpFilters, - LocalReplyConfig: &hcmv3.LocalReplyConfig{ - Mappers: getErrorResponseMappers(), - }, - RequestTimeout: ptypes.DurationProto(conf.Envoy.Connection.Timeouts.RequestTimeoutInSeconds * time.Second), // default disabled - RequestHeadersTimeout: ptypes.DurationProto(conf.Envoy.Connection.Timeouts.RequestHeadersTimeoutInSeconds * time.Second), // default disabled - StreamIdleTimeout: ptypes.DurationProto(conf.Envoy.Connection.Timeouts.StreamIdleTimeoutInSeconds * time.Second), // Default 5 mins - CommonHttpProtocolOptions: &corev3.HttpProtocolOptions{ - IdleTimeout: ptypes.DurationProto(conf.Envoy.Connection.Timeouts.IdleTimeoutInSeconds * time.Second), // Default 1 hr - }, - } - - if len(accessLogs) > 0 { - manager.AccessLog = accessLogs - } - - if conf.Tracing.Enabled && conf.Tracing.Type != TracerTypeAzure { - if tracing, err := getTracing(conf); err == nil { - manager.Tracing = tracing - manager.GenerateRequestId = &wrappers.BoolValue{Value: conf.Tracing.Enabled} - } else { - logger.LoggerOasparser.Error("Failed to initialize tracing. Router tracing will be disabled. ", err) - conf.Tracing.Enabled = false - } - } - + //creating the manager for exisitng listener + manager := createHTTPConnectionManager(httpFilters, upgradeFilters, accessLogs, conf) pbst, err := anypb.New(manager) if err != nil { logger.LoggerOasparser.Fatal(err) @@ -138,10 +97,25 @@ func createListeners(conf *config.Config) []*listenerv3.Listener { TypedConfig: pbst, }, } - // add filters filters = append(filters, &connectionManagerFilterP) + // Creating the new manager for new listener + newManager := createHTTPConnectionManager(router, router, accessLogs, conf) + pbstNewManager, err := anypb.New(newManager) + if err != nil { + logger.LoggerOasparser.Fatal(err) + } + connectionManagerFilterNew := listenerv3.Filter{ + Name: wellknown.HTTPConnectionManager, + ConfigType: &listenerv3.Filter_TypedConfig{ + TypedConfig: pbstNewManager, + }, + } + // add new filters + customFilters = append(customFilters, &connectionManagerFilterNew) + + if conf.Envoy.SecuredListenerPort > 0 { listenerHostAddress := defaultListenerHostAddress if len(conf.Envoy.SecuredListenerHost) > 0 { @@ -228,6 +202,39 @@ func createListeners(conf *config.Config) []*listenerv3.Listener { logger.LoggerOasparser.Info("No Non-securedListenerPort is included.") } + // new listener + if conf.Envoy.NewListenerPort > 0 { + listenerHostAddress := defaultListenerHostAddress + if len(conf.Envoy.NewListenerHost) > 0 { + listenerHostAddress = conf.Envoy.NewListenerHost + } + listenerAddress := &corev3.Address_SocketAddress{ + SocketAddress: &corev3.SocketAddress{ + Protocol: corev3.SocketAddress_TCP, + Address: listenerHostAddress, + PortSpecifier: &corev3.SocketAddress_PortValue{ + PortValue: conf.Envoy.NewListenerPort, + }, + }, + } + + listener := listenerv3.Listener{ + Name: newHTTPListenerName, + Address: &corev3.Address{ + Address: listenerAddress, + }, + FilterChains: []*listenerv3.FilterChain{{ + Filters: customFilters, + }, + }, + } + listeners = append(listeners, &listener) + logger.LoggerOasparser.Infof("New Listener is added. %s : %d", listenerHostAddress, conf.Envoy.NewListenerPort) + } else { + logger.LoggerOasparser.Info("No NewListenerPort is included.") + } + + if len(listeners) == 0 { err := errors.New("No Listeners are configured as no port value is mentioned under securedListenerPort or ListenerPort") logger.LoggerOasparser.Fatal(err) @@ -319,3 +326,56 @@ func getTracing(conf *config.Config) (*hcmv3.HttpConnectionManager_Tracing, erro return tracing, nil } + +// function to create http managers +func createHTTPConnectionManager(httpFilters []*hcmv3.HttpFilter, upgradeFilters []*hcmv3.HttpFilter, accessLogs []*config_access_logv3.AccessLog, conf *config.Config) *hcmv3.HttpConnectionManager { + manager := &hcmv3.HttpConnectionManager{ + CodecType: hcmv3.HttpConnectionManager_AUTO, + StatPrefix: httpConManagerStartPrefix, + // WebSocket upgrades enabled from the HCM + UpgradeConfigs: []*hcmv3.HttpConnectionManager_UpgradeConfig{{ + UpgradeType: "websocket", + Enabled: &wrappers.BoolValue{Value: true}, + Filters: upgradeFilters, + }}, + RouteSpecifier: &hcmv3.HttpConnectionManager_Rds{ + Rds: &hcmv3.Rds{ + RouteConfigName: defaultRdsConfigName, + ConfigSource: &corev3.ConfigSource{ + ConfigSourceSpecifier: &corev3.ConfigSource_Ads{ + Ads: &corev3.AggregatedConfigSource{}, + }, + ResourceApiVersion: corev3.ApiVersion_V3, + }, + }, + }, + HttpFilters: httpFilters, + LocalReplyConfig: &hcmv3.LocalReplyConfig{ + Mappers: getErrorResponseMappers(), + }, + RequestTimeout: ptypes.DurationProto(conf.Envoy.Connection.Timeouts.RequestTimeoutInSeconds * time.Second), // default disabled + RequestHeadersTimeout: ptypes.DurationProto(conf.Envoy.Connection.Timeouts.RequestHeadersTimeoutInSeconds * time.Second), // default disabled + StreamIdleTimeout: ptypes.DurationProto(conf.Envoy.Connection.Timeouts.StreamIdleTimeoutInSeconds * time.Second), // Default 5 mins + CommonHttpProtocolOptions: &corev3.HttpProtocolOptions{ + IdleTimeout: ptypes.DurationProto(conf.Envoy.Connection.Timeouts.IdleTimeoutInSeconds * time.Second), // Default 1 hr + }, + + } + + + if len(accessLogs) > 0 { + manager.AccessLog = accessLogs + } + + if conf.Tracing.Enabled && conf.Tracing.Type != TracerTypeAzure { + if tracing, err := getTracing(conf); err == nil { + manager.Tracing = tracing + manager.GenerateRequestId = &wrappers.BoolValue{Value: conf.Tracing.Enabled} + } else { + logger.LoggerOasparser.Error("Failed to initialize tracing. Router tracing will be disabled. ", err) + conf.Tracing.Enabled = false + } + } + + return manager +} diff --git a/adapter/internal/oasparser/envoyconf/listener_test.go b/adapter/internal/oasparser/envoyconf/listener_test.go index bccc0920f5..493c57e131 100644 --- a/adapter/internal/oasparser/envoyconf/listener_test.go +++ b/adapter/internal/oasparser/envoyconf/listener_test.go @@ -31,7 +31,7 @@ func TestCreateListenerWithRds(t *testing.T) { // TODO: (Vajira) Add more test scenarios listeners := CreateListenersWithRds() assert.NotEmpty(t, listeners, "Listeners creation has been failed") - assert.Equal(t, 2, len(listeners), "Two listeners are not created.") + assert.Equal(t, 3, len(listeners), "Two listeners are not created.") securedListener := listeners[0] if securedListener.Validate() != nil { diff --git a/adapter/internal/oasparser/envoyconf/routes_with_clusters.go b/adapter/internal/oasparser/envoyconf/routes_with_clusters.go index 9355023d76..664fcc1490 100644 --- a/adapter/internal/oasparser/envoyconf/routes_with_clusters.go +++ b/adapter/internal/oasparser/envoyconf/routes_with_clusters.go @@ -446,6 +446,29 @@ func CreateRateLimitCluster() (*clusterv3.Cluster, []*corev3.Address, error) { return cluster, address, nil } +// CreateVarnishCluster creates cluster relevant to the varnish +func CreateVarnishCluster() (*clusterv3.Cluster, []*corev3.Address, error) { + + conf, _ := config.ReadConfigs() + + varnishCluster := &model.EndpointCluster{ + Endpoints: []model.Endpoint{ + { + Host: conf.Varnish.Host, + URLType: httpURLType, + Port: conf.Varnish.Port, + }, + }, + } + + cluster, address, varnishErr := processEndpoints(varnishClusterName, varnishCluster, nil, 20, "") + if varnishErr != nil { + return nil, nil, varnishErr + } + + return cluster, address, nil +} + // CreateTracingCluster creates a cluster definition for router's tracing server. func CreateTracingCluster(conf *config.Config) (*clusterv3.Cluster, []*corev3.Address, error) { var epHost string diff --git a/enforcer-parent/commons/src/main/java/org/wso2/choreo/connect/enforcer/commons/model/ResourceConfig.java b/enforcer-parent/commons/src/main/java/org/wso2/choreo/connect/enforcer/commons/model/ResourceConfig.java index e1fb684a1a..ca7c04e8e1 100644 --- a/enforcer-parent/commons/src/main/java/org/wso2/choreo/connect/enforcer/commons/model/ResourceConfig.java +++ b/enforcer-parent/commons/src/main/java/org/wso2/choreo/connect/enforcer/commons/model/ResourceConfig.java @@ -31,6 +31,7 @@ public class ResourceConfig { private Map> securitySchemas = new HashMap(); // security_schema_name -> scopes private String tier = "Unlimited"; private boolean disableSecurity = false; + public boolean responseCache = false; private Map endpoints; // "PRODUCTION" OR "SANDBOX" -> endpoint cluster /** @@ -128,5 +129,10 @@ public void setEndpoints(Map endpoints) { this.endpoints = endpoints; } + // check for the response caching enabled apis + public boolean isResponseCache() { + return responseCache; + } + } diff --git a/enforcer-parent/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/constants/APIConstants.java b/enforcer-parent/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/constants/APIConstants.java index e3fee1f08e..18c239c8c8 100644 --- a/enforcer-parent/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/constants/APIConstants.java +++ b/enforcer-parent/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/constants/APIConstants.java @@ -35,6 +35,8 @@ public class APIConstants { public static final String GATEWAY_PUBLIC_CERTIFICATE_ALIAS = "gateway_certificate_alias"; public static final String WSO2_PUBLIC_CERTIFICATE_ALIAS = "wso2carbon"; public static final String HTTPS_PROTOCOL = "https"; + public static final String HTTP_GET_METHOD = "GET"; + public static final String HTTP_HEAD_METHOD = "HEAD"; public static final String SUPER_TENANT_DOMAIN_NAME = "carbon.super"; public static final String BANDWIDTH_TYPE = "bandwidthVolume"; public static final String AUTHORIZATION_HEADER_DEFAULT = "Authorization"; diff --git a/enforcer-parent/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/constants/AdapterConstants.java b/enforcer-parent/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/constants/AdapterConstants.java index 154ee44c94..bbc5d1c9d7 100644 --- a/enforcer-parent/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/constants/AdapterConstants.java +++ b/enforcer-parent/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/constants/AdapterConstants.java @@ -25,6 +25,12 @@ public class AdapterConstants { // The header which should be populated to set the upstream cluster public static final String CLUSTER_HEADER = "x-wso2-cluster-header"; + // The header which should be populated to set the upstream cluster when response caching is enabled + public static final String ACTUAL_CLUSTER_HEADER = "x-wso2-actual-cluster"; + // The header which should be populated to set the actual request path when response caching is enabled + public static final String PATH_HEADER = "x-wso2-request-path"; + // The header which should be populated to set the upstream cluster when response caching is enabled + public static final String ACTUAL_HOST_HEADER = "x-wso2-actual-host"; // The key which specifies the production cluster name inside the request context public static final String PROD_CLUSTER_HEADER_KEY = "prodClusterName"; // The key which specifies the sandbox cluster name inside the request context diff --git a/enforcer-parent/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/server/HttpRequestHandler.java b/enforcer-parent/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/server/HttpRequestHandler.java index 1b960f3b3e..4ba6185e11 100644 --- a/enforcer-parent/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/server/HttpRequestHandler.java +++ b/enforcer-parent/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/server/HttpRequestHandler.java @@ -55,6 +55,17 @@ public ResponseObject process(CheckRequest request) { RequestContext requestContext = buildRequestContext(matchedAPI, request); ResponseObject responseObject = matchedAPI.process(requestContext); + + // to only go insside GET,HEAD otherwise OPTION calls will be failed + if (APIConstants.HTTP_GET_METHOD.equals(requestContext.getRequestMethod()) || + APIConstants.HTTP_HEAD_METHOD.equals(requestContext.getRequestMethod())) { + // Check if response cache is enabled + if (requestContext.getMatchedResourcePath().isResponseCache()) { + // Add, change headers for caching enabled requests + addCustomHeadersForCaching(responseObject, requestContext); + } + } + responseObject.setExtAuthDetails(requestContext.getExtAuthDetails()); return responseObject; } @@ -84,4 +95,16 @@ private RequestContext buildRequestContext(API api, CheckRequest request) { .prodClusterHeader(prodCluster).sandClusterHeader(sandCluster).requestTimeStamp(requestTimeInMillis) .pathTemplate(pathTemplate).build(); } + + private void addCustomHeadersForCaching(ResponseObject responseObject, RequestContext requestContext) { + + String prodClusterHeaderValue = requestContext.getProdClusterHeader(); + String actualHostHeaderValue = requestContext.getHeaders().get(":authority"); + + responseObject.getHeaderMap().put(AdapterConstants.ACTUAL_CLUSTER_HEADER, prodClusterHeaderValue); + responseObject.getHeaderMap().put(AdapterConstants.PATH_HEADER, requestContext.getRequestPath()); + responseObject.getHeaderMap().put(AdapterConstants.CLUSTER_HEADER, "varnish"); + responseObject.getHeaderMap().put(AdapterConstants.ACTUAL_HOST_HEADER, actualHostHeaderValue); + + } } diff --git a/resources/conf/default.vcl b/resources/conf/default.vcl new file mode 100644 index 0000000000..22bc83106f --- /dev/null +++ b/resources/conf/default.vcl @@ -0,0 +1,103 @@ +# vcl config for the varnish container used in response-caching feature + +vcl 4.1; +import std; + +backend default { + .host = "router"; + .port = "9096"; +} + +# define who is authorize to purge/invalidate the cache +acl purge { + "localhost"; + "router"; + "varnish"; + "192.168.0.1"; +} + +sub vcl_recv { + + #liveness and rediness + if (req.url == "/varnish-ping") { + return(synth(200)); + } + + if (req.url == "/varnish-ready") { + return(synth(200)); + } + + if (req.method == "PURGE") { + if (!client.ip ~ purge) { + return(synth(405)); + } + if(!req.http.x-invalidate-pattern) { + return(purge); + } + ban("req.url ~ " + req.http.x-invalidate-pattern + + " && req.http.host == " + req.http.host); + return (synth(200,"Ban added")); + } + + set req.http.Host = req.http.x-wso2-actual-host; + set req.url = req.http.x-wso2-request-path; + set req.url = std.querysort(req.url); + +} + +sub vcl_miss { + set req.http.x-wso2-cluster-header = req.http.x-wso2-actual-cluster; + return (fetch); +} + +sub vcl_backend_fetch { + if (bereq.http.x-wso2-request-path) { + set bereq.url = bereq.http.x-wso2-request-path; + set bereq.url = std.querysort(bereq.url); + unset bereq.http.x-wso2-request-path; + unset bereq.http.x-wso2-actual-cluster; + } +} + +sub vcl_backend_response { + # List of status codes that should be marked as uncacheable + if (beresp.status == 300 || beresp.status == 301 || beresp.status == 302 || + beresp.status == 307 || beresp.status == 304 || beresp.status == 404 || + beresp.status == 410 || beresp.status == 414) { + set beresp.uncacheable = true; + } + + if (bereq.http.x-cache-default-ttl && !beresp.http.Cache-Control) { + set beresp.ttl = std.duration(bereq.http.x-cache-default-ttl + "s", 120s); + } + + # aloows to respect the must-revalidate cache-control header + if(beresp.http.Cache-Control ~ "must-revalidate") { + set beresp.grace = 0s; + } + + # Determine the appropriate storage + if (bereq.http.x-cache-partition == "slot1") { + set beresp.storage = storage.slot1; + } else if (bereq.http.x-cache-partition == "slot2") { + set beresp.storage = storage.slot2; + } else { + set beresp.storage = storage.default; + } + + if (beresp.storage == storage.default) { + set beresp.http.x-storage = "default"; + } else{ + set beresp.http.x-storage = bereq.http.x-cache-partition; + } +} + +sub vcl_deliver { + if (obj.hits > 0) { + set resp.http.X-Cached-By = "Varnish"; + set resp.http.X-Cache-Info = "Cached under host: " + req.http.Host + "; Request URI: " + req.url; + } +} + + + diff --git a/resources/docker-compose/response-caching-feature/docker-compose.yaml b/resources/docker-compose/response-caching-feature/docker-compose.yaml new file mode 100644 index 0000000000..08d058c7bd --- /dev/null +++ b/resources/docker-compose/response-caching-feature/docker-compose.yaml @@ -0,0 +1,31 @@ +version: '3.8' + +services: + varnish: + image: varnish:stable + ports: + - "8080:80" + networks: + - varnish_network + deploy: + resources: + limits: + cpus: "0.5" + memory: "500M" + reservations: + cpus: "0.5" + memory: "500M" + command: > + varnishd -F + -f /etc/varnish/default.vcl + -s malloc,1G + -s malloc,1G + -s malloc,1G + # find the vcl file under the resources/conf/default.vcl + volumes: + - ./varnish/default.vcl:/etc/varnish/default.vcl + +# connect to the same network where adapter, enforcer.. running +networks: + varnish_network: + external: true