Skip to content

Commit b1cd451

Browse files
authored
Feat/custom log file (#9)
* remove healthcheck for bad configure ingress with wildcard * feat: add a way to configure log path
1 parent 8d8aa2d commit b1cd451

8 files changed

Lines changed: 63 additions & 32 deletions

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ Yggdrasil can be configured using a config file e.g:
139139
{
140140
"nodeName": "foo",
141141
"ingressClasses": ["multi-cluster", "multi-cluster-staging"],
142+
"accessLog": "/var/log/envoy/",
142143
"certificates": [
143144
{
144145
"hosts": ["*.api.com"],
@@ -187,6 +188,7 @@ The Yggdrasil-specific metrics which are available from the API are:
187188
--cert string certfile
188189
--config string config file
189190
--debug Log at debug level
191+
--access-log path for the file logs
190192
--envoy-listener-ipv4-address strings IPv4 addresses by the envoy proxy to accept incoming connections (default "0.0.0.0")
191193
--envoy-port uint32 port by the envoy proxy to accept incoming connections (default 10000)
192194
--health-address string yggdrasil health API listen address (default "0.0.0.0:8081")

cmd/root.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ type config struct {
3434
NodeName string `json:"nodeName"`
3535
Clusters []clusterConfig `json:"clusters"`
3636
SyncSecrets bool `json:"syncSecrets"`
37+
AccessLog string `json:"accessLog"`
3738
Certificates []envoy.Certificate `json:"certificates"`
3839
TrustCA string `json:"trustCA"`
3940
UpstreamPort uint32 `json:"upstreamPort"`
@@ -78,6 +79,7 @@ func init() {
7879
rootCmd.PersistentFlags().String("address", "0.0.0.0:8080", "yggdrasil envoy control plane listen address")
7980
rootCmd.PersistentFlags().String("health-address", "0.0.0.0:8081", "yggdrasil health API listen address")
8081
rootCmd.PersistentFlags().String("node-name", "", "envoy node name")
82+
rootCmd.PersistentFlags().String("access-log", "/var/log/envoy/", "envoy default access log file")
8183
rootCmd.PersistentFlags().String("cert", "", "certfile")
8284
rootCmd.PersistentFlags().String("key", "", "keyfile")
8385
rootCmd.PersistentFlags().String("ca", "", "trustedCA")
@@ -113,6 +115,7 @@ func init() {
113115
viper.BindPFlag("address", rootCmd.PersistentFlags().Lookup("address"))
114116
viper.BindPFlag("healthAddress", rootCmd.PersistentFlags().Lookup("health-address"))
115117
viper.BindPFlag("nodeName", rootCmd.PersistentFlags().Lookup("node-name"))
118+
viper.BindPFlag("accessLog", rootCmd.PersistentFlags().Lookup("access-log"))
116119
viper.BindPFlag("ingressClasses", rootCmd.PersistentFlags().Lookup("ingress-classes"))
117120
viper.BindPFlag("cert", rootCmd.PersistentFlags().Lookup("cert"))
118121
viper.BindPFlag("key", rootCmd.PersistentFlags().Lookup("key"))
@@ -232,6 +235,7 @@ func main(*cobra.Command, []string) error {
232235
c.Certificates,
233236
viper.GetString("trustCA"),
234237
viper.GetStringSlice("ingressClasses"),
238+
viper.GetString("accessLog"),
235239
envoy.WithUpstreamPort(uint32(viper.GetInt32("upstreamPort"))),
236240
envoy.WithEnvoyListenerIpv4Address(viper.GetStringSlice("envoyListenerIpv4Address")),
237241
envoy.WithEnvoyPort(uint32(viper.GetInt32("envoyPort"))),
@@ -246,6 +250,7 @@ func main(*cobra.Command, []string) error {
246250
envoy.WithDefaultRetryOn(viper.GetString("retryOn")),
247251
envoy.WithAlpnProtocols(viper.GetStringSlice("alpnProtocols")),
248252
)
253+
configurator.ValidateAndFormatPath()
249254
snapshotter := envoy.NewSnapshotter(envoyCache, configurator, aggregator)
250255

251256
go snapshotter.Run(aggregator)

pkg/envoy/boilerplate.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package envoy
33
import (
44
"fmt"
55
"log"
6+
"path/filepath"
67
"strings"
78

89
cal "github.com/envoyproxy/go-control-plane/envoy/config/accesslog/v3"
@@ -175,10 +176,10 @@ func makeGrpcLoggerConfig(cfg HttpGrpcLogger) *gal.HttpGrpcAccessLogConfig {
175176
}
176177
}
177178

178-
func (c *KubernetesConfigurator) makeConnectionManager(virtualHosts []*route.VirtualHost) *hcm.HttpConnectionManager {
179+
func (c *KubernetesConfigurator) makeConnectionManager(virtualHosts []*route.VirtualHost, accessLog string) *hcm.HttpConnectionManager {
179180
// Access Logs
180181
accessLogConfig := &eal.FileAccessLog{
181-
Path: "/var/log/envoy/access.log",
182+
Path: filepath.Join(accessLog, "access.log"),
182183
AccessLogFormat: &eal.FileAccessLog_LogFormat{
183184
LogFormat: &core.SubstitutionFormatString{
184185
Format: &core.SubstitutionFormatString_JsonFormat{
@@ -257,8 +258,8 @@ func (c *KubernetesConfigurator) makeConnectionManager(virtualHosts []*route.Vir
257258
}
258259
}
259260

260-
func (c *KubernetesConfigurator) makeFilterChain(certificate Certificate, virtualHosts []*route.VirtualHost) (listener.FilterChain, error) {
261-
httpConnectionManager := c.makeConnectionManager(virtualHosts)
261+
func (c *KubernetesConfigurator) makeFilterChain(certificate Certificate, virtualHosts []*route.VirtualHost, accessLog string) (listener.FilterChain, error) {
262+
httpConnectionManager := c.makeConnectionManager(virtualHosts, accessLog)
262263
anyHttpConfig, err := anypb.New(httpConnectionManager)
263264
if err != nil {
264265
return listener.FilterChain{}, fmt.Errorf("failed to marshal HTTP config struct to typed struct: %s", err)

pkg/envoy/configurator.go

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package envoy
33
import (
44
"errors"
55
"log"
6+
"path/filepath"
67
"strings"
78
"sync"
89
"time"
@@ -58,6 +59,7 @@ type KubernetesConfigurator struct {
5859
ingressClasses []string
5960
nodeID string
6061
syncSecrets bool
62+
accessLog string
6163
certificates []Certificate
6264
trustCA string
6365
upstreamPort uint32
@@ -80,21 +82,40 @@ type KubernetesConfigurator struct {
8082
}
8183

8284
// NewKubernetesConfigurator returns a Kubernetes configurator given a lister and ingress class
83-
func NewKubernetesConfigurator(nodeID string, certificates []Certificate, ca string, ingressClasses []string, options ...option) *KubernetesConfigurator {
84-
c := &KubernetesConfigurator{ingressClasses: ingressClasses, nodeID: nodeID, certificates: certificates, trustCA: ca}
85+
func NewKubernetesConfigurator(nodeID string, certificates []Certificate, ca string, ingressClasses []string, accessLog string, options ...option) *KubernetesConfigurator {
86+
c := &KubernetesConfigurator{ingressClasses: ingressClasses, nodeID: nodeID, certificates: certificates, trustCA: ca, accessLog: accessLog}
8587
for _, opt := range options {
8688
opt(c)
8789
}
8890
return c
8991
}
9092

93+
func (c *KubernetesConfigurator) ValidateAndFormatPath() {
94+
if c.accessLog == "" {
95+
logrus.Fatal("accessLog path cannot be empty")
96+
}
97+
98+
// Clean the path and make it absolute
99+
c.accessLog = filepath.Clean(c.accessLog)
100+
absolutePath, err := filepath.Abs(c.accessLog)
101+
if err != nil {
102+
logrus.Fatalf("invalid path: %v", err)
103+
}
104+
c.accessLog = absolutePath
105+
106+
// Ensure the path ends with a directory separator if it's a directory
107+
if strings.HasSuffix(c.accessLog, string(filepath.Separator)) {
108+
c.accessLog = string(filepath.Separator)
109+
}
110+
}
111+
91112
// Generate creates a new snapshot
92113
func (c *KubernetesConfigurator) Generate(ingresses []*k8s.Ingress, secrets []*v1.Secret) cache.Snapshot {
93114
c.Lock()
94115
defer c.Unlock()
95116

96117
validIngresses := validIngressFilter(classFilter(ingresses, c.ingressClasses))
97-
config := translateIngresses(validIngresses, c.syncSecrets, secrets, c.defaultTimeouts)
118+
config := translateIngresses(validIngresses, c.syncSecrets, secrets, c.defaultTimeouts, c.accessLog)
98119

99120
vmatch, cmatch := config.equals(c.previousConfig)
100121

@@ -195,7 +216,7 @@ func (c *KubernetesConfigurator) generateDynamicTLSFilterChains(config *envoyCon
195216
Cert: virtualHost.TlsCert,
196217
Key: virtualHost.TlsKey,
197218
}
198-
filterChain, err := c.makeFilterChain(certificate, []*route.VirtualHost{envoyVhost})
219+
filterChain, err := c.makeFilterChain(certificate, []*route.VirtualHost{envoyVhost}, config.AccessLog)
199220
if err != nil {
200221
logrus.Warnf("error making filter chain: %v", err)
201222
}
@@ -208,7 +229,7 @@ func (c *KubernetesConfigurator) generateDynamicTLSFilterChains(config *envoyCon
208229
Cert: c.certificates[0].Cert,
209230
Key: c.certificates[0].Key,
210231
}
211-
if defaultFC, err := c.makeFilterChain(defaultCert, allVhosts); err != nil {
232+
if defaultFC, err := c.makeFilterChain(defaultCert, allVhosts, config.AccessLog); err != nil {
212233
logrus.Warnf("error making default filter chain: %v", err)
213234
} else {
214235
filterChains = append(filterChains, &defaultFC)
@@ -224,7 +245,7 @@ func (c *KubernetesConfigurator) generateHTTPFilterChain(config *envoyConfigurat
224245
virtualHosts = append(virtualHosts, makeVirtualHost(virtualHost, c.hostSelectionRetryAttempts, c.defaultRetryOn))
225246
}
226247

227-
httpConnectionManager := c.makeConnectionManager(virtualHosts)
248+
httpConnectionManager := c.makeConnectionManager(virtualHosts, config.AccessLog)
228249
httpConfig, err := util.MessageToStruct(httpConnectionManager)
229250
if err != nil {
230251
log.Fatalf("failed to convert virtualHost to envoy control plane struct: %s", err)
@@ -267,7 +288,7 @@ func (c *KubernetesConfigurator) generateTLSFilterChains(config *envoyConfigurat
267288
continue
268289
}
269290

270-
filterChain, err := c.makeFilterChain(certificate, virtualHosts)
291+
filterChain, err := c.makeFilterChain(certificate, virtualHosts, config.AccessLog)
271292
if err != nil {
272293
log.Printf("error making filter chain: %v", err)
273294
}

pkg/envoy/configurator_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ func TestGenerate(t *testing.T) {
7878

7979
configurator := NewKubernetesConfigurator("a", []Certificate{
8080
{Hosts: []string{"*"}, Cert: "b", Key: "c"},
81-
}, "d", []string{"bar"})
81+
}, "d", []string{"bar"}, "/var/log/envoy/")
8282

8383
snapshot := configurator.Generate(ingresses, []*v1.Secret{})
8484

@@ -99,7 +99,7 @@ func TestGenerateMultipleCerts(t *testing.T) {
9999
configurator := NewKubernetesConfigurator("a", []Certificate{
100100
{Hosts: []string{"*.internal.api.com"}, Cert: "com", Key: "com"},
101101
{Hosts: []string{"*.internal.api.co.uk"}, Cert: "couk", Key: "couk"},
102-
}, "d", []string{"bar"})
102+
}, "d", []string{"bar"}, "/var/log/envoy/")
103103

104104
snapshot := configurator.Generate(ingresses, []*v1.Secret{})
105105
listener := snapshot.Resources[tcache.Listener].Items["listener_0"].Resource.(*listener.Listener)
@@ -120,7 +120,7 @@ func TestGenerateMultipleHosts(t *testing.T) {
120120

121121
configurator := NewKubernetesConfigurator("a", []Certificate{
122122
{Hosts: []string{"*.internal.api.com", "*.internal.api.co.uk"}, Cert: "com", Key: "com"},
123-
}, "d", []string{"bar"})
123+
}, "d", []string{"bar"}, "/var/log/envoy/")
124124

125125
snapshot := configurator.Generate(ingresses, []*v1.Secret{})
126126
listener := snapshot.Resources[tcache.Listener].Items["listener_0"].Resource.(*listener.Listener)
@@ -141,7 +141,7 @@ func TestGenerateNoMatchingCert(t *testing.T) {
141141

142142
configurator := NewKubernetesConfigurator("a", []Certificate{
143143
{Hosts: []string{"*.internal.api.com"}, Cert: "com", Key: "com"},
144-
}, "d", []string{"bar"})
144+
}, "d", []string{"bar"}, "/var/log/envoy/")
145145

146146
snapshot := configurator.Generate(ingresses, []*v1.Secret{})
147147
listener := snapshot.Resources[tcache.Listener].Items["listener_0"].Resource.(*listener.Listener)
@@ -159,7 +159,7 @@ func TestGenerateIntoTwoCerts(t *testing.T) {
159159
configurator := NewKubernetesConfigurator("a", []Certificate{
160160
{Hosts: []string{"*.internal.api.com"}, Cert: "com", Key: "com"},
161161
{Hosts: []string{"*"}, Cert: "all", Key: "all"},
162-
}, "d", []string{"bar"})
162+
}, "d", []string{"bar"}, "/var/log/envoy/")
163163

164164
snapshot := configurator.Generate(ingresses, []*v1.Secret{})
165165
listener := snapshot.Resources[tcache.Listener].Items["listener_0"].Resource.(*listener.Listener)
@@ -228,7 +228,7 @@ func TestGenerateListeners(t *testing.T) {
228228
}
229229
for _, tc := range testcases {
230230
t.Run(tc.name, func(t *testing.T) {
231-
configurator := NewKubernetesConfigurator("a", tc.certs, "", nil)
231+
configurator := NewKubernetesConfigurator("a", tc.certs, "", nil, "/var/log/envoy/")
232232
ret := configurator.generateListeners(&envoyConfiguration{VirtualHosts: tc.virtualHost})
233233
listener := ret[0].(*listener.Listener)
234234
if len(listener.FilterChains) != 1 {

pkg/envoy/ingress_translator.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ func VirtualHostsEquals(a, b []*virtualHost) bool {
6565
type envoyConfiguration struct {
6666
VirtualHosts []*virtualHost
6767
Clusters []*cluster
68+
AccessLog string
6869
}
6970

7071
type virtualHost struct {
@@ -373,7 +374,7 @@ func validateSubdomain(ruleHost, host string) bool {
373374
return strings.HasSuffix(host, ruleHost)
374375
}
375376

376-
func translateIngresses(ingresses []*k8s.Ingress, syncSecrets bool, secrets []*v1.Secret, timeouts DefaultTimeouts) *envoyConfiguration {
377+
func translateIngresses(ingresses []*k8s.Ingress, syncSecrets bool, secrets []*v1.Secret, timeouts DefaultTimeouts, accessLog string) *envoyConfiguration {
377378
cfg := &envoyConfiguration{}
378379
envoyIngresses := map[string]*envoyIngress{}
379380

@@ -471,6 +472,7 @@ func translateIngresses(ingresses []*k8s.Ingress, syncSecrets bool, secrets []*v
471472
for _, ingress := range envoyIngresses {
472473
cfg.Clusters = append(cfg.Clusters, ingress.cluster)
473474
cfg.VirtualHosts = append(cfg.VirtualHosts, ingress.vhost)
475+
cfg.AccessLog = accessLog
474476
}
475477

476478
numVhosts.Set(float64(len(cfg.VirtualHosts)))

pkg/envoy/ingress_translator_test.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -209,8 +209,8 @@ func TestEquals(t *testing.T) {
209209
Route: 15 * time.Second,
210210
PerTry: 5 * time.Second,
211211
}
212-
c := translateIngresses([]*k8s.Ingress{ingress, ingress2}, false, []*v1.Secret{}, timeouts)
213-
c2 := translateIngresses([]*k8s.Ingress{ingress, ingress2}, false, []*v1.Secret{}, timeouts)
212+
c := translateIngresses([]*k8s.Ingress{ingress, ingress2}, false, []*v1.Secret{}, timeouts, "/var/log/envoy/")
213+
c2 := translateIngresses([]*k8s.Ingress{ingress, ingress2}, false, []*v1.Secret{}, timeouts, "/var/log/envoy/")
214214

215215
vmatch, cmatch := c.equals(c2)
216216
if vmatch != true {
@@ -231,8 +231,8 @@ func TestNotEquals(t *testing.T) {
231231
Route: 15 * time.Second,
232232
PerTry: 5 * time.Second,
233233
}
234-
c := translateIngresses([]*k8s.Ingress{ingress, ingress3, ingress2}, false, []*v1.Secret{}, timeouts)
235-
c2 := translateIngresses([]*k8s.Ingress{ingress, ingress2, ingress4}, false, []*v1.Secret{}, timeouts)
234+
c := translateIngresses([]*k8s.Ingress{ingress, ingress3, ingress2}, false, []*v1.Secret{}, timeouts, "/var/log/envoy/")
235+
c2 := translateIngresses([]*k8s.Ingress{ingress, ingress2, ingress4}, false, []*v1.Secret{}, timeouts, "/var/log/envoy/")
236236

237237
vmatch, cmatch := c.equals(c2)
238238
if vmatch == true {
@@ -252,8 +252,8 @@ func TestPartialEquals(t *testing.T) {
252252
Route: 15 * time.Second,
253253
PerTry: 5 * time.Second,
254254
}
255-
c := translateIngresses([]*k8s.Ingress{ingress2}, false, []*v1.Secret{}, timeouts)
256-
c2 := translateIngresses([]*k8s.Ingress{ingress}, false, []*v1.Secret{}, timeouts)
255+
c := translateIngresses([]*k8s.Ingress{ingress2}, false, []*v1.Secret{}, timeouts, "/var/log/envoy/")
256+
c2 := translateIngresses([]*k8s.Ingress{ingress}, false, []*v1.Secret{}, timeouts, "/var/log/envoy/")
257257

258258
vmatch, cmatch := c2.equals(c)
259259
if vmatch != true {
@@ -272,7 +272,7 @@ func TestGeneratesForSingleIngress(t *testing.T) {
272272
Route: 15 * time.Second,
273273
PerTry: 5 * time.Second,
274274
}
275-
c := translateIngresses([]*k8s.Ingress{ingress}, false, []*v1.Secret{}, timeouts)
275+
c := translateIngresses([]*k8s.Ingress{ingress}, false, []*v1.Secret{}, timeouts, "/var/log/envoy/")
276276

277277
if len(c.VirtualHosts) != 1 {
278278
t.Error("expected 1 virtual host")
@@ -313,7 +313,7 @@ func TestGeneratesForMultipleIngressSharingSpecHost(t *testing.T) {
313313
Route: 15 * time.Second,
314314
PerTry: 5 * time.Second,
315315
}
316-
c := translateIngresses([]*k8s.Ingress{fooIngress, barIngress}, false, []*v1.Secret{}, timeouts)
316+
c := translateIngresses([]*k8s.Ingress{fooIngress, barIngress}, false, []*v1.Secret{}, timeouts, "/var/log/envoy/")
317317

318318
if len(c.VirtualHosts) != 1 {
319319
t.Error("expected 1 virtual host")
@@ -373,7 +373,7 @@ func TestIngressWithIP(t *testing.T) {
373373
Route: 15 * time.Second,
374374
PerTry: 5 * time.Second,
375375
}
376-
c := translateIngresses([]*k8s.Ingress{ingress}, false, []*v1.Secret{}, timeouts)
376+
c := translateIngresses([]*k8s.Ingress{ingress}, false, []*v1.Secret{}, timeouts, "/var/log/envoy/")
377377
if c.Clusters[0].Hosts[0].Host != "127.0.0.1" {
378378
t.Errorf("expected cluster host to be IP address, was %s", c.Clusters[0].Hosts[0].Host)
379379
}

pkg/envoy/snapshotter.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,21 @@ import (
1111
"github.com/uswitch/yggdrasil/pkg/k8s"
1212
)
1313

14-
//Configurator is an interface that implements Generate and NodeID
14+
// Configurator is an interface that implements Generate and NodeID
1515
type Configurator interface {
1616
Generate([]*k8s.Ingress, []*v1.Secret) cache.Snapshot
1717
NodeID() string
1818
}
1919

20-
//Snapshotter watches for Ingress changes and updates the
21-
//config snapshot
20+
// Snapshotter watches for Ingress changes and updates the
21+
// config snapshot
2222
type Snapshotter struct {
2323
snapshotCache cache.SnapshotCache
2424
configurator Configurator
2525
aggregator *k8s.Aggregator
2626
}
2727

28-
//NewSnapshotter returns a new Snapshotter
28+
// NewSnapshotter returns a new Snapshotter
2929
func NewSnapshotter(snapshotCache cache.SnapshotCache, config Configurator, aggregator *k8s.Aggregator) *Snapshotter {
3030
return &Snapshotter{snapshotCache: snapshotCache, configurator: config, aggregator: aggregator}
3131
}
@@ -48,7 +48,7 @@ func (s *Snapshotter) snapshot() error {
4848
return nil
4949
}
5050

51-
//Run will periodically refresh the snapshot
51+
// Run will periodically refresh the snapshot
5252
func (s *Snapshotter) Run(a *k8s.Aggregator) {
5353
log.Infof("started snapshotter")
5454
hadChanges := false

0 commit comments

Comments
 (0)