Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Check production_routes, warn if environment is not an environment #9

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion internal/lagoonyml/lagoon.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,12 @@ type Environment struct {

// Lagoon represents the .lagoon.yml file.
type Lagoon struct {
Environments map[string]Environment `json:"environments"`
ProductionRoutes ProductionRoutes `json:"production_routes"`
Environments map[string]Environment `json:"environments"`
}

// ProductionRoutes represents active/standby route configurations.
type ProductionRoutes struct {
Active Environment `json:"active"`
Standby Environment `json:"standby"`
}
43 changes: 41 additions & 2 deletions internal/lagoonyml/lint.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,53 @@ type Linter func(*Lagoon) error
// `.lagoon.yml` is valid.
func Lint(path string, linters ...Linter) error {
var l Lagoon
l.Environments = make(map[string]Environment)

rawYAML, err := os.ReadFile(path)
if err != nil {
return fmt.Errorf("couldn't read %v: %v", path, err)
}
err = yaml.Unmarshal(rawYAML, &l)

// unmarshal the raw yaml into a map[string]interface{}
var li map[string]interface{}
err = yaml.Unmarshal(rawYAML, &li)
if err != nil {
return fmt.Errorf("couldn't unmarshal %v: %v", path, err)
return fmt.Errorf(".lagoon.yml configuration not valid for %v: %v", path, err)
}

// check each block for ability to be unmarshalled
for key, block := range li {
if b, ok := block.(map[string]interface{}); ok {
switch key {
case "environments":
for env, config := range b {
c, _ := yaml.Marshal(config)
var le Environment
err = yaml.Unmarshal(c, &le)
if err != nil {
fmt.Printf("Warning: .lagoon.yml configuration not valid for environment '%s': %v\n", env, err)
}
l.Environments[env] = le
}
case "production_routes":
for env, config := range b {
c, _ := yaml.Marshal(config)
var le Environment
err = yaml.Unmarshal(c, &le)
if err != nil {
fmt.Printf("Warning: .lagoon.yml configuration not valid for production_routes '%s': %v\n", env, err)
}
if env == "active" {
l.ProductionRoutes.Active = le
} else if env == "standby" {
l.ProductionRoutes.Standby = le
}
}
}
}
}

// run the linter
for _, linter := range linters {
if err := linter(&l); err != nil {
return &ErrLint{
Expand Down
4 changes: 4 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/invalid.1.lagoon.yml",
valid: false,
},
"valid.broken.0.lagoon.yml": {
input: "testdata/valid.broken.0.lagoon.yml",
valid: true,
},
}
for name, tc := range testCases {
t.Run(name, func(tt *testing.T) {
Expand Down
72 changes: 72 additions & 0 deletions internal/lagoonyml/routeannotation.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,78 @@ func RouteAnnotation() Linter {
}
}
}
for _, routeMap := range l.ProductionRoutes.Active.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 active environment, 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 active environment, route %s, ingress %s: %s",
configurationSnippet, rName, iName, annotation)
}
// modsecurity-snippet
if _, ok := ingress.Annotations[modsecuritySnippet]; ok {
return fmt.Errorf(
"invalid %s annotation on active environment, 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 active environment, route %s, ingress %s: %s",
serverSnippet, rName, iName, annotation)
}
}
}
}
}
for _, routeMap := range l.ProductionRoutes.Standby.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 standby environment, 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 standby environment, route %s, ingress %s: %s",
configurationSnippet, rName, iName, annotation)
}
// modsecurity-snippet
if _, ok := ingress.Annotations[modsecuritySnippet]; ok {
return fmt.Errorf(
"invalid %s annotation on standby environment, 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 standby environment, route %s, ingress %s: %s",
serverSnippet, rName, iName, annotation)
}
}
}
}
}
return nil
}
}
19 changes: 19 additions & 0 deletions internal/lagoonyml/testdata/invalid.0.lagoon.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,22 @@ environments:
if ($request_uri !~ \"^/abc\") {
return 301 https://dev.example.com$request_uri;
}

production_routes:
active:
routes:
- nginx:
- "www.example.com":
tls-acme: true
insecure: Redirect
annotations:
nginx.ingress.kubernetes.io/server-snippet: |
if ($request_uri !~ \"^/abc\") {
return 301 https://dev.example.com$request_uri;
}
standby:
routes:
- nginx:
- "www.standby.example.com":
tls-acme: "false"
insecure: Redirect
24 changes: 24 additions & 0 deletions internal/lagoonyml/testdata/valid.broken.0.lagoon.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
environments:
cronjobs:
routes:
- name: a cronjob defined as environment
schedule: "* * * * *"
command: echo "broken definition"
service: cli
main:
routes:
- nginx:
- example.com
- "www.example.com":
tls-acme: 'true'
insecure: Redirect
hsts: max-age=31536000
- "example.com":
annotations:
nginx.ingress.kubernetes.io/server-snippet: |
set_real_ip_from 1.2.3.4/32;
- "dev.example.com":
annotations:
nginx.ingress.kubernetes.io/server-snippet: |
set_real_ip_from 1.2.3.4/32;
add_header Content-type text/plain;