Skip to content

Commit 069b36c

Browse files
authored
Set ups CRUD operations, leaves stubs for auth in comments. (#12)
* 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]>
1 parent 7437573 commit 069b36c

File tree

20 files changed

+1110
-126
lines changed

20 files changed

+1110
-126
lines changed

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
coverage.out
22
.tools/
3-
.vscode/
3+
.vscode/
4+
vendor/
5+
dnscontroller
6+
crdb-data

README.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
`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.
44

5+
6+
## Project status
7+
8+
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.
9+
10+
511
## About
612

713
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.
@@ -31,7 +37,7 @@ Follow the format `_service._proto.name. ttl IN SRV priority weight port target.
3137

3238
```go
3339
type Answer struct {
34-
40+
3541
owner string
3642
protocol string
3743
service string
@@ -57,7 +63,7 @@ owner:
5763
origin: cluster-a
5864
owner: team-a
5965
service: artifacts
60-
answer:
66+
answer:
6167
priority: 0
6268
protocol: tcp
6369
port: 443

cmd/migrate.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ fix Apply sequential ordering to migrations
3636
}
3737

3838
func init() {
39-
rootCmd.AddCommand(migrateCmd)
39+
root.Cmd.AddCommand(migrateCmd)
4040
}
4141

4242
func migrate(command string, args []string) {

cmd/root.go

Lines changed: 17 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,84 +1,45 @@
11
package cmd
22

33
import (
4-
"strings"
5-
64
"github.com/spf13/cobra"
7-
"github.com/spf13/pflag"
8-
"go.hollow.sh/toolbox/version"
95
"go.uber.org/zap"
106

11-
homedir "github.com/mitchellh/go-homedir"
12-
"github.com/spf13/viper"
7+
"go.hollow.sh/toolbox/rootcmd"
8+
"go.hollow.sh/toolbox/version"
139
)
1410

11+
const app = "dnscontroller"
12+
1513
var (
16-
cfgFile string
17-
logger *zap.SugaredLogger
14+
logger *zap.SugaredLogger
1815
)
1916

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

2620
// Execute adds all child commands to the root command and sets flags appropriately.
2721
// This is called by main.main(). It only needs to happen once to the rootCmd.
2822
func Execute() {
29-
cobra.CheckErr(rootCmd.Execute())
23+
cobra.CheckErr(root.Execute())
3024
}
3125

3226
func init() {
33-
cobra.OnInitialize(initConfig)
34-
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.hollow.yaml)")
35-
rootCmd.PersistentFlags().Bool("debug", false, "enable debug logging")
36-
viperBindFlag("logging.debug", rootCmd.PersistentFlags().Lookup("debug"))
37-
rootCmd.PersistentFlags().Bool("pretty", false, "enable pretty (human readable) logging output")
38-
viperBindFlag("logging.pretty", rootCmd.PersistentFlags().Lookup("pretty"))
39-
40-
rootCmd.PersistentFlags().String("db-uri", "postgresql://root@localhost:26257/dns-controller?sslmode=disable", "URI for database connection")
41-
viperBindFlag("db.uri", rootCmd.PersistentFlags().Lookup("db-uri"))
27+
root.InitFlags()
28+
cobra.OnInitialize(appInit)
4229
}
4330

44-
// initConfig reads in config file and ENV variables if set.
45-
func initConfig() {
46-
if cfgFile != "" {
47-
// Use config file from the flag.
48-
viper.SetConfigFile(cfgFile)
49-
} else {
50-
// Find home directory.
51-
home, err := homedir.Dir()
52-
cobra.CheckErr(err)
53-
54-
// Search config in home directory with name ".hollow" (without extension).
55-
viper.AddConfigPath(home)
56-
viper.SetConfigName(".dns-controller")
57-
}
58-
59-
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
60-
viper.SetEnvPrefix("dnscontroller")
61-
viper.AutomaticEnv() // read in environment variables that match
62-
63-
// If a config file is found, read it in.
64-
err := viper.ReadInConfig()
65-
66-
setupLogging()
67-
68-
if err == nil {
69-
logger.Infow("using config file",
70-
"file", viper.ConfigFileUsed(),
71-
)
72-
}
31+
func appInit() {
32+
root.Options.InitConfig()
33+
logger = setupLogging(root.Options)
7334
}
7435

75-
func setupLogging() {
36+
func setupLogging(o *rootcmd.Options) *zap.SugaredLogger {
7637
cfg := zap.NewProductionConfig()
77-
if viper.GetBool("logging.pretty") {
38+
if o.PrettyPrint {
7839
cfg = zap.NewDevelopmentConfig()
7940
}
8041

81-
if viper.GetBool("logging.debug") {
42+
if o.Debug {
8243
cfg.Level = zap.NewAtomicLevelAt(zap.DebugLevel)
8344
} else {
8445
cfg.Level = zap.NewAtomicLevelAt(zap.InfoLevel)
@@ -89,14 +50,5 @@ func setupLogging() {
8950
panic(err)
9051
}
9152

92-
logger = l.Sugar().With("app", "dns-controller", "version", version.Version())
93-
defer logger.Sync() //nolint:errcheck
94-
}
95-
96-
// viperBindFlag provides a wrapper around the viper bindings that handles error checks
97-
func viperBindFlag(name string, flag *pflag.Flag) {
98-
err := viper.BindPFlag(name, flag)
99-
if err != nil {
100-
panic(err)
101-
}
53+
return l.Sugar().With("app", o.App, "version", version.Version())
10254
}

cmd/serve.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package cmd
2+
3+
import (
4+
"context"
5+
6+
"github.com/jmoiron/sqlx"
7+
"github.com/spf13/cobra"
8+
"github.com/spf13/viper"
9+
"go.hollow.sh/toolbox/ginjwt"
10+
11+
"go.hollow.sh/dnscontroller/internal/httpsrv"
12+
dbx "go.hollow.sh/dnscontroller/internal/x/db"
13+
flagsx "go.hollow.sh/dnscontroller/internal/x/flags"
14+
)
15+
16+
var serveCmd = &cobra.Command{
17+
Use: "serve",
18+
Short: "starts the dns-controller api server",
19+
Run: func(cmd *cobra.Command, args []string) {
20+
serve(cmd.Context())
21+
},
22+
}
23+
24+
func init() {
25+
root.Cmd.AddCommand(serveCmd)
26+
27+
logger = root.Options.GetLogger()
28+
29+
serveCmd.Flags().String("listen", "0.0.0.0:14000", "address on which to listen")
30+
flagsx.MustBindPFlag("listen", serveCmd.Flags().Lookup("listen"))
31+
32+
serveCmd.Flags().String("db-uri", "postgresql://root@localhost:26257/dns-controller?sslmode=disable", "URI for database connection")
33+
flagsx.MustBindPFlag("db.uri", serveCmd.Flags().Lookup("db-uri"))
34+
35+
flagsx.RegisterOIDCFlags(serveCmd)
36+
}
37+
38+
func serve(ctx context.Context) {
39+
var db *sqlx.DB
40+
if viper.GetBool("tracing.enabled") {
41+
db = dbx.NewDBWithTracing(logger)
42+
} else {
43+
db = dbx.NewDB(logger)
44+
}
45+
46+
logger.Infow("starting dns-controller api server", "address", viper.GetString("listen"))
47+
48+
hs := &httpsrv.Server{
49+
Logger: logger,
50+
Listen: viper.GetString("listen"),
51+
Debug: viper.GetBool("logging.debug"),
52+
DB: db,
53+
AuthConfig: ginjwt.AuthConfig{
54+
Enabled: viper.GetBool("oidc.enabled"),
55+
Audience: viper.GetString("oidc.audience"),
56+
Issuer: viper.GetString("oidc.issuer"),
57+
JWKSURI: viper.GetString("oidc.jwksuri"),
58+
LogFields: viper.GetStringSlice("oidc.log"), // TODO: We don't seem to be grabbing this from config?
59+
RolesClaim: viper.GetString("oidc.claims.roles"),
60+
UsernameClaim: viper.GetString("oidc.claims.username"),
61+
},
62+
TrustedProxies: viper.GetStringSlice("gin.trustedproxies"),
63+
}
64+
65+
if err := hs.Run(); err != nil {
66+
logger.Fatalw("failed starting metadata server", "error", err)
67+
}
68+
}

db/migrations/00001_init.sql

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,26 +23,28 @@ CREATE TABLE owners (
2323
id UUID PRIMARY KEY NOT NULL DEFAULT gen_random_uuid(),
2424
target STRING NOT NULL,
2525
type STRING NOT NULL,
26-
ttl INT DEFAULT 3600 NOT NULL,
26+
ttl INT DEFAULT 3600 NOT NULL CHECK (ttl >= 0),
2727
has_details BOOL NOT NULL,
2828
owner_id UUID NOT NULL REFERENCES owners(id) ON DELETE CASCADE ON UPDATE CASCADE,
2929
record_id UUID NOT NULL REFERENCES records(id) ON DELETE CASCADE ON UPDATE CASCADE,
3030
created_at TIMESTAMPTZ NOT NULL,
3131
updated_at TIMESTAMPTZ NOT NULL,
32-
INDEX idx_record_owner (record_id, owner_id)
32+
INDEX idx_record_owner (record_id, owner_id),
33+
INDEX idx_record_target_type (record_id, target, type),
34+
UNIQUE INDEX idx_record_owner_target_type (record_id, owner_id, target, type)
3335
);
3436

3537
CREATE TABLE answer_details (
3638
id UUID PRIMARY KEY NOT NULL DEFAULT gen_random_uuid(),
3739
answer_id UUID NOT NULL REFERENCES answers(id) ON DELETE CASCADE ON UPDATE CASCADE,
38-
port INT,
39-
priority INT,
40+
port INT CHECK (port >= 0),
41+
priority INT CHECK (priority >= 0),
4042
protocol STRING,
41-
weight STRING,
43+
weight INT CHECK (weight >= 0),
4244
created_at TIMESTAMPTZ NOT NULL,
4345
updated_at TIMESTAMPTZ NOT NULL,
4446
UNIQUE INDEX idx_answer_id (answer_id)
45-
);
47+
);
4648

4749
-- +goose StatementEnd
4850

go.mod

Lines changed: 55 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,50 @@ module go.hollow.sh/dnscontroller
22

33
go 1.19
44

5+
// replace go.hollow.sh/toolbox => /home/aholtzmann/workspace.new/github.com/andy-v-h/hollow-toolbox
6+
57
require (
8+
github.com/XSAM/otelsql v0.16.0
69
github.com/cockroachdb/cockroach-go/v2 v2.2.14
710
github.com/friendsofgo/errors v0.9.2
11+
github.com/gin-contrib/zap v0.1.0
12+
github.com/gin-gonic/gin v1.8.1
13+
github.com/google/uuid v1.3.0
14+
github.com/jmoiron/sqlx v1.3.5
815
github.com/lib/pq v1.10.6
9-
github.com/mitchellh/go-homedir v1.1.0
1016
github.com/pkg/errors v0.9.1
1117
github.com/pressly/goose/v3 v3.6.1
12-
github.com/spf13/cobra v1.5.0
18+
github.com/spf13/cobra v1.6.0
1319
github.com/spf13/pflag v1.0.5
14-
github.com/spf13/viper v1.12.0
20+
github.com/spf13/viper v1.13.0
1521
github.com/volatiletech/null/v8 v8.1.2
1622
github.com/volatiletech/randomize v0.0.1
1723
github.com/volatiletech/sqlboiler/v4 v4.13.0
1824
github.com/volatiletech/strmangle v0.0.4
19-
go.hollow.sh/toolbox v0.1.5
20-
go.uber.org/zap v1.21.0
25+
github.com/zsais/go-gin-prometheus v0.1.0
26+
go.hollow.sh/toolbox v0.4.0
27+
go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.36.3
28+
go.opentelemetry.io/otel v1.11.0
29+
go.opentelemetry.io/otel/exporters/jaeger v1.11.0
30+
go.opentelemetry.io/otel/sdk v1.11.0
31+
go.uber.org/zap v1.23.0
2132
)
2233

2334
require (
24-
github.com/fsnotify/fsnotify v1.5.4 // indirect
35+
github.com/beorn7/perks v1.0.1 // indirect
36+
github.com/cespare/xxhash/v2 v2.1.2 // indirect
37+
github.com/fsnotify/fsnotify v1.6.0 // indirect
38+
github.com/gin-contrib/sse v0.1.0 // indirect
39+
github.com/go-logr/logr v1.2.3 // indirect
40+
github.com/go-logr/stdr v1.2.2 // indirect
41+
github.com/go-playground/locales v0.14.0 // indirect
42+
github.com/go-playground/universal-translator v0.18.0 // indirect
43+
github.com/go-playground/validator/v10 v10.11.1 // indirect
44+
github.com/goccy/go-json v0.9.11 // indirect
2545
github.com/gofrs/uuid v4.0.0+incompatible // indirect
46+
github.com/golang/protobuf v1.5.2 // indirect
2647
github.com/hashicorp/hcl v1.0.0 // indirect
27-
github.com/inconshreveable/mousetrap v1.0.0 // indirect
48+
github.com/inconshreveable/mousetrap v1.0.1 // indirect
2849
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
2950
github.com/jackc/pgconn v1.12.1 // indirect
3051
github.com/jackc/pgio v1.0.0 // indirect
@@ -33,22 +54,40 @@ require (
3354
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
3455
github.com/jackc/pgtype v1.11.0 // indirect
3556
github.com/jackc/pgx/v4 v4.16.1 // indirect
57+
github.com/json-iterator/go v1.1.12 // indirect
58+
github.com/leodido/go-urn v1.2.1 // indirect
3659
github.com/magiconair/properties v1.8.6 // indirect
60+
github.com/mattn/go-isatty v0.0.16 // indirect
61+
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
62+
github.com/mitchellh/go-homedir v1.1.0 // indirect
3763
github.com/mitchellh/mapstructure v1.5.0 // indirect
64+
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
65+
github.com/modern-go/reflect2 v1.0.2 // indirect
3866
github.com/pelletier/go-toml v1.9.5 // indirect
39-
github.com/pelletier/go-toml/v2 v2.0.1 // indirect
40-
github.com/spf13/afero v1.8.2 // indirect
67+
github.com/pelletier/go-toml/v2 v2.0.5 // indirect
68+
github.com/prometheus/client_golang v1.13.0 // indirect
69+
github.com/prometheus/client_model v0.2.0 // indirect
70+
github.com/prometheus/common v0.37.0 // indirect
71+
github.com/prometheus/procfs v0.8.0 // indirect
72+
github.com/sirupsen/logrus v1.8.1 // indirect
73+
github.com/spf13/afero v1.9.2 // indirect
4174
github.com/spf13/cast v1.5.0 // indirect
4275
github.com/spf13/jwalterweatherman v1.1.0 // indirect
43-
github.com/subosito/gotenv v1.3.0 // indirect
76+
github.com/subosito/gotenv v1.4.1 // indirect
77+
github.com/ugorji/go/codec v1.2.7 // indirect
4478
github.com/volatiletech/inflect v0.0.1 // indirect
45-
go.uber.org/atomic v1.7.0 // indirect
46-
go.uber.org/multierr v1.6.0 // indirect
47-
golang.org/x/crypto v0.0.0-20220517005047-85d78b3ac167 // indirect
48-
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect
49-
golang.org/x/text v0.3.7 // indirect
79+
go.opentelemetry.io/otel/metric v0.31.0 // indirect
80+
go.opentelemetry.io/otel/trace v1.11.0 // indirect
81+
go.uber.org/atomic v1.10.0 // indirect
82+
go.uber.org/multierr v1.8.0 // indirect
83+
golang.org/x/crypto v0.0.0-20221012134737-56aed061732a // indirect
84+
golang.org/x/net v0.0.0-20221014081412-f15817d10f9b // indirect
85+
golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43 // indirect
86+
golang.org/x/text v0.3.8 // indirect
5087
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df // indirect
51-
gopkg.in/ini.v1 v1.66.4 // indirect
88+
google.golang.org/protobuf v1.28.1 // indirect
89+
gopkg.in/ini.v1 v1.67.0 // indirect
90+
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
5291
gopkg.in/yaml.v2 v2.4.0 // indirect
5392
gopkg.in/yaml.v3 v3.0.1 // indirect
5493
)

0 commit comments

Comments
 (0)