Skip to content

Commit

Permalink
Merge pull request #12 from uselagoon/active-standby-route
Browse files Browse the repository at this point in the history
Also lint active/standby routes
  • Loading branch information
smlx authored Nov 1, 2021
2 parents 56b4468 + e59a85e commit c548265
Show file tree
Hide file tree
Showing 7 changed files with 163 additions and 36 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ Currently implemented linters.

| Name | Description |
| --- | --- |
| RouteAnnotation | Validates the `server-snippets` Lagoon route / kubernetes Ingress annotation against an allow-list. |
| RouteAnnotation | Validates Lagoon Route / Kubernetes Ingress annotations. |
9 changes: 8 additions & 1 deletion internal/lagoonyml/lagoon.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,14 @@ type Environment struct {
Routes []map[string][]LagoonRoute `json:"routes"`
}

// ProductionRoutes represents an active/standby configuration.
type ProductionRoutes struct {
Active *Environment `json:"active"`
Standby *Environment `json:"standby"`
}

// Lagoon represents the .lagoon.yml file.
type Lagoon struct {
Environments map[string]Environment `json:"environments"`
Environments map[string]Environment `json:"environments"`
ProductionRoutes *ProductionRoutes `json:"production_routes"`
}
12 changes: 12 additions & 0 deletions internal/lagoonyml/lint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ func TestLint(t *testing.T) {
input: "testdata/valid.5.lagoon.yml",
valid: true,
},
"standby route valid annotation": {
input: "testdata/valid.6.lagoon.yml",
valid: true,
},
"invalid.0.lagoon.yml": {
input: "testdata/invalid.0.lagoon.yml",
valid: false,
Expand All @@ -43,6 +47,14 @@ func TestLint(t *testing.T) {
input: "testdata/invalid.1.lagoon.yml",
valid: false,
},
"standby route invalid annotation": {
input: "testdata/invalid.2.lagoon.yml",
valid: false,
},
"active route invalid annotation": {
input: "testdata/invalid.3.lagoon.yml",
valid: false,
},
}
for name, tc := range testCases {
t.Run(name, func(tt *testing.T) {
Expand Down
89 changes: 55 additions & 34 deletions internal/lagoonyml/routeannotation.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,44 +37,65 @@ func validate(annotations map[string]string, r *regexp.Regexp,
return "", true
}

// validateEnvironment returns an error if the annotations on the environment
// are invalid, and nil otherwise.
func validateEnvironment(e *Environment) error {
for _, routeMap := range e.Routes {
for rName, lagoonRoutes := range routeMap {
for _, lagoonRoute := range lagoonRoutes {
for iName, ingress := range lagoonRoute.Ingresses {
// auth-snippet
if _, ok := ingress.Annotations[authSnippet]; ok {
return fmt.Errorf(
"invalid %s annotation on route %s, ingress %s: %s",
authSnippet, rName, iName,
"this annotation is restricted")
}
// configuration-snippet
if annotation, ok := validate(ingress.Annotations, validSnippets,
configurationSnippet); !ok {
return fmt.Errorf(
"invalid %s annotation on route %s, ingress %s: %s",
configurationSnippet, rName, iName, annotation)
}
// modsecurity-snippet
if _, ok := ingress.Annotations[modsecuritySnippet]; ok {
return fmt.Errorf(
"invalid %s annotation on route %s, ingress %s: %s",
modsecuritySnippet, rName, iName,
"this annotation is restricted")
}
// server-snippet
if annotation, ok := validate(ingress.Annotations, validSnippets,
serverSnippet); !ok {
return fmt.Errorf(
"invalid %s annotation on route %s, ingress %s: %s",
serverSnippet, rName, iName, annotation)
}
}
}
}
}
return nil
}

// RouteAnnotation checks for valid annotations on defined routes.
func RouteAnnotation() Linter {
return func(l *Lagoon) error {
for eName, e := range l.Environments {
for _, routeMap := range e.Routes {
for rName, lagoonRoutes := range routeMap {
for _, lagoonRoute := range lagoonRoutes {
for iName, ingress := range lagoonRoute.Ingresses {
// auth-snippet
if _, ok := ingress.Annotations[authSnippet]; ok {
return fmt.Errorf(
"invalid %s annotation on environment %s, route %s, ingress %s: %s",
authSnippet, eName, rName, iName,
"this annotation is restricted")
}
// configuration-snippet
if annotation, ok := validate(ingress.Annotations, validSnippets,
configurationSnippet); !ok {
return fmt.Errorf(
"invalid %s annotation on environment %s, route %s, ingress %s: %s",
configurationSnippet, eName, rName, iName, annotation)
}
// modsecurity-snippet
if _, ok := ingress.Annotations[modsecuritySnippet]; ok {
return fmt.Errorf(
"invalid %s annotation on environment %s, route %s, ingress %s: %s",
modsecuritySnippet, eName, rName, iName,
"this annotation is restricted")
}
// server-snippet
if annotation, ok := validate(ingress.Annotations, validSnippets,
serverSnippet); !ok {
return fmt.Errorf(
"invalid %s annotation on environment %s, route %s, ingress %s: %s",
serverSnippet, eName, rName, iName, annotation)
}
}
}
if err := validateEnvironment(&e); err != nil {
return fmt.Errorf("environment %s: %v", eName, err)
}
}
if l.ProductionRoutes != nil {
if l.ProductionRoutes.Active != nil {
if err := validateEnvironment(l.ProductionRoutes.Active); err != nil {
return fmt.Errorf("active environment: %v", err)
}
}
if l.ProductionRoutes.Standby != nil {
if err := validateEnvironment(l.ProductionRoutes.Standby); err != nil {
return fmt.Errorf("standby environment: %v", err)
}
}
}
Expand Down
31 changes: 31 additions & 0 deletions internal/lagoonyml/testdata/invalid.2.lagoon.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
production_routes:
active:
routes:
- nginx:
- "www.example.com":
tls-acme: true
insecure: Redirect
- "de.example.com":
tls-acme: "true"
insecure: Redirect

standby:
routes:
- nginx:
- "www.standby.example.com":
tls-acme: "false"
insecure: Redirect
- "de.standby.example.com":
tls-acme: "false"
insecure: Redirect
annotations:
nginx.ingress.kubernetes.io/server-snippet: |
set $agentflag 0;
if ($http_user_agent ~* "(Mobile)" ){
set $agentflag 1;
}
if ( $agentflag = 1 ) {
return 301 https://m.example.com;
}
31 changes: 31 additions & 0 deletions internal/lagoonyml/testdata/invalid.3.lagoon.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
production_routes:
active:
routes:
- nginx:
- "www.example.com":
tls-acme: true
insecure: Redirect
- "de.example.com":
tls-acme: "true"
insecure: Redirect
annotations:
nginx.ingress.kubernetes.io/server-snippet: |
set $agentflag 0;
if ($http_user_agent ~* "(Mobile)" ){
set $agentflag 1;
}
if ( $agentflag = 1 ) {
return 301 https://m.example.com;
}
standby:
routes:
- nginx:
- "www.standby.example.com":
tls-acme: "false"
insecure: Redirect
- "de.standby.example.com":
tls-acme: "false"
insecure: Redirect
25 changes: 25 additions & 0 deletions internal/lagoonyml/testdata/valid.6.lagoon.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
production_routes:
active:
routes:
- nginx:
- "www.example.com":
tls-acme: true
insecure: Redirect
- "de.example.com":
tls-acme: "true"
insecure: Redirect

standby:
routes:
- nginx:
- "www.standby.example.com":
tls-acme: "false"
insecure: Redirect
- "de.standby.example.com":
tls-acme: "false"
insecure: Redirect
annotations:
nginx.ingress.kubernetes.io/server-snippet: |
set_real_ip_from 1.2.3.4/32;
add_header Content-type text/plain;

0 comments on commit c548265

Please sign in to comment.