Skip to content
This repository has been archived by the owner on Jun 21, 2023. It is now read-only.

Commit

Permalink
Add support for configuring log level (#1)
Browse files Browse the repository at this point in the history
  • Loading branch information
Brianna Buckner authored Jul 6, 2021
1 parent 3e82f4e commit 179bf8b
Show file tree
Hide file tree
Showing 7 changed files with 712 additions and 145 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ The primary use case for this feature is Kubernetes Jobs, where a sidecar contai

kubexit is configured with environment variables only, to make it easy to configure in Kubernetes and minimize entrypoint/command changes.

General:
- `KUBEXIT_LOG_LEVEL` - The log level for logged messages. Default: `info`.

Tombstone:
- `KUBEXIT_NAME` - The name of the tombstone file to use. Must match the name of the Kubernetes pod container, if using birth dependency.
- `KUBEXIT_GRAVEYARD` - The file path of the graveyard directory, where tombstones will be read and written.
Expand Down
88 changes: 43 additions & 45 deletions cmd/kubexit/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ package main

import (
"context"
"errors"
"fmt"
"log"
"os"
"os/exec"
"os/signal"
Expand All @@ -14,6 +14,7 @@ import (

"github.com/fsnotify/fsnotify"
"github.com/karlkfi/kubexit/pkg/kubernetes"
"github.com/karlkfi/kubexit/pkg/log"
"github.com/karlkfi/kubexit/pkg/supervisor"
"github.com/karlkfi/kubexit/pkg/tombstone"

Expand All @@ -24,21 +25,18 @@ import (
func main() {
var err error

// remove log timestamp
log.SetFlags(log.Flags() &^ (log.Ldate | log.Ltime))

args := os.Args[1:]
if len(args) == 0 {
log.Println("Error: no arguments found")
log.Error(errors.New("no arguments found"), "Error: no arguments found")
os.Exit(2)
}

name := os.Getenv("KUBEXIT_NAME")
if name == "" {
log.Println("Error: missing env var: KUBEXIT_NAME")
log.Error(errors.New("missing env var: KUBEXIT_NAME"), "Error: missing env var: KUBEXIT_NAME")
os.Exit(2)
}
log.Printf("Name: %s\n", name)
log.Info("Name:", "name", name)

graveyard := os.Getenv("KUBEXIT_GRAVEYARD")
if graveyard == "" {
Expand All @@ -47,74 +45,74 @@ func main() {
graveyard = strings.TrimRight(graveyard, "/")
graveyard = filepath.Clean(graveyard)
}
log.Printf("Graveyard: %s\n", graveyard)
log.Info("Graveyard:", "graveyard", graveyard)

ts := &tombstone.Tombstone{
Graveyard: graveyard,
Name: name,
}
log.Printf("Tombstone: %s\n", ts.Path())
log.Info("Tombstone:", "tombstone", ts.Path())

birthDepsStr := os.Getenv("KUBEXIT_BIRTH_DEPS")
var birthDeps []string
if birthDepsStr == "" {
log.Println("Birth Deps: N/A")
log.Info("Birth Deps: N/A")
} else {
birthDeps = strings.Split(birthDepsStr, ",")
log.Printf("Birth Deps: %s\n", strings.Join(birthDeps, ","))
log.Info("Birth Deps:", "birth deps", strings.Join(birthDeps, ","))
}

deathDepsStr := os.Getenv("KUBEXIT_DEATH_DEPS")
var deathDeps []string
if deathDepsStr == "" {
log.Println("Death Deps: N/A")
log.Info("Death Deps: N/A")
} else {
deathDeps = strings.Split(deathDepsStr, ",")
log.Printf("Death Deps: %s\n", strings.Join(deathDeps, ","))
log.Info("Death Deps:", "death deps", strings.Join(deathDeps, ","))
}

birthTimeout := 30 * time.Second
birthTimeoutStr := os.Getenv("KUBEXIT_BIRTH_TIMEOUT")
if birthTimeoutStr != "" {
birthTimeout, err = time.ParseDuration(birthTimeoutStr)
if err != nil {
log.Printf("Error: failed to parse birth timeout: %v\n", err)
log.Error(err, "Error: failed to parse birth timeout")
os.Exit(2)
}
}
log.Printf("Birth Timeout: %s\n", birthTimeout)
log.Info("Birth Timeout:", "birth timeout", birthTimeout)

gracePeriod := 30 * time.Second
gracePeriodStr := os.Getenv("KUBEXIT_GRACE_PERIOD")
if gracePeriodStr != "" {
gracePeriod, err = time.ParseDuration(gracePeriodStr)
if err != nil {
log.Printf("Error: failed to parse grace period: %v\n", err)
log.Error(err, "Error: failed to parse grace period")
os.Exit(2)
}
}
log.Printf("Grace Period: %s\n", gracePeriod)
log.Info("Grace Period:", "grace period", gracePeriod)

podName := os.Getenv("KUBEXIT_POD_NAME")
if podName == "" {
if len(birthDeps) > 0 {
log.Println("Error: missing env var: KUBEXIT_POD_NAME")
log.Error(errors.New("missing env var: KUBEXIT_POD_NAME"), "missing env var", "var_name", "KUBEXIT_POD_NAME)
os.Exit(2)
}
log.Println("Pod Name: N/A")
log.Info("Pod Name: N/A")
} else {
log.Printf("Pod Name: %s\n", podName)
log.Info("Pod Name:", "pod name", podName)
}

namespace := os.Getenv("KUBEXIT_NAMESPACE")
if namespace == "" {
if len(birthDeps) > 0 {
log.Println("Error: missing env var: KUBEXIT_NAMESPACE")
log.Error(errors.New("missing env var: KUBEXIT_NAMESPACE"), "Error: missing env var: KUBEXIT_NAMESPACE")
os.Exit(2)
}
log.Println("Namespace: N/A")
log.Info("Namespace: N/A")
} else {
log.Printf("Namespace: %s\n", namespace)
log.Info("Namespace:", "namespace", namespace)
}

child := supervisor.New(args[0], args[1:]...)
Expand All @@ -125,44 +123,44 @@ func main() {
// stop graveyard watchers on exit, if not sooner
defer stopGraveyardWatcher()

log.Println("Watching graveyard...")
log.Info("Watching graveyard...")
err = tombstone.Watch(ctx, graveyard, onDeathOfAny(deathDeps, func() {
stopGraveyardWatcher()
// trigger graceful shutdown
// Skipped if not started.
err := child.ShutdownWithTimeout(gracePeriod)
// ShutdownWithTimeout doesn't block until timeout
if err != nil {
log.Printf("Error: failed to shutdown: %v\n", err)
log.Error(err, "Error: failed to shutdown")
}
}))
if err != nil {
fatalf(child, ts, "Error: failed to watch graveyard: %v\n", err)
fatalf(child, ts, err, "Error: failed to watch graveyard")
}
}

if len(birthDeps) > 0 {
err = waitForBirthDeps(birthDeps, namespace, podName, birthTimeout)
if err != nil {
fatalf(child, ts, "Error: %v\n", err)
fatalf(child, ts, err, "Error: failed waiting for birth deps")
}
}

err = child.Start()
if err != nil {
fatalf(child, ts, "Error: %v\n", err)
fatalf(child, ts, err, "Error: failed starting child")
}

err = ts.RecordBirth()
if err != nil {
fatalf(child, ts, "Error: %v\n", err)
fatalf(child, ts, err, "Error: failed recording birth")
}

code := waitForChildExit(child)

err = ts.RecordDeath(code)
if err != nil {
log.Printf("Error: %v\n", err)
log.Error(err, "Error: failed to record death")
os.Exit(1)
}

Expand All @@ -177,7 +175,7 @@ func waitForBirthDeps(birthDeps []string, namespace, podName string, timeout tim
// Stop pod watcher on exit, if not sooner
defer stopPodWatcher()

log.Println("Watching pod updates...")
log.Info("Watching pod updates...")
err := kubernetes.WatchPod(ctx, namespace, podName,
onReadyOfAll(birthDeps, stopPodWatcher),
)
Expand All @@ -195,7 +193,7 @@ func waitForBirthDeps(birthDeps []string, namespace, podName string, timeout tim
return fmt.Errorf("waiting for birth deps to be ready: %v", err)
}

log.Printf("All birth deps ready: %v\n", strings.Join(birthDeps, ", "))
log.Info("All birth deps ready:", "birth deps", strings.Join(birthDeps, ", "))
return nil
}

Expand All @@ -214,7 +212,7 @@ func withCancelOnSignal(ctx context.Context, signals ...os.Signal) context.Conte
if !ok {
return
}
log.Printf("Received shutdown signal: %v", s)
log.Info("Received shutdown signal:", "signal", s)
cancel()
case <-ctx.Done():
signal.Reset()
Expand All @@ -236,23 +234,23 @@ func waitForChildExit(child *supervisor.Supervisor) int {
} else {
code = -1
}
log.Printf("Child Exited(%d): %v\n", code, err)
log.Info("Child Exited:", "code", code, "err", err)
} else {
code = 0
log.Println("Child Exited(0)")
log.Info("Child Exited(0)")
}
return code
}

// fatalf is for terminal errors.
// The child process may or may not be running.
func fatalf(child *supervisor.Supervisor, ts *tombstone.Tombstone, msg string, args ...interface{}) {
log.Printf(msg, args...)
func fatalf(child *supervisor.Supervisor, ts *tombstone.Tombstone, fatalErr error, msg string, args ...interface{}) {
log.Error(fatalErr, msg, args...)

// Skipped if not started.
err := child.ShutdownNow()
if err != nil {
log.Printf("Error: failed to shutdown child process: %v", err)
log.Error(err, "Error: failed to shutdown child process")
os.Exit(1)
}

Expand All @@ -264,7 +262,7 @@ func fatalf(child *supervisor.Supervisor, ts *tombstone.Tombstone, msg string, a
// Another process may be waiting for it.
err = ts.RecordDeath(code)
if err != nil {
log.Printf("Error: %v\n", err)
log.Error(err, "Error: failed to record death")
os.Exit(1)
}

Expand All @@ -288,7 +286,7 @@ func onReadyOfAll(birthDeps []string, callback func()) kubernetes.EventHandler {

pod, ok := event.Object.(*corev1.Pod)
if !ok {
log.Printf("Error: unexpected non-pod object type: %+v\n", event.Object)
log.Error(fmt.Errorf("unexpected non-pod object type: %+v", event.Object), "Error: unexpected non-pod object type")
return
}

Expand Down Expand Up @@ -328,25 +326,25 @@ func onDeathOfAny(deathDeps []string, callback func()) tombstone.EventHandler {
graveyard := filepath.Dir(event.Name)
name := filepath.Base(event.Name)

log.Printf("Tombstone modified: %s\n", name)
log.Info("Tombstone modified:", "name", name)
if _, ok := deathDepSet[name]; !ok {
// ignore other tombstones
return
}

log.Printf("Reading tombstone: %s\n", name)
log.Info("Reading tombstone:", "name", name)
ts, err := tombstone.Read(graveyard, name)
if err != nil {
log.Printf("Error: failed to read tombstone: %v\n", err)
log.Error(err, "Error: failed to read tombstone")
return
}

if ts.Died == nil {
// still alive
return
}
log.Printf("New death: %s\n", name)
log.Printf("Tombstone(%s): %s\n", name, ts)
log.Info("New death:", "name", name)
log.Info("Tombstone:", "name", name, "tombstone", ts)

callback()
}
Expand Down
9 changes: 6 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ go 1.14

require (
github.com/fsnotify/fsnotify v1.4.9
k8s.io/api v0.18.2
k8s.io/apimachinery v0.18.2
k8s.io/client-go v0.18.2
github.com/go-logr/logr v0.4.0
go.uber.org/zap v1.17.0
k8s.io/api v0.21.1
k8s.io/apimachinery v0.21.1
k8s.io/client-go v0.21.1
sigs.k8s.io/controller-runtime v0.9.0
sigs.k8s.io/yaml v1.2.0
)
Loading

0 comments on commit 179bf8b

Please sign in to comment.