-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #4 from nsmith5/rekor-client
Basic Rekor client implementation
- Loading branch information
Showing
10 changed files
with
404 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,23 +1,89 @@ | ||
package main | ||
|
||
import "context" | ||
import ( | ||
"context" | ||
"errors" | ||
"fmt" | ||
"time" | ||
) | ||
|
||
type agent struct { | ||
rc *rekorClient | ||
|
||
quit chan struct{} | ||
} | ||
|
||
// newAgent constructs an agent from config or bails | ||
func newAgent(c config) (*agent, error) { | ||
return nil, ErrNotImplimented | ||
rc, err := newRekorClient(c.RekorServerURL) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
quit := make(chan struct{}) | ||
|
||
return &agent{rc, quit}, nil | ||
} | ||
|
||
// run starts off the agent. The call blocks or exits returning an error | ||
// if the agent hits a fatal error. | ||
func (a *agent) run() error { | ||
return ErrNotImplimented | ||
const initialBackoff = time.Duration(10) | ||
var currentBackoff = time.Duration(10) | ||
|
||
for { | ||
select { | ||
case _, ok := <-a.quit: | ||
if !ok { | ||
// Should be unreachable as we're supposed to close the channel | ||
// ourselves! | ||
return errors.New(`agent: quit chan closed. agent state corrupted`) | ||
} | ||
|
||
// Close channel to signal to shutdown caller we've cleanly shutdown | ||
close(a.quit) | ||
|
||
return nil | ||
|
||
default: | ||
entry, err := a.rc.getNextLogEntry() | ||
if err != nil { | ||
if err == ErrEntryDoesntExist { | ||
// Log doesn't exist yet, lets just wait 10 seconds and try again | ||
time.Sleep(10 * time.Second) | ||
|
||
} else { | ||
// Lets assume a temporary outage and retry with exponential backoff | ||
time.Sleep(currentBackoff * time.Second) | ||
currentBackoff *= 2 | ||
} | ||
break | ||
} | ||
|
||
// Incase we just recovered from a temporary outage, lets reset the backoff | ||
currentBackoff = initialBackoff | ||
|
||
// TODO: Do something with this log entry! | ||
fmt.Printf("%#v\n", entry) | ||
time.Sleep(5 * time.Second) | ||
} | ||
} | ||
} | ||
|
||
// shutdown gracefully stops the agent. Shutdown can take an arbitrarily long time. Use | ||
// context cancellation to force shutdown. | ||
func (a *agent) shutdown(context.Context) error { | ||
return ErrNotImplimented | ||
// context cancellation to force shutdown. Calling shutdown more than once will cause a | ||
// panic. | ||
func (a *agent) shutdown(ctx context.Context) error { | ||
a.quit <- struct{}{} | ||
|
||
select { | ||
case <-a.quit: | ||
// Graceful shutdown complete | ||
return nil | ||
|
||
case <-ctx.Done(): | ||
// We took too long shutting down and the caller is | ||
// angry. Time to give up | ||
return errors.New(`timeout on graceful shutdown of agent`) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
package main | ||
|
||
type config struct { | ||
RekorServerURL string `yaml:"rekorServerURL"` | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
rekorServerURL: https://rekor.sigstore.dev |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,36 +1,104 @@ | ||
package main | ||
|
||
import ( | ||
"errors" | ||
"context" | ||
"fmt" | ||
"log" | ||
"os" | ||
"os/signal" | ||
"syscall" | ||
"time" | ||
|
||
"github.com/oklog/run" | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
var ( | ||
// ErrNotImplimented signals incomplete code path | ||
ErrNotImplimented = errors.New(`rekor-sidekick: not implemented`) | ||
"github.com/spf13/viper" | ||
) | ||
|
||
func newCLI() *cobra.Command { | ||
cmd := &cobra.Command{ | ||
Use: "rekor-sidekick", | ||
Short: "Transparency log monitoring and alerting", | ||
Long: "Daemon that monitors a Rekor instance and forwards entries of interest to configurable destinations", | ||
Run: runCLI, | ||
} | ||
|
||
cmd.Flags().String("config", "/etc/rekor-sidekick/config.yaml", "Path to configuration file") | ||
return cmd | ||
} | ||
|
||
func runCLI(cmd *cobra.Command, args []string) { | ||
fmt.Println(ErrNotImplimented) | ||
os.Exit(1) | ||
if err := viper.BindPFlags(cmd.Flags()); err != nil { | ||
fmt.Println("Failed to bind command line flags to viper:", err) | ||
os.Exit(1) | ||
} | ||
|
||
// Environment variable setup | ||
viper.SetEnvPrefix(`REKOR_SIDEKICK`) | ||
viper.AutomaticEnv() | ||
|
||
// Load config file | ||
{ | ||
f, err := os.Open(viper.GetString("config")) | ||
if err != nil { | ||
fmt.Println("Failed to open config file:", err) | ||
os.Exit(1) | ||
} | ||
defer f.Close() | ||
|
||
viper.SetConfigType("yaml") | ||
if err := viper.ReadConfig(f); err != nil { | ||
fmt.Println("Failed to parse config:", err) | ||
os.Exit(1) | ||
} | ||
} | ||
|
||
var c config | ||
if err := viper.Unmarshal(&c); err != nil { | ||
fmt.Println("Failed to load configuration:", err) | ||
os.Exit(1) | ||
} | ||
|
||
a, err := newAgent(c) | ||
if err != nil { | ||
fmt.Println("Failed to initialize agent:", err) | ||
os.Exit(1) | ||
} | ||
|
||
// This run group manages all the concurrent processes | ||
// this command runs. That is the agent and a signal handler | ||
// at the moment. | ||
var g run.Group | ||
|
||
// Agent process | ||
g.Add(a.run, func(error) { | ||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) | ||
defer cancel() | ||
a.shutdown(ctx) | ||
}) | ||
|
||
// Signal handler process | ||
{ | ||
ctx, cancel := context.WithCancel(context.Background()) | ||
g.Add(func() error { | ||
c := make(chan os.Signal, 1) | ||
signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) | ||
select { | ||
case sig := <-c: | ||
return fmt.Errorf("received signal %s", sig) | ||
case <-ctx.Done(): | ||
return ctx.Err() | ||
} | ||
}, func(error) { | ||
cancel() | ||
}) | ||
} | ||
|
||
// Launch! | ||
if err = g.Run(); err != nil { | ||
log.Println(err) | ||
} | ||
|
||
} | ||
|
||
func main() { | ||
cmd := newCLI() | ||
|
||
cmd.Execute() | ||
} |
Oops, something went wrong.