Skip to content

Commit

Permalink
add static source
Browse files Browse the repository at this point in the history
  • Loading branch information
reddec committed Dec 9, 2021
1 parent 5aac9c0 commit b146a31
Show file tree
Hide file tree
Showing 5 changed files with 189 additions and 6 deletions.
6 changes: 6 additions & 0 deletions cmd/ingress-dashboard/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ type Config struct {
Auth string `long:"auth" env:"AUTH" description:"Auth scheme" default:"none" choice:"none" choice:"oidc" choice:"basic"`
BasicUser string `long:"basic-user" env:"BASIC_USER" description:"Basic Auth username"`
BasicPassword string `long:"basic-password" env:"BASIC_PASSWORD" description:"Basic Auth password"`
StaticSource string `long:"static-source" env:"STATIC_SOURCE" description:"Location of static ingress definitions" `
}

func main() {
Expand Down Expand Up @@ -63,6 +64,11 @@ func run(cfg Config) error {
defer cancel()

svc := internal.New()
staticDefinitions, err := internal.LoadDefinitions(cfg.StaticSource)
if err != nil {
return fmt.Errorf("load static definitions: %w", err)
}
svc.Prepend(staticDefinitions)

secured, err := cfg.secureHandler(ctx, svc)
if err != nil {
Expand Down
102 changes: 101 additions & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,107 @@ spec:
ingress-dashboard relies on annotations in
each [Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/) object to configure dashboard.
## Annotation
## Static source
For the external resource (without Ingress definitions) it is possible to
define static dashboard definitions.
Static definitions always placed before dynamic. No automatic logo URL detection available.
To enable static source define environment `STATIC_SOURCE=/path/to/source`, where source
could be directory or single file.

Directories are scanned recursively for each file with extension `.yml`, `.yaml`, or `.json`.
YAML documents may contain multiple definitions.

Support fields:

* `name` - resource label
* `namespace` - (optional) resource namespace, used in `from`
* `description` - (optional) resource description
* `hide` - (optional) mark resource as hidden or not. Default is `false`
* `urls` - list of urls
* `logo_url` - (optional) URL for log


Example:

```yaml
---
name: Some site
namespace: external links
urls:
- https://example.com
---
name: Google
logo_url: https://www.google.ru/favicon.ico
description: |
Well-known search engine
urls:
- https://google.com
```

Example usage in kubernetes with [config map](https://kubernetes.io/docs/concepts/configuration/configmap/):

```yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: static
namespace: ingress-dashboard
data:
static.yaml: |
---
name: Some site
namespace: external links
urls:
- https://example.com
---
name: Google
logo_url: https://www.google.ru/favicon.ico
description: |
Well-known search engine
urls:
- https://google.com
```

> Hint: you may use Kustomize [config map generator](https://kubernetes.io/docs/tasks/manage-kubernetes-objects/kustomization/#configmapgenerator) to simplify the process

And update deployment

```yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: "dashboard"
namespace: "ingress-dashboard"
spec:
# ...
template:
# ...
spec:
# ...
containers:
- name: "dashboard"
# ...
env:
# ...
- name: STATIC_SOURCE
value: /static
# ...
volumeMounts:
- name: config-volume
mountPath: /static
# ...
volumes:
- name: config-volume
configMap:
name: static
```

## Annotations

All annotations are optional.

Expand Down
13 changes: 13 additions & 0 deletions example/static.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
name: Some site
namespace: external links # used in 'from'
urls:
- https://example.com
---
name: Google
namespace: external links
logo_url: https://www.google.ru/favicon.ico
description: |
Well-known search engine
urls:
- https://google.com
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ require (
github.com/reddec/run-http-server v0.0.0-20211110142919-824037361ead
golang.org/x/net v0.0.0-20210520170846-37e1c6afe023
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
k8s.io/api v0.22.4
k8s.io/client-go v0.22.4
)
Expand Down Expand Up @@ -37,7 +38,6 @@ require (
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/square/go-jose.v2 v2.5.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
k8s.io/apimachinery v0.22.4 // indirect
k8s.io/klog/v2 v2.9.0 // indirect
k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a // indirect
Expand Down
72 changes: 68 additions & 4 deletions internal/service.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
package internal

import (
"errors"
"fmt"
"html/template"
"io"
"io/fs"
"net/http"
"os"
"path/filepath"
"strings"
"sync/atomic"

"github.com/reddec/ingress-dashboard/internal/auth"
"github.com/reddec/ingress-dashboard/internal/static"
"gopkg.in/yaml.v3"
)

type Ingress struct {
Expand Down Expand Up @@ -60,9 +67,10 @@ func New() *Service {
}

type Service struct {
cache atomic.Value // []Ingress
page *template.Template
router *http.ServeMux
cache atomic.Value // []Ingress
prepend atomic.Value // []Ingres
page *template.Template
router *http.ServeMux
}

func (svc *Service) Set(ingress []Ingress) {
Expand All @@ -73,14 +81,25 @@ func (svc *Service) Get() []Ingress {
return svc.cache.Load().([]Ingress)
}

// Prepend static list of ingresses.
func (svc *Service) Prepend(ingress []Ingress) {
svc.prepend.Store(ingress)
}

func (svc *Service) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
svc.router.ServeHTTP(writer, request)
}

func (svc *Service) getList() []Ingress {
prepend := svc.prepend.Load().([]Ingress)
main := svc.cache.Load().([]Ingress)
return append(prepend, main...)
}

func (svc *Service) getIndex(writer http.ResponseWriter, request *http.Request) {
writer.Header().Set("Content-Type", "text/html")
_ = svc.page.Execute(writer, UIContext{
Ingresses: visibleIngresses(svc.Get()),
Ingresses: visibleIngresses(svc.getList()),
User: auth.UserFromContext(request.Context()),
})
}
Expand All @@ -94,3 +113,48 @@ func visibleIngresses(list []Ingress) []Ingress {
}
return cp
}

// LoadDefinitions scans location (file or dir) for YAML/JSON (.yml, .yaml, .json) definitions of Ingress.
// Directories scanned recursive and each file can contain multiple definitions.
//
// Empty location is a special case and cause returning empty slice.
func LoadDefinitions(location string) ([]Ingress, error) {
if location == "" {
return nil, nil
}
var ans []Ingress
err := filepath.Walk(location, func(path string, info fs.FileInfo, err error) error {
if info.IsDir() {
return nil
}
if err != nil {
return err
}
ext := filepath.Ext(path)
if !(ext == ".yml" || ext == ".yaml" || ext == ".json") {
return nil
}

f, err := os.Open(path)
if err != nil {
return fmt.Errorf("open config file: %w", err)
}
defer f.Close()

var decoder = yaml.NewDecoder(f)
for {
var ingress Ingress
err := decoder.Decode(&ingress)
if errors.Is(err, io.EOF) {
break
}
if err != nil {
return fmt.Errorf("decode config %s: %w", path, err)
}
ans = append(ans, ingress)
}

return nil
})
return ans, err
}

0 comments on commit b146a31

Please sign in to comment.