Skip to content

Commit

Permalink
Set ups CRUD operations, leaves stubs for auth in comments. (#12)
Browse files Browse the repository at this point in the history
* Set ups CRUD operations, leaves stibs for auth in comments.

Signed-off-by: Andrew Holtzmann <[email protected]>

* PR Feedback

Signed-off-by: Andrew Holtzmann <[email protected]>

* Makes supported types better, rename api/v1/record -> records

Signed-off-by: Andrew Holtzmann <[email protected]>

* Have the isSupported check return the ErrorUnsupportedType

Signed-off-by: Andrew Holtzmann <[email protected]>

* Fix comments

Signed-off-by: Andrew Holtzmann <[email protected]>

* Use defined errors more

Signed-off-by: Andrew Holtzmann <[email protected]>

Signed-off-by: Andrew Holtzmann <[email protected]>
  • Loading branch information
andy-v-h authored Oct 21, 2022
1 parent 7437573 commit 069b36c
Show file tree
Hide file tree
Showing 20 changed files with 1,110 additions and 126 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
coverage.out
.tools/
.vscode/
.vscode/
vendor/
dnscontroller
crdb-data
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

`dns-controller` is a simple controller that allows you to create and update [SRV records](https://datatracker.ietf.org/doc/html/rfc2782), as well as remove them when no endpoints are present.


## Project status

This project is in a alpha level / development phase. Each PR will strive to add functionality, although that may not be functionaly to the end state goals.


## About

SRV records are a lightweight service discovery method that can be implemented in a wide range of applications. It uses the well-known DNS protocol to retrieve information about the location, port, and protocol of particular service. `dns-controller` centrally manages the lifecycle of these records.
Expand Down Expand Up @@ -31,7 +37,7 @@ Follow the format `_service._proto.name. ttl IN SRV priority weight port target.

```go
type Answer struct {

owner string
protocol string
service string
Expand All @@ -57,7 +63,7 @@ owner:
origin: cluster-a
owner: team-a
service: artifacts
answer:
answer:
priority: 0
protocol: tcp
port: 443
Expand Down
2 changes: 1 addition & 1 deletion cmd/migrate.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ fix Apply sequential ordering to migrations
}

func init() {
rootCmd.AddCommand(migrateCmd)
root.Cmd.AddCommand(migrateCmd)
}

func migrate(command string, args []string) {
Expand Down
82 changes: 17 additions & 65 deletions cmd/root.go
Original file line number Diff line number Diff line change
@@ -1,84 +1,45 @@
package cmd

import (
"strings"

"github.com/spf13/cobra"
"github.com/spf13/pflag"
"go.hollow.sh/toolbox/version"
"go.uber.org/zap"

homedir "github.com/mitchellh/go-homedir"
"github.com/spf13/viper"
"go.hollow.sh/toolbox/rootcmd"
"go.hollow.sh/toolbox/version"
)

const app = "dnscontroller"

var (
cfgFile string
logger *zap.SugaredLogger
logger *zap.SugaredLogger
)

// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Use: "dns-controller",
Short: "metal-toolbox/dns-controller is a tool for centrally managing DNS record from disjoint sources",
}
// root represents the base command when called without any subcommands
var root = rootcmd.NewRootCmd(app, app+" is a tool for centrally managing DNS record from disjoint sources")

// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
cobra.CheckErr(rootCmd.Execute())
cobra.CheckErr(root.Execute())
}

func init() {
cobra.OnInitialize(initConfig)
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.hollow.yaml)")
rootCmd.PersistentFlags().Bool("debug", false, "enable debug logging")
viperBindFlag("logging.debug", rootCmd.PersistentFlags().Lookup("debug"))
rootCmd.PersistentFlags().Bool("pretty", false, "enable pretty (human readable) logging output")
viperBindFlag("logging.pretty", rootCmd.PersistentFlags().Lookup("pretty"))

rootCmd.PersistentFlags().String("db-uri", "postgresql://root@localhost:26257/dns-controller?sslmode=disable", "URI for database connection")
viperBindFlag("db.uri", rootCmd.PersistentFlags().Lookup("db-uri"))
root.InitFlags()
cobra.OnInitialize(appInit)
}

