diff --git a/.vscode/launch.json b/.vscode/launch.json index 3bca20a..e7a1abf 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -11,15 +11,10 @@ "mode": "auto", "program": "${workspaceFolder}/cmd/", "args": [ + "-c", + "${workspaceFolder}/conf/localdev.toml", "server", - "--debug", - "--provider-address", - "127.0.0.1:9090", - "--provider-insecure", - "--control-enabled", - "--control-insecure", - "--external-url=http://127.0.0.1:8080", - ] + ], }, { "name": "Run DSP devcontainer", @@ -31,14 +26,9 @@ "-buildvcs=false" ], "args": [ + "-c", + "${workspaceFolder}/conf/devcontainer.toml", "server", - "--debug", - "--provider-address", - "reference-provider:9090", - "--provider-insecure", - "--control-enabled", - "--control-insecure", - "--external-url=http://127.0.0.1:8080", ] } ] diff --git a/Dockerfile b/Dockerfile index e5296f0..a14c689 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -FROM golang:1.22.5 as builder +FROM docker.io/library/golang:1.22.5 AS builder WORKDIR /app COPY . ./ RUN make build diff --git a/cmd/main.go b/cmd/main.go index cc5a4b5..28958bd 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -15,26 +15,11 @@ package main import ( - "os" _ "time/tzdata" - "github.com/alecthomas/kong" - "github.com/go-dataspace/run-dsp/internal/cli" - "github.com/go-dataspace/run-dsp/internal/server" + "github.com/go-dataspace/run-dsp/cmd/root" ) -var ui struct { - cli.GlobalOptions - Server server.Command `cmd:"" help:"Run server"` -} - func main() { - if len(os.Args) == 1 { - os.Args = append(os.Args, "--help") - } - - ctx := kong.Parse(&ui) - params := cli.GenParams(ui.GlobalOptions) - ctx.BindTo(params, (*cli.Params)(nil)) - ctx.FatalIfErrorf(ctx.Run()) + root.Execute() } diff --git a/cmd/root/command.go b/cmd/root/command.go new file mode 100644 index 0000000..39d61fe --- /dev/null +++ b/cmd/root/command.go @@ -0,0 +1,102 @@ +// Copyright 2024 go-dataspace +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package root + +import ( + "context" + "fmt" + "log" + "os" + "slices" + + "github.com/go-dataspace/run-dsp/internal/server" + "github.com/go-dataspace/run-dsp/logging" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +var ( + cfgFile string + + validLogLevels = []string{"debug", "info", "warn", "error"} + + rootCmd = &cobra.Command{ + Use: "run-dsp", + Short: "RUN-DSP is a lightweight dataspace connector.", + Long: `A lightweight IDSA dataspace connector, designed to + connect non-dataspace data providers via gRPC`, + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + logLevel := viper.GetString("logLevel") + if !slices.Contains(validLogLevels, logLevel) { + return fmt.Errorf("Invalid log level %s, valid levels: %v", logLevel, validLogLevels) + } + ctx := context.Background() + humanReadable := false + if viper.GetBool("debug") { + humanReadable = true + logLevel = "debug" + } + ctx = logging.Inject(ctx, logging.NewJSON(logLevel, humanReadable)) + viper.Set("initCTX", ctx) + return nil + }, + } +) + +func init() { + cobra.OnInitialize(initConfig) + cobra.EnableTraverseRunHooks = true + + rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "", "config file (default is /etc/run-dsp/run-dsp.toml)") + rootCmd.PersistentFlags().BoolP("debug", "d", false, "enable debug mode") + rootCmd.PersistentFlags().StringP( + "log-level", "l", "info", fmt.Sprintf("set log level, valid levels: %v", validLogLevels)) + + err := viper.BindPFlag("debug", rootCmd.PersistentFlags().Lookup("debug")) + if err != nil { + panic(err.Error()) + } + err = viper.BindPFlag("logLevel", rootCmd.PersistentFlags().Lookup("log-level")) + if err != nil { + panic(err.Error()) + } + + viper.SetDefault("debug", false) + viper.SetDefault("logLevel", "info") + + rootCmd.AddCommand(server.Command) +} + +func initConfig() { + if cfgFile != "" { + viper.SetConfigFile(cfgFile) + } else { + viper.AddConfigPath("/etc/run-dsp") + viper.SetConfigType("toml") + viper.SetConfigName("run-dsp.toml") + } + + viper.AutomaticEnv() + if err := viper.ReadInConfig(); err == nil { + log.Println("Using config file:", viper.ConfigFileUsed()) + } +} + +func Execute() { + if err := rootCmd.Execute(); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} diff --git a/conf/devcontainer.toml b/conf/devcontainer.toml new file mode 100644 index 0000000..b0bf948 --- /dev/null +++ b/conf/devcontainer.toml @@ -0,0 +1,24 @@ +### GLOBAL OPTIONS ### +debug = true # Sets the output to a human readable format and sets the log level to debug. ($DEBUG) +logLevel = "debug" # Sets the log level. Valid values: debug/info/warn/error ($LOGLEVEL) + +### SERVER OPTIONS ### +[server] + +## Dataspace component configuration +[server.dsp] +address = "127.0.0.1" # IP address of the local machine to listen to for dataspace requests. ($SERVER.DSP.ADDRESS) +port = 8080 # TCP port to listen on for dataspace requests. ($SERVER.DSP.PORT) +externalURL = "http://127.0.0.1:8080" # Address that we are reachable by to other dataspace participants. ($SERVER.DSP.EXTERNALURL) + +## Provider gRPC settings +[server.provider] +address = "reference-provider:9090" # The address of the provider service. ($SERVER.PROVIDER.ADDRESS) +insecure = true # Disable TLS when connecting to the provider. ($SERVER.PROVIDER.INSECURE) + +## Control service settings +[server.control] +enabled = true # Enable the control service. ($SERVER.CONTROL.ENABLED) +address = "127.0.0.1" # IP address of the local machine to listen to for the control service. ($SERVER.CONTROL.ADDRESS) +port = 8081 # TCP port to listen on for the control service. ($SERVER.CONTROL.PORT) +insecure = true # Disable TLS for the control service ($SERVER.CONTROL.INSECURE) diff --git a/conf/localdev.toml b/conf/localdev.toml new file mode 100644 index 0000000..1a9048a --- /dev/null +++ b/conf/localdev.toml @@ -0,0 +1,24 @@ +### GLOBAL OPTIONS ### +debug = true # Sets the output to a human readable format and sets the log level to debug. ($DEBUG) +logLevel = "debug" # Sets the log level. Valid values: debug/info/warn/error ($LOGLEVEL) + +### SERVER OPTIONS ### +[server] + +## Dataspace component configuration +[server.dsp] +address = "127.0.0.1" # IP address of the local machine to listen to for dataspace requests. ($SERVER.DSP.ADDRESS) +port = 8080 # TCP port to listen on for dataspace requests. ($SERVER.DSP.PORT) +externalURL = "http://127.0.0.1:8080" # Address that we are reachable by to other dataspace participants. ($SERVER.DSP.EXTERNALURL) + +## Provider gRPC settings +[server.provider] +address = "127.0.0.1:19090" # The address of the provider service. ($SERVER.PROVIDER.ADDRESS) +insecure = true # Disable TLS when connecting to the provider. ($SERVER.PROVIDER.INSECURE) + +## Control service settings +[server.control] +enabled = true # Enable the control service. ($SERVER.CONTROL.ENABLED) +address = "127.0.0.1" # IP address of the local machine to listen to for the control service. ($SERVER.CONTROL.ADDRESS) +port = 8081 # TCP port to listen on for the control service. ($SERVER.CONTROL.PORT) +insecure = true # Disable TLS for the control service ($SERVER.CONTROL.INSECURE) diff --git a/conf/reference.toml b/conf/reference.toml new file mode 100644 index 0000000..fce11f5 --- /dev/null +++ b/conf/reference.toml @@ -0,0 +1,35 @@ +## This shows all configuration options of RUN-DSP, what they do and the corresponsing environment variables.###### RUN-DSP reference configuration file ##### +## The default location of the configuration file is /etc/run-dsp/run-dsp.toml but you can change that +## with the `-d` command line flag + +### GLOBAL OPTIONS ### +debug = true # Sets the output to a human readable format and sets the log level to debug. ($DEBUG) +logLevel = "info" # Sets the log level. Valid values: debug/info/warn/error ($LOGLEVEL) + +### SERVER OPTIONS ### +[server] + +## Dataspace component configuration +[server.dsp] +address = "127.0.0.1" # IP address of the local machine to listen to for dataspace requests. ($SERVER.DSP.ADDRESS) +port = 8080 # TCP port to listen on for dataspace requests. ($SERVER.DSP.PORT) +externalURL = "http://127.0.0.1:8080" # Address that we are reachable by to other dataspace participants. ($SERVER.DSP.EXTERNALURL) + +## Provider gRPC settings +[server.provider] +address = "127.0.0.1:9090" # The address of the provider service. ($SERVER.PROVIDER.ADDRESS) +insecure = false # Disable TLS when connecting to the provider. ($SERVER.PROVIDER.INSECURE) +caCert = "/path/to/ca.crt" # Path to the CA certificate of the CA that issued the server certificate of the provider. ($SERVER.PROVIDER.CACERT) +clientCert = "/path/to/client.crt" # Client certificate to authenticate with to the provider. ($SERVER.PROVIDER.CLIENTCERT) +clientCertKey = "/path/to/client.key" # Key to the above mentioned certificate. ($SERVER.PROVIDER.CLIENTCERTKEY) + +## Control service settings +[server.control] +enabled = true # Enable the control service. ($SERVER.CONTROL.ENABLED) +address = "127.0.0.1" # IP address of the local machine to listen to for the control service. ($SERVER.CONTROL.ADDRESS) +port = 8081 # TCP port to listen on for the control service. ($SERVER.CONTROL.PORT) +insecure = false # Disable TLS for the control service ($SERVER.CONTROL.INSECURE) +cert = "/path/to/control.crt" # TLS certificate to use for the control service ($SERVER.CONTROL.CERT) +certKey = "/path/to/control.key" # Key to the above mentioned certificate. ($SERVER.CONTROL.CERTKEY) +verifyClientCerts = true # Only allow access to clients with a certificate issued by the CA defined below. ($SERVER.CONTROL.VERIFYCLIENTCERTS) +clientCACert = "/etc/hosts" # Certificate of the CA that issues client certificates. ($SERVER.CONTROL.CLIENTCACERT) diff --git a/docker-compose.yml b/docker-compose.yml index 6dc265e..d6cb5ad 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,11 +6,11 @@ services: command: server environment: - LOGLEVEL=debug - - PROVIDER_INSECURE=true - - PROVIDER_URL=reference-provider:9090 - - EXTERNAL_URL=http://127.0.0.1:8080/ + - SERVER.PROVIDER.ADDRESS=reference-provider:9090 + - SERVER.PROVIDER.INSECURE=true + - SERVER.DSP.EXTERNALURL=http://127.0.0.1:8080/ ports: - - '8080' + - '18080:8080' depends_on: - reference-provider reference-provider: @@ -23,4 +23,5 @@ services: volumes: - .:/var/lib/run-dsp/fsprovider ports: - - '9091' + - '19091:9091' + - '19090:9090' diff --git a/go.mod b/go.mod index d54b048..096a930 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,6 @@ module github.com/go-dataspace/run-dsp go 1.22.5 require ( - github.com/alecthomas/kong v0.9.0 github.com/go-dataspace/run-dsrpc v0.0.3-alpha1 github.com/go-playground/validator/v10 v10.22.0 github.com/google/uuid v1.6.0 @@ -11,25 +10,44 @@ require ( github.com/justinas/alice v1.2.0 github.com/lmittmann/tint v1.0.5 github.com/samber/slog-http v1.3.1 + github.com/spf13/cobra v1.8.1 + github.com/spf13/viper v1.19.0 github.com/stretchr/testify v1.9.0 google.golang.org/grpc v1.64.1 google.golang.org/protobuf v1.34.2 ) require ( - github.com/davecgh/go-spew v1.1.1 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/gabriel-vasile/mimetype v1.4.4 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/sagikazarmark/locafero v0.4.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.11.0 // indirect + github.com/spf13/cast v1.6.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/objx v0.5.2 // indirect - go.opentelemetry.io/otel v1.19.0 // indirect - go.opentelemetry.io/otel/trace v1.19.0 // indirect + github.com/subosito/gotenv v1.6.0 // indirect + go.opentelemetry.io/otel v1.24.0 // indirect + go.opentelemetry.io/otel/trace v1.24.0 // indirect + go.uber.org/atomic v1.9.0 // indirect + go.uber.org/multierr v1.9.0 // indirect golang.org/x/crypto v0.24.0 // indirect + golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect golang.org/x/net v0.26.0 // indirect golang.org/x/sys v0.21.0 // indirect golang.org/x/text v0.16.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 9b50c6b..9d71037 100644 --- a/go.sum +++ b/go.sum @@ -1,17 +1,14 @@ -github.com/alecthomas/assert/v2 v2.6.0 h1:o3WJwILtexrEUk3cUVal3oiQY2tfgr/FHWiz/v2n4FU= -github.com/alecthomas/assert/v2 v2.6.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= -github.com/alecthomas/kong v0.9.0 h1:G5diXxc85KvoV2f0ZRVuMsi45IrBgx9zDNGNj165aPA= -github.com/alecthomas/kong v0.9.0/go.mod h1:Y47y5gKfHp1hDc7CH7OeXgLIpp+Q2m1Ni0L5s3bI8Os= -github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= -github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/gabriel-vasile/mimetype v1.4.4 h1:QjV6pZ7/XZ7ryI2KuyeEDE8wnh7fHP9YnQy+R0LnH8I= github.com/gabriel-vasile/mimetype v1.4.4/go.mod h1:JwLei5XPtWdGiMFB5Pjle1oEeoSeEuJfJE+TtfvdB/s= -github.com/go-dataspace/run-dsrpc v0.0.1-alpha1 h1:5AidaX66NMAjjVGjKRm/DMEw+xEySdn5dm8WUzWI73M= -github.com/go-dataspace/run-dsrpc v0.0.1-alpha1/go.mod h1:GS4hV6keaQWCn9KTwMqNgzOdIgPnJ/+NSHSvgrsLbTE= -github.com/go-dataspace/run-dsrpc v0.0.2-alpha1 h1:MMOqPIMZ0Qwor8EQAsLJVQsE46//8zZP0uxz6jh5LWY= -github.com/go-dataspace/run-dsrpc v0.0.2-alpha1/go.mod h1:GS4hV6keaQWCn9KTwMqNgzOdIgPnJ/+NSHSvgrsLbTE= github.com/go-dataspace/run-dsrpc v0.0.3-alpha1 h1:YvzHkw7ew+A7NMxOfwD6jOQ5Ki49BvKmzDhohzyYNPQ= github.com/go-dataspace/run-dsrpc v0.0.3-alpha1/go.mod h1:GS4hV6keaQWCn9KTwMqNgzOdIgPnJ/+NSHSvgrsLbTE= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= @@ -28,28 +25,75 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 h1:pRhl55Yx1eC7BZ1N+BBWwnKaMyD8uC+34TLdndZMAKk= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0/go.mod h1:XKMd7iuf/RGPSMJ/U4HP0zS2Z9Fh8Ps9a+6X26m/tmI= -github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= -github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/justinas/alice v1.2.0 h1:+MHSA/vccVCF4Uq37S42jwlkvI2Xzl7zTPCN5BnZNVo= github.com/justinas/alice v1.2.0/go.mod h1:fN5HRH/reO/zrUflLfTN43t3vXvKzvZIENsNEe7i7qA= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/lmittmann/tint v1.0.5 h1:NQclAutOfYsqs2F1Lenue6OoWCajs5wJcP3DfWVpePw= github.com/lmittmann/tint v1.0.5/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= +github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= github.com/samber/slog-http v1.3.1 h1:Fho8CGX4elTKAXFKCNGloRAz2yWt1WD+vXpO9iylQ9g= github.com/samber/slog-http v1.3.1/go.mod h1:n6h4x2ZBeTgLqMKf95EuNlU6mcJF1b/RVLxo1od5+V0= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= +github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= +github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs= -go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY= -go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg= -go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= +go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= +go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= +go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= +go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= @@ -62,7 +106,11 @@ google.golang.org/grpc v1.64.1 h1:LKtvyfbX3UGVPFcGqJ9ItpVWW6oN/2XqTxfAnwRRXiA= google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/cfg/cobra.go b/internal/cfg/cobra.go new file mode 100644 index 0000000..4286259 --- /dev/null +++ b/internal/cfg/cobra.go @@ -0,0 +1,44 @@ +// Copyright 2024 go-dataspace +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package cfg contains configuration helpers for RUN-DSP. +package cfg + +import ( + "fmt" + + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +// AddPersistentFlag adds a persistent flag `flag“, with default `def“ and usage message `usage`, +// and it will also bind the flag to the viper `configKey`. +func AddPersistentFlag(cmd *cobra.Command, configKey, flag, usage string, def any) { + switch v := def.(type) { + case int: + cmd.PersistentFlags().Int(flag, v, usage) + case string: + cmd.PersistentFlags().String(flag, v, usage) + case bool: + cmd.PersistentFlags().Bool(flag, v, usage) + default: + panic(fmt.Sprintf("Unsupported type: %T", v)) + } + err := viper.BindPFlag(configKey, cmd.PersistentFlags().Lookup(flag)) + if err != nil { + // impossible in this setup + panic(err.Error()) + } + viper.SetDefault(configKey, def) +} diff --git a/internal/cfg/validators.go b/internal/cfg/validators.go new file mode 100644 index 0000000..109cc00 --- /dev/null +++ b/internal/cfg/validators.go @@ -0,0 +1,59 @@ +// Copyright 2024 go-dataspace +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cfg + +import ( + "fmt" + "net" + "os" + "time" +) + +// CheckListenPort checks if it's possible to listen on an address/port combination, +// and will return an error if anything is wrong. +func CheckListenPort(address string, port int) error { + listen, err := net.Listen("tcp", fmt.Sprintf("%s:%d", address, port)) + if err != nil { + return fmt.Errorf("can't listen on %s:%d: %w", address, port, err) + } + defer listen.Close() + return nil +} + +// CheckConnectAddr checks if it's possible to connect to a TCP address, +// and will return an error if anything is wrong. +func CheckConnectAddr(address string) error { + d := net.Dialer{Timeout: 1 * time.Second} + conn, err := d.Dial("tcp", address) + if err != nil { + return fmt.Errorf("can't connect to %s: %w", address, err) + } + defer conn.Close() + return nil +} + +// CheckFilesExist checks if the given files exist, and are not directories. +func CheckFilesExist(files ...string) error { + for _, f := range files { + s, err := os.Stat(f) + if err != nil { + return fmt.Errorf("could not read %s: %w", f, err) + } + if s.IsDir() { + return fmt.Errorf("%s is a directory", f) + } + } + return nil +} diff --git a/internal/cli/global.go b/internal/cli/global.go deleted file mode 100644 index e071663..0000000 --- a/internal/cli/global.go +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2024 go-dataspace -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package cli contains utilities for building the CLI -package cli - -type GlobalOptions struct { - Debug bool - LogLevel string `help:"Set log level." enum:"debug,info,warn,error" default:"info" env:"LOGLEVEL"` -} diff --git a/internal/cli/params.go b/internal/cli/params.go deleted file mode 100644 index 469a750..0000000 --- a/internal/cli/params.go +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2024 go-dataspace -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cli - -import ( - "context" - "log/slog" - - "github.com/go-dataspace/run-dsp/logging" -) - -// Params is a simple interface for passing parameters to subcommands. -type Params interface { - Debug() bool - Context() context.Context -} - -// ContreteParams are parameters for subcommands, they include global options and things like -// loggers, contexts, etc. -type ConcreteParams struct { - logger *slog.Logger - debug bool - ctx context.Context -} - -// GenParams generates a new Params object based on the global options. -func GenParams(g GlobalOptions) *ConcreteParams { - ctx := context.Background() - logLevel := g.LogLevel - humanReadable := false - if g.Debug { - logLevel = "debug" - humanReadable = true - } - return &ConcreteParams{ - ctx: logging.Inject(ctx, logging.NewJSON(logLevel, humanReadable)), - debug: g.Debug, - } -} - -// Logger returns the logger. -func (p *ConcreteParams) Logger() *slog.Logger { - return p.logger -} - -// Debug returns the debug value. -func (p *ConcreteParams) Debug() bool { - return p.debug -} - -// Context returns the context. -func (p *ConcreteParams) Context() context.Context { - return p.ctx -} diff --git a/internal/server/command.go b/internal/server/command.go index 562767e..a0c09a8 100644 --- a/internal/server/command.go +++ b/internal/server/command.go @@ -34,12 +34,14 @@ import ( "github.com/go-dataspace/run-dsp/dsp/shared" "github.com/go-dataspace/run-dsp/dsp/statemachine" "github.com/go-dataspace/run-dsp/internal/authforwarder" - "github.com/go-dataspace/run-dsp/internal/cli" + "github.com/go-dataspace/run-dsp/internal/cfg" "github.com/go-dataspace/run-dsp/internal/constants" "github.com/go-dataspace/run-dsp/logging" providerv1 "github.com/go-dataspace/run-dsrpc/gen/go/dsp/v1alpha1" grpclog "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging" "github.com/justinas/alice" + "github.com/spf13/cobra" + "github.com/spf13/viper" "google.golang.org/grpc" "google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials/insecure" @@ -47,129 +49,167 @@ import ( sloghttp "github.com/samber/slog-http" ) -// Command contains all the server parameters. -// -//nolint:lll -type Command struct { - ListenAddr string `help:"Listen address" default:"0.0.0.0" env:"LISTEN_ADDR"` - Port int `help:"Listen port" default:"8080" env:"PORT"` - - ExternalURL *url.URL `help:"URL that RUN-DSP uses in the dataspace." required:"" env:"EXTERNAL_URL"` - - // GRPC settings for the provider - ProviderAddress string `help:"Address of provider GRPC endpoint" required:"" env:"PROVIDER_URL"` - ProviderInsecure bool `help:"Provider connection does not use TLS" default:"false" env:"PROVIDER_INSECURE"` - ProviderCACert string `help:"Custom CA certificate for provider's TLS certificate" env:"PROVIDER_CA"` - ProviderClientCert string `help:"Client certificate to use to authenticate with provider" env:"PROVIDER_CLIENT_CERT"` - ProviderClientCertKey string `help:"Key to the client certificate" env:"PROVIDER_CLIENT_CERT_KEY"` +// init initialises all the flags for the command. +func init() { + cfg.AddPersistentFlag( + Command, "server.dsp.address", "dsp-address", "address to listen on for dataspace operations", "0.0.0.0") + cfg.AddPersistentFlag( + Command, "server.dsp.port", "dsp-port", "port to listen on for dataspace operations", 8080) + cfg.AddPersistentFlag( + Command, + "server.dsp.externalURL", + "external-url", + "URL that the dataspace service is reachable by from the dataspace", + "", + ) - // GRPC control interface settings. - ControlEnabled bool `help:"Enable to GRPC control interface" default:"false" env:"CONTROL_ENABLED"` - ControlListenAddr string `help:"Listen address for the control interface" default:"0.0.0.0" env:"CONTROL_ADDR"` - ControlPort int `help:"Port for the control interface" default:"8081" env:"CONTROL_PORT"` - ControlInsecure bool `help:"Disable TLS for the control interface" default:"false" env:"CONTROL_INSECURE"` - ControlCert string `help:"Certificate to use for the control interface" env:"CONTROL_CERT"` - ControlCertKey string `help:"Key to the control interface" env:"CONTROL_CERT_KEY"` - ControlVerifyClientCertificates bool `help:"Require validated client certificates to connect to control interface" default:"false" env:"CONTROL_VERIFY_CLIENT_CERTIFICATES" ` - ControlClientCACert string `help:"Custom CA certificate to verify client certificates with" env:"CONTROL_PROVIDER_CA"` + cfg.AddPersistentFlag( + Command, "server.provider.address", "provider-address", "Address of the provider gRPC endpoint", "") + cfg.AddPersistentFlag( + Command, "server.provider.insecure", "provider-insecure", "Disable TLS when connecting to provider", false) + cfg.AddPersistentFlag( + Command, "server.provider.caCert", "provider-ca-cert", "CA certificate of provider cert issuer", "") + cfg.AddPersistentFlag( + Command, "server.provider.clientCert", "provider-client-cert", "Client certificate to use with provider", "") + cfg.AddPersistentFlag( + Command, "server.provider.clientCertKey", "provider-client-cert-key", "Key for client certificate", "") + + cfg.AddPersistentFlag(Command, "server.control.enabled", "control-enabled", "enable gRPC control service", false) + cfg.AddPersistentFlag( + Command, "server.control.address", "control-address", "address for the control service to listen on", "0.0.0.0") + cfg.AddPersistentFlag( + Command, "server.control.port", "control-port", "port for the control service to listen on", 8081) + cfg.AddPersistentFlag( + Command, "server.control.insecure", "control-insecure", "disable TLS for the control service", false) + cfg.AddPersistentFlag( + Command, "server.control.cert", "control-cert", "TLS certificate for the control service", "") + cfg.AddPersistentFlag( + Command, "server.control.certKey", "control-cert-key", "Key for control service certificate", "") + cfg.AddPersistentFlag( + Command, + "server.control.verifyClientCerts", + "control-verify-client-certs", + "Require CA issued client certificates", + false, + ) + cfg.AddPersistentFlag( + Command, "server.control.clientCACert", "control-client-ca-cert", "CA certificate of client cert issuer", "") } -// Validate checks if the parameters are correct. -func (c *Command) Validate() error { - if c.ExternalURL == nil { - u, err := url.Parse(fmt.Sprintf("http://%s:%d", c.ListenAddr, c.Port)) +// Command validates the configuration and then runs the server. +var Command = &cobra.Command{ + Use: "server", + Short: "Start the RUN-DSP server", + Long: `Starts the RUN-DSP connector, which then connects to the provider and will start + serving dataspace requests`, + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + _, err := url.Parse(viper.GetString("server.dsp.externalURL")) if err != nil { - return fmt.Errorf("broken default URL, see your listen address and port") + return fmt.Errorf("Invalid external URL: %w", err) } - c.ExternalURL = u - } - - if !c.ProviderInsecure { - err := c.validateTLS() + err = cfg.CheckListenPort(viper.GetString("server.dsp.address"), viper.GetInt("server.dsp.port")) if err != nil { return err } - } - if c.ControlEnabled { - return c.validateControl() - } - return nil -} - -func (c *Command) validateControl() error { - if c.ControlInsecure { - return nil - } + err = cfg.CheckConnectAddr(viper.GetString("server.provider.address")) + if err != nil { + return err + } - certSupplied, err := checkFile(c.ControlCert) - if err != nil { - return fmt.Errorf("Control interface certificate: %w", err) - } - keySupplied, err := checkFile(c.ControlCertKey) - if err != nil { - return fmt.Errorf("Control interface certificate key: %w", err) - } + if !viper.GetBool("server.provider.insecure") { + err = cfg.CheckFilesExist( + viper.GetString("server.provider.caCert"), + viper.GetString("server.provider.clientCert"), + viper.GetString("server.provider.clientCertKey"), + ) + if err != nil { + return err + } + } - if !certSupplied { - return fmt.Errorf("Control interface certificate not supplied") - } + if viper.GetBool("server.control.enabled") { + err = cfg.CheckListenPort(viper.GetString("server.control.address"), viper.GetInt("server.control.port")) + if err != nil { + return err + } + if !viper.GetBool("server.control.insecure") { + err = cfg.CheckFilesExist( + viper.GetString("server.control.cert"), + viper.GetString("server.control.certKey"), + ) + if err != nil { + return err + } + if viper.GetBool("server.control.verifyClientCerts") { + err = cfg.CheckFilesExist(viper.GetString("server.control.clientCACert")) + if err != nil { + return err + } + } + } - if !keySupplied { - return fmt.Errorf("Control interface certificate key not supplied") - } + } - if c.ControlVerifyClientCertificates { - caSupplied, err := checkFile(c.ControlClientCACert) + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + u, err := url.Parse(viper.GetString("server.dsp.externalURL")) if err != nil { - return fmt.Errorf("Control interface client CA certificate: %w", err) + panic(err.Error()) } - if !caSupplied { - return fmt.Errorf("Control interface client CA certificate required when using client cert auth") + c := command{ + ListenAddr: viper.GetString("server.dsp.address"), + Port: viper.GetInt("server.dsp.port"), + ExternalURL: u, + ProviderAddress: viper.GetString("server.provider.address"), + ProviderInsecure: viper.GetBool("server.provider.insecure"), + ProviderCACert: viper.GetString("server.provider.caCert"), + ProviderClientCert: viper.GetString("server.provider.clientCert"), + ProviderClientCertKey: viper.GetString("server.provider.clientCert"), + ControlEnabled: viper.GetBool("server.control.enabled"), + ControlListenAddr: viper.GetString("server.control.address"), + ControlPort: viper.GetInt("server.control.port"), + ControlInsecure: viper.GetBool("server.control.insecure"), + ControlCert: viper.GetString("server.control.cert"), + ControlCertKey: viper.GetString("server.control.certKey"), + ControlVerifyClientCertificates: viper.GetBool("server.control.verifyClientCerts"), + ControlClientCACert: viper.GetString("server.control.clientCACert"), } - } - - return nil + ctx, ok := viper.Get("initCTX").(context.Context) + if !ok { + return fmt.Errorf("couldn't fetch initial context") + } + return c.Run(ctx) + }, } -func (c *Command) validateTLS() error { - _, err := checkFile(c.ProviderCACert) - if err != nil { - return fmt.Errorf("Provider CA certificate: %w", err) - } +type command struct { + ListenAddr string + Port int - certSupplied, err := checkFile(c.ProviderClientCert) - if err != nil { - return fmt.Errorf("Provider client certificate: %w", err) - } - keySupplied, err := checkFile(c.ProviderClientCertKey) - if err != nil { - return fmt.Errorf("Provider client certificate key: %w", err) - } + ExternalURL *url.URL - if certSupplied != keySupplied { - return fmt.Errorf("Need to supply both client certificate and its key.") - } - return nil -} + // GRPC settings for the provider + ProviderAddress string + ProviderInsecure bool + ProviderCACert string + ProviderClientCert string + ProviderClientCertKey string -func checkFile(l string) (bool, error) { - if l != "" { - f, err := os.Stat(l) - if err != nil { - return true, fmt.Errorf("could not read %s: %w", l, err) - } - if f.IsDir() { - return true, fmt.Errorf("%s is a directory", l) - } - return true, nil - } - return false, nil + // GRPC control interface settings. + ControlEnabled bool + ControlListenAddr string + ControlPort int + ControlInsecure bool + ControlCert string + ControlCertKey string + ControlVerifyClientCertificates bool + ControlClientCACert string } // Run starts the server. -func (c *Command) Run(p cli.Params) error { - ctx := p.Context() +func (c *command) Run(ctx context.Context) error { wg := &sync.WaitGroup{} logger := logging.Extract(ctx) @@ -231,7 +271,7 @@ func (c *Command) Run(p cli.Params) error { return srv.ListenAndServe() } -func (c *Command) startControl( +func (c *command) startControl( ctx context.Context, wg *sync.WaitGroup, controlSVC *control.Server, ) error { @@ -287,7 +327,7 @@ func (c *Command) startControl( return nil } -func (c *Command) loadControlTLSCredentials() (credentials.TransportCredentials, error) { +func (c *command) loadControlTLSCredentials() (credentials.TransportCredentials, error) { if c.ControlInsecure { return insecure.NewCredentials(), nil } @@ -319,7 +359,7 @@ func (c *Command) loadControlTLSCredentials() (credentials.TransportCredentials, return credentials.NewTLS(config), nil } -func (c *Command) getProvider(ctx context.Context) (providerv1.ProviderServiceClient, *grpc.ClientConn, error) { +func (c *command) getProvider(ctx context.Context) (providerv1.ProviderServiceClient, *grpc.ClientConn, error) { logger := logging.Extract(ctx) tlsCredentials, err := c.loadTLSCredentials() if err != nil { @@ -349,7 +389,7 @@ func (c *Command) getProvider(ctx context.Context) (providerv1.ProviderServiceCl return provider, conn, nil } -func (c *Command) loadTLSCredentials() (credentials.TransportCredentials, error) { +func (c *command) loadTLSCredentials() (credentials.TransportCredentials, error) { if c.ProviderInsecure { return insecure.NewCredentials(), nil }