Skip to content

Commit

Permalink
Ensure namespaces get deleted
Browse files Browse the repository at this point in the history
- can't reproduce #1 but added code to ensure namespaces get removed if they have been deleted from the cluster
- update backend-dev to run go code directly rather than building docker image
- make timings configurable and add to README
- add error handling to ranger code to avoid #1 scenario
- disable CORS by default, only needed for dev
  • Loading branch information
sgdan committed Jun 27, 2020
1 parent 7acf7ae commit 10fc8a1
Show file tree
Hide file tree
Showing 12 changed files with 75 additions and 30 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ gradle
gradlew
gradlew.bat
.idea
reaper
10 changes: 6 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,12 @@ frontend-dev:
cd frontend && elm-app start

# Back end runs on localhost:8080, need CORS so dev front end can connect
backend-dev: build
docker run --rm -it -e CORS_ENABLED=true \
-v $(HOME)/.kube:/root/.kube -p 8080:8080 podreaper
# Assumes you have golang tools installed
backend-dev:
go mod tidy
go mod download
go build -o reaper ./cmd/reaper
CORS_ENABLED=true ./reaper


# unit testing
Expand All @@ -47,7 +50,6 @@ backend-test:
go test -v ./cmd/reaper



# gradle shell for back end
backend-shell: build
docker run --rm -it -e CORS_ENABLED=true \
Expand Down
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,20 @@ The UI served by the k8s pod allows the following:
To build and run the docker container, use `make run` then go to
[http://localhost:8080](http://localhost:8080) for the UI.

## Configuration

The container can be configured using environment variables:

| Variable | Default | Description |
| ------------------ | -------------------------------------------------------- | -------------------------------------------- |
| IGNORED_NAMESPACES | kube-system,kube-public,kube-node-lease,podreaper,docker | Reaper will ignore these namespaces |
| ZONE_ID | UTC | Time Zone used by UI |
| NAMESPACE_TICK | 11s | How often to update namespace data for UI |
| NAMESPACES_TICK | 17s | How often to check for new namespaces |
| RANGER_TICK | 41s | How often to check limit ranges |
| CLOCK_TICK | 13s | How often to update UI clock |
| REAPER_TICK | 29s | How often to check if pods need to be reaped |

## Deployment

```bash
Expand Down Expand Up @@ -71,7 +85,7 @@ For front end unit tests run `make frontend-test`.
- Back end written in [Golang](https://golang.org/) and uses:
- [client-go](https://github.com/kubernetes/client-go) go client for k8s
- Note: Original backend was based on [Micronaut](https://micronaut.io/) and used:
- [Kotlin](https://kotlinlang.org/))
- [Kotlin](https://kotlinlang.org/)
- [Fabric8 kubernetes client](https://github.com/fabric8io/kubernetes-client)

# Links
Expand Down
12 changes: 11 additions & 1 deletion cmd/reaper/k8s.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,16 @@ func (o *k8s) removeResourceQuota(ns string, rqName string) error {

// Create default limit range for namespace if it doesn't exist
func (o *k8s) checkLimitRange(ns string) {
status, err := o.getStatusOf(ns)
if err != nil {
log.Printf("Ignoring limit range for %v because it has no status", ns)
return
}
if status != "Active" {
log.Printf("Ignoring limit range for %v because it has status %v", ns, status)
return
}

lr := &v1.LimitRange{
ObjectMeta: metav1.ObjectMeta{Name: limitRangeName},
Spec: v1.LimitRangeSpec{
Expand All @@ -151,7 +161,7 @@ func (o *k8s) checkLimitRange(ns string) {
},
}
lrs := o.clientset.CoreV1().LimitRanges(ns)
_, err := lrs.Get(limitRangeName, metav1.GetOptions{})
_, err = lrs.Get(limitRangeName, metav1.GetOptions{})
if err != nil {
log.Printf("Creating default limit range for %v", ns)
lrs.Create(lr)
Expand Down
2 changes: 1 addition & 1 deletion cmd/reaper/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func main() {
clientset = initOutOfCluster()
}

s := newState(*location, spec.IgnoredNamespaces, k8s{clientset: clientset})
s := newState(spec, *location, k8s{clientset: clientset})
go maintainStatus(s)
go maintainNamespaces(s)
go maintainLimitRanges(s)
Expand Down
14 changes: 11 additions & 3 deletions cmd/reaper/namespaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func maintainNamespaces(s state) {
tickers := map[string](chan bool){}

checkNamespaces(tickers, s) // don't wait for first tick
tick := time.Tick(7 * time.Second)
tick := time.Tick(s.Spec.NamespacesTick)
for {
select {
case <-tick:
Expand Down Expand Up @@ -48,7 +48,7 @@ func checkNamespaces(tickers map[string](chan bool), s state) {
// get the names of valid namespaces by removing the ignored ones
valid := []string{}
for _, next := range namespaces {
if !contains(s.ignoredNamespaces, next) {
if !contains(s.Spec.IgnoredNamespaces, next) {
valid = append(valid, next)
}
}
Expand All @@ -59,11 +59,19 @@ func checkNamespaces(tickers map[string](chan bool), s state) {
tickers[ns] = createTickerFor(ns, s)
}
}

// remove namespaces that have been deleted
// this will make sure it gets removed if the namespace ticker fails
for _, state := range <-s.getStates {
if !contains(valid, state.Name) {
s.rmNamespace <- state.Name
}
}
}

// Create a ticker to trigger updates for a particular namespace
func createTickerFor(name string, s state) chan bool {
ticker := time.NewTicker(5 * time.Second)
ticker := time.NewTicker(s.Spec.NamespaceTick)
done := make(chan bool)
go func() {
for {
Expand Down
2 changes: 1 addition & 1 deletion cmd/reaper/ranger.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package main
import "time"

func maintainLimitRanges(s state) {
tick := time.Tick(60 * time.Second)
tick := time.Tick(s.Spec.RangerTick)
for {
select {
case <-tick:
Expand Down
2 changes: 1 addition & 1 deletion cmd/reaper/reaper.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
)

func reap(s state) {
tick := time.Tick(21 * time.Second)
tick := time.Tick(s.Spec.ReaperTick)
for {
select {
case <-tick:
Expand Down
28 changes: 14 additions & 14 deletions cmd/reaper/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import (
)

type state struct {
timeZone time.Location
ignoredNamespaces []string
cluster k8s // access to the cluster
Spec Specification
timeZone time.Location
cluster k8s // access to the cluster

// changes and updates
triggerNs chan string // signal that namespace needs to be updated
Expand All @@ -23,18 +23,18 @@ type state struct {
getStates chan []nsState // get cached namespace state
}

func newState(tz time.Location, ignored []string, cluster k8s) state {
func newState(spec Specification, tz time.Location, cluster k8s) state {
s := state{
timeZone: tz,
ignoredNamespaces: ignored,
cluster: cluster,
triggerNs: make(chan string),
rmNamespace: make(chan string),
updateNsState: make(chan nsState),
updateNsConfig: make(chan nsConfig),
getStatus: make(chan string),
getConfigs: make(chan []nsConfig),
getStates: make(chan []nsState),
Spec: spec,
timeZone: tz,
cluster: cluster,
triggerNs: make(chan string),
rmNamespace: make(chan string),
updateNsState: make(chan nsState),
updateNsConfig: make(chan nsConfig),
getStatus: make(chan string),
getConfigs: make(chan []nsConfig),
getStates: make(chan []nsState),
}
return s
}
Expand Down
4 changes: 2 additions & 2 deletions cmd/reaper/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ func maintainStatus(s state) {
configs := loadConfigs(s)
configsChanged := false
states := map[string]nsState{}
clockTick := time.Tick(7 * time.Second) // trigger clock updates
cfgTick := time.Tick(17 * time.Second) // trigger config saves
clockTick := time.Tick(s.Spec.ClockTick) // trigger clock updates
cfgTick := time.Tick(s.Spec.ReaperTick) // trigger config saves

for {
select {
Expand Down
12 changes: 11 additions & 1 deletion cmd/reaper/types.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package main

import "time"

/*
Specification contains default configuration for this app
that can be overridden using environment variables.
Expand All @@ -11,10 +13,18 @@ previous Kotlin/Micronaut code.
type Specification struct {
IgnoredNamespaces []string `default:"kube-system,kube-public,kube-node-lease,podreaper,docker" envconfig:"ignored_namespaces"`
ZoneID string `default:"UTC" envconfig:"zone_id"`
CorsEnabled bool `default:"true" envconfig:"cors_enabled"`
CorsEnabled bool `default:"false" envconfig:"cors_enabled"`
CorsOrigins []string `default:"http://localhost:3000" envconfig:"cors_origins"`
InCluster bool `default:"false" envconfig:"in_cluster"`
StaticFiles string `default:"" envconfig:"static_files"`

// timings
NamespaceTick time.Duration `default:"11s" envconfig:"namespace_tick"`
NamespacesTick time.Duration `default:"17s" envconfig:"namespaces_tick"`
RangerTick time.Duration `default:"41s" envconfig:"ranger_tick"`
ClockTick time.Duration `default:"13s" envconfig:"clock_tick"`
ConfigTick time.Duration `default:"17s" envconfig:"config_tick"`
ReaperTick time.Duration `default:"29s" envconfig:"reaper_tick"`
}

// This is the status displayed by the UI
Expand Down
2 changes: 1 addition & 1 deletion deploy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ metadata:
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "watch", "list", "delete"]
verbs: ["get", "watch", "list", "delete", "deletecollection"]
- apiGroups: [""]
resources: ["namespaces"]
verbs: ["get", "list"]
Expand Down

0 comments on commit 10fc8a1

Please sign in to comment.