// initConfig reads in config file and ENV variables if set.
func initConfig() {
if cfgFile != "" {
// Use config file from the flag.
viper.SetConfigFile(cfgFile)
} else {
// Find home directory.
home, err := homedir.Dir()
cobra.CheckErr(err)

// Search config in home directory with name ".hollow" (without extension).
viper.AddConfigPath(home)
viper.SetConfigName(".dns-controller")
}

viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
viper.SetEnvPrefix("dnscontroller")
viper.AutomaticEnv() // read in environment variables that match

// If a config file is found, read it in.
err := viper.ReadInConfig()

setupLogging()

if err == nil {
logger.Infow("using config file",
"file", viper.ConfigFileUsed(),
)
}
func appInit() {
root.Options.InitConfig()
logger = setupLogging(root.Options)
}

func setupLogging() {
func setupLogging(o *rootcmd.Options) *zap.SugaredLogger {
cfg := zap.NewProductionConfig()
if viper.GetBool("logging.pretty") {
if o.PrettyPrint {
cfg = zap.NewDevelopmentConfig()
}

if viper.GetBool("logging.debug") {
if o.Debug {
cfg.Level = zap.NewAtomicLevelAt(zap.DebugLevel)
} else {
cfg.Level = zap.NewAtomicLevelAt(zap.InfoLevel)
Expand All @@ -89,14 +50,5 @@ func setupLogging() {
panic(err)
}

logger = l.Sugar().With("app", "dns-controller", "version", version.Version())
defer logger.Sync() //nolint:errcheck
}

// viperBindFlag provides a wrapper around the viper bindings that handles error checks
func viperBindFlag(name string, flag *pflag.Flag) {
err := viper.BindPFlag(name, flag)
if err != nil {
panic(err)
}
return l.Sugar().With("app", o.App, "version", version.Version())
}
68 changes: 68 additions & 0 deletions cmd/serve.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package cmd

import (
"context"

"github.com/jmoiron/sqlx"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"go.hollow.sh/toolbox/ginjwt"

"go.hollow.sh/dnscontroller/internal/httpsrv"
dbx "go.hollow.sh/dnscontroller/internal/x/db"
flagsx "go.hollow.sh/dnscontroller/internal/x/flags"
)

var serveCmd = &cobra.Command{
Use: "serve",
Short: "starts the dns-controller api server",
Run: func(cmd *cobra.Command, args []string) {
serve(cmd.Context())
},
}

func init() {
root.Cmd.AddCommand(serveCmd)

logger = root.Options.GetLogger()

serveCmd.Flags().String("listen", "0.0.0.0:14000", "address on which to listen")
flagsx.MustBindPFlag("listen", serveCmd.Flags().Lookup("listen"))

serveCmd.Flags().String("db-uri", "postgresql://root@localhost:26257/dns-controller?sslmode=disable", "URI for database connection")
flagsx.MustBindPFlag("db.uri", serveCmd.Flags().Lookup("db-uri"))

flagsx.RegisterOIDCFlags(serveCmd)
}

func serve(ctx context.Context) {
var db *sqlx.DB
if viper.GetBool("tracing.enabled") {
db = dbx.NewDBWithTracing(logger)
} else {
db = dbx.NewDB(logger)
}

logger.Infow("starting dns-controller api server", "address", viper.GetString("listen"))

hs := &httpsrv.Server{
Logger: logger,
Listen: viper.GetString("listen"),
Debug: viper.GetBool("logging.debug"),
DB: db,
AuthConfig: ginjwt.AuthConfig{
Enabled: viper.GetBool("oidc.enabled"),
Audience: viper.GetString("oidc.audience"),
Issuer: viper.GetString("oidc.issuer"),
JWKSURI: viper.GetString("oidc.jwksuri"),
LogFields: viper.GetStringSlice("oidc.log"), // TODO: We don't seem to be grabbing this from config?
RolesClaim: viper.GetString("oidc.claims.roles"),
UsernameClaim: viper.GetString("oidc.claims.username"),
},
TrustedProxies: viper.GetStringSlice("gin.trustedproxies"),
}

if err := hs.Run(); err != nil {
logger.Fatalw("failed starting metadata server", "error", err)
}
}
14 changes: 8 additions & 6 deletions db/migrations/00001_init.sql
Original file line number Diff line number Diff line change
Expand Up @@ -23,26 +23,28 @@ CREATE TABLE owners (
id UUID PRIMARY KEY NOT NULL DEFAULT gen_random_uuid(),
target STRING NOT NULL,
type STRING NOT NULL,
ttl INT DEFAULT 3600 NOT NULL,
ttl INT DEFAULT 3600 NOT NULL CHECK (ttl >= 0),
has_details BOOL NOT NULL,
owner_id UUID NOT NULL REFERENCES owners(id) ON DELETE CASCADE ON UPDATE CASCADE,
record_id UUID NOT NULL REFERENCES records(id) ON DELETE CASCADE ON UPDATE CASCADE,
created_at TIMESTAMPTZ NOT NULL,
updated_at TIMESTAMPTZ NOT NULL,
INDEX idx_record_owner (record_id, owner_id)
INDEX idx_record_owner (record_id, owner_id),
INDEX idx_record_target_type (record_id, target, type),
UNIQUE INDEX idx_record_owner_target_type (record_id, owner_id, target, type)
);

CREATE TABLE answer_details (
id UUID PRIMARY KEY NOT NULL DEFAULT gen_random_uuid(),
answer_id UUID NOT NULL REFERENCES answers(id) ON DELETE CASCADE ON UPDATE CASCADE,
port INT,
priority INT,
port INT CHECK (port >= 0),
priority INT CHECK (priority >= 0),
protocol STRING,
weight STRING,
weight INT CHECK (weight >= 0),
created_at TIMESTAMPTZ NOT NULL,
updated_at TIMESTAMPTZ NOT NULL,
UNIQUE INDEX idx_answer_id (answer_id)
);
);

-- +goose StatementEnd

Expand Down
71 changes: 55 additions & 16 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,50 @@ module go.hollow.sh/dnscontroller

go 1.19

// replace go.hollow.sh/toolbox => /home/aholtzmann/workspace.new/github.com/andy-v-h/hollow-toolbox

require (
github.com/XSAM/otelsql v0.16.0
github.com/cockroachdb/cockroach-go/v2 v2.2.14
github.com/friendsofgo/errors v0.9.2
github.com/gin-contrib/zap v0.1.0
github.com/gin-gonic/gin v1.8.1
github.com/google/uuid v1.3.0
github.com/jmoiron/sqlx v1.3.5
github.com/lib/pq v1.10.6
github.com/mitchellh/go-homedir v1.1.0
github.com/pkg/errors v0.9.1
github.com/pressly/goose/v3 v3.6.1
github.com/spf13/cobra v1.5.0
github.com/spf13/cobra v1.6.0
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.12.0
github.com/spf13/viper v1.13.0
github.com/volatiletech/null/v8 v8.1.2
github.com/volatiletech/randomize v0.0.1
github.com/volatiletech/sqlboiler/v4 v4.13.0
github.com/volatiletech/strmangle v0.0.4
go.hollow.sh/toolbox v0.1.5
go.uber.org/zap v1.21.0
github.com/zsais/go-gin-prometheus v0.1.0
go.hollow.sh/toolbox v0.4.0
go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.36.3
go.opentelemetry.io/otel v1.11.0
go.opentelemetry.io/otel/exporters/jaeger v1.11.0
go.opentelemetry.io/otel/sdk v1.11.0
go.uber.org/zap v1.23.0
)

require (
github.com/fsnotify/fsnotify v1.5.4 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-logr/logr v1.2.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-playground/locales v0.14.0 // indirect
github.com/go-playground/universal-translator v0.18.0 // indirect
github.com/go-playground/validator/v10 v10.11.1 // indirect
github.com/goccy/go-json v0.9.11 // indirect
github.com/gofrs/uuid v4.0.0+incompatible // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.0.1 // indirect
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
github.com/jackc/pgconn v1.12.1 // indirect
github.com/jackc/pgio v1.0.0 // indirect
Expand All @@ -33,22 +54,40 @@ require (
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
github.com/jackc/pgtype v1.11.0 // indirect
github.com/jackc/pgx/v4 v4.16.1 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
github.com/magiconair/properties v1.8.6 // indirect
github.com/mattn/go-isatty v0.0.16 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/pelletier/go-toml/v2 v2.0.1 // indirect
github.com/spf13/afero v1.8.2 // indirect
github.com/pelletier/go-toml/v2 v2.0.5 // indirect
github.com/prometheus/client_golang v1.13.0 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.37.0 // indirect
github.com/prometheus/procfs v0.8.0 // indirect
github.com/sirupsen/logrus v1.8.1 // indirect
github.com/spf13/afero v1.9.2 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/subosito/gotenv v1.3.0 // indirect
github.com/subosito/gotenv v1.4.1 // indirect
github.com/ugorji/go/codec v1.2.7 // indirect
github.com/volatiletech/inflect v0.0.1 // indirect
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
golang.org/x/crypto v0.0.0-20220517005047-85d78b3ac167 // indirect
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect
golang.org/x/text v0.3.7 // indirect
go.opentelemetry.io/otel/metric v0.31.0 // indirect
go.opentelemetry.io/otel/trace v1.11.0 // indirect
go.uber.org/atomic v1.10.0 // indirect
go.uber.org/multierr v1.8.0 // indirect
golang.org/x/crypto v0.0.0-20221012134737-56aed061732a // indirect
golang.org/x/net v0.0.0-20221014081412-f15817d10f9b // indirect
golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43 // indirect
golang.org/x/text v0.3.8 // indirect
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df // indirect
gopkg.in/ini.v1 v1.66.4 // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
Loading

0 comments on commit 069b36c

Please sign in to comment.