From 8a281f593a3c549133a2afe2140c3c3860958694 Mon Sep 17 00:00:00 2001 From: roos Date: Fri, 12 Oct 2018 16:26:11 +0200 Subject: [PATCH] lib: Usage sample for lib/env programs --- go/lib/env/sample.go | 74 +++++++++++++++ go/path_srv/internal/psconfig/sample.go | 80 +++++++++++++++++ go/path_srv/internal/psconfig/sample_test.go | 63 +++++++++++++ go/path_srv/main.go | 14 +-- go/sciond/internal/sdconfig/sample.go | 94 ++++++++++++++++++++ go/sciond/internal/sdconfig/sample_test.go | 65 ++++++++++++++ go/sciond/main.go | 20 +++-- 7 files changed, 396 insertions(+), 14 deletions(-) create mode 100644 go/lib/env/sample.go create mode 100644 go/path_srv/internal/psconfig/sample.go create mode 100644 go/path_srv/internal/psconfig/sample_test.go create mode 100644 go/sciond/internal/sdconfig/sample.go create mode 100644 go/sciond/internal/sdconfig/sample_test.go diff --git a/go/lib/env/sample.go b/go/lib/env/sample.go new file mode 100644 index 0000000000..b9ce9aeb0a --- /dev/null +++ b/go/lib/env/sample.go @@ -0,0 +1,74 @@ +// Copyright 2018 Anapaya Systems +// +// 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 +// +// http://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 env contains common command line and initialization code for SCION services. +// If something is specific to one app, it should go into that app's code and not here. +// +// During initialization, SIGHUPs are masked. To call a function on each +// SIGHUP, pass the function when calling Init. +package env + +import ( + "flag" + "fmt" + "io/ioutil" + "os" +) + +// ConfigFlag adds a config flag. +func ConfigFlag() *string { + return flag.String("config", "", "Service TOML config file (required)") +} + +// SampleFlag adds a sample flag. +func SampleFlag() *string { + return flag.String("sample", "", + "Filename for creating a sample config. If set, the service is not started.") +} + +// Usage returns a usage function based on the sample config. +func Usage(sampleConfig string) func() { + return func() { + fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) + flag.PrintDefaults() + fmt.Fprintf(os.Stderr, "\nSample config file:\n%s", sampleConfig) + } +} + +// CheckFlags checks whether the config flag has been provided. In case it +// is not provided and the sample flag is not set, the usage message is printed. +// If the sample flag is set, a sample config is written to the specified file. +// The first return value is the return code of the program. The second value +// indicates whether the program can continue with its execution or should exit. +func CheckFlags(flagConfig, flagSample, sampleConfig string) (int, bool) { + if flagConfig == "" { + if flagSample != "" { + return writeSample(flagSample, sampleConfig), false + } + fmt.Fprintln(os.Stderr, "Missing config file") + flag.Usage() + return 1, false + } + return 0, true +} + +func writeSample(flagSample, sampleConfig string) int { + if err := ioutil.WriteFile(flagSample, []byte(sampleConfig), 0666); err != nil { + fmt.Fprintln(os.Stderr, "Unable to write sample: "+err.Error()) + flag.Usage() + return 1 + } + fmt.Fprintln(os.Stdout, "Sample file written to: "+flagSample) + return 0 +} diff --git a/go/path_srv/internal/psconfig/sample.go b/go/path_srv/internal/psconfig/sample.go new file mode 100644 index 0000000000..31bd1697ca --- /dev/null +++ b/go/path_srv/internal/psconfig/sample.go @@ -0,0 +1,80 @@ +// Copyright 2018 Anapaya Systems +// +// 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 +// +// http://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 psconfig + +const Sample = `[general] + # The ID of the service. This is used to choose the relevant portion of the + # topology file for some services. + ID = "ps1-ff00_0_110-1" + + # Directory for loading AS information, certs, keys, path policy, topology. + ConfigDir = "gen/ISD1/ASff00_0_110/ps1-ff00_0_110-1" + + # Topology file. If not specified, topology.json is loaded from the config + # directory. + # Topology = "gen/ISD1/ASff00_0_110/ps1-ff00_0_110-1/topology.json" + + # ReconnectToDispatcher can be set to true to enable the snetproxy reconnecter. + # ReconnectToDispatcher = true + +[logging] + [logging.file] + # Location of the logging file. + Path = "logs/ps1-ff00_0_110-1.log" + + # File logging level (trace|debug|info|warn|error|crit) (default debug) + Level = "debug" + + # Max size of log file in MiB (default 50) + # Size = 50 + + # Max age of log file in days (default 7) + # MaxAge = 7 + + # How frequently to flush to the log file, in seconds. If 0, all messages + # are immediately flushed. If negative, messages are never flushed + # automatically. (default 5) + FlushInterval = 10 + [logging.console] + # Console logging level (trace|debug|info|warn|error|crit) (default crit) + Level = "warn" + +[metrics] + # The address to export prometheus metrics on. If not set, metrics are not + # exported. + # Prometheus = "127.0.0.1:8000" + +[infra] + # Node type. + Type = "PS" + +[trust] + # Database for trust information. If a file already exists, it is treated as + # initial trust information. If a file does not exist, it is created from the + # initial information found under ConfigDir/certs. + TrustDB = "gen-cache/ps1-ff00_0_110-1.trust.db" + +[ps] + # Path to the path database + PathDB = "gen-cache/ps1-ff00_0_110-1.path.db" + + # Enable the "old" replication of down segments between cores using SegSync + # messages (default false) + SegSync = false + + # The time after which segments for a destination are refetched. (default 5m) + QueryInterval = "5m" + +` diff --git a/go/path_srv/internal/psconfig/sample_test.go b/go/path_srv/internal/psconfig/sample_test.go new file mode 100644 index 0000000000..07d2b51f56 --- /dev/null +++ b/go/path_srv/internal/psconfig/sample_test.go @@ -0,0 +1,63 @@ +// Copyright 2018 Anapaya Systems +// +// 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 +// +// http://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 psconfig + +import ( + "testing" + "time" + + "github.com/BurntSushi/toml" + . "github.com/smartystreets/goconvey/convey" + + "github.com/scionproto/scion/go/lib/common" + "github.com/scionproto/scion/go/lib/env" +) + +type TestConfig struct { + General env.General + Logging env.Logging + Metrics env.Metrics + Infra env.Infra + Trust env.Trust + PS Config +} + +func TestSampleCorrect(t *testing.T) { + Convey("Load", t, func() { + var cfg TestConfig + // Make sure SegSync is set. + cfg.PS.SegSync = true + _, err := toml.Decode(Sample, &cfg) + SoMsg("err", err, ShouldBeNil) + + // Non-psconfig specific + SoMsg("ID correct", cfg.General.ID, ShouldEqual, "ps1-ff00_0_110-1") + SoMsg("ConfigDir correct", cfg.General.ConfigDir, ShouldEqual, + "gen/ISD1/ASff00_0_110/ps1-ff00_0_110-1") + SoMsg("LogFile correct", cfg.Logging.File.Path, ShouldEqual, "logs/ps1-ff00_0_110-1.log") + SoMsg("LogFile correct", cfg.Logging.File.Path, ShouldEqual, "logs/ps1-ff00_0_110-1.log") + SoMsg("LogLvl correct", cfg.Logging.File.Level, ShouldEqual, "debug") + SoMsg("LogFlush correct", *cfg.Logging.File.FlushInterval, ShouldEqual, 10) + SoMsg("LogConsoleLvl correct", cfg.Logging.Console.Level, ShouldEqual, "warn") + SoMsg("Infra correct", cfg.Infra.Type, ShouldEqual, common.PS) + SoMsg("TrustDB correct", cfg.Trust.TrustDB, ShouldEqual, + "gen-cache/ps1-ff00_0_110-1.trust.db") + + // psconfig specific + SoMsg("PathDB correct", cfg.PS.PathDB, ShouldEqual, "gen-cache/ps1-ff00_0_110-1.path.db") + SoMsg("SegSync set", cfg.PS.SegSync, ShouldBeFalse) + SoMsg("QueryInterval correct", cfg.PS.QueryInterval.Duration, ShouldEqual, 5*time.Minute) + }) +} diff --git a/go/path_srv/main.go b/go/path_srv/main.go index da65d509a0..2cc4468f28 100644 --- a/go/path_srv/main.go +++ b/go/path_srv/main.go @@ -52,11 +52,17 @@ type Config struct { } var ( + flagConfig = env.ConfigFlag() + flagSample = env.SampleFlag() + config Config environment *env.Env - flagConfig = flag.String("config", "", "Service TOML config file (required)") ) +func init() { + flag.Usage = env.Usage(psconfig.Sample) +} + // main initializes the path server and starts the dispatcher. func main() { os.Exit(realMain()) @@ -64,10 +70,8 @@ func main() { func realMain() int { flag.Parse() - if *flagConfig == "" { - fmt.Fprintln(os.Stderr, "Missing config file") - flag.Usage() - return 1 + if v, ok := env.CheckFlags(*flagConfig, *flagSample, psconfig.Sample); !ok { + return v } if err := setup(*flagConfig); err != nil { fmt.Fprintln(os.Stderr, err) diff --git a/go/sciond/internal/sdconfig/sample.go b/go/sciond/internal/sdconfig/sample.go new file mode 100644 index 0000000000..c5f6846fa0 --- /dev/null +++ b/go/sciond/internal/sdconfig/sample.go @@ -0,0 +1,94 @@ +// Copyright 2018 Anapaya Systems +// +// 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 +// +// http://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 sdconfig + +const Sample = `[general] + # The ID of the service. + ID = "sd1-ff00_0_110" + + # Directory for loading AS information, certs, keys, path policy, topology. + ConfigDir = "gen/ISD1/ASff00_0_110/endhost" + + # Topology file. If not specified, topology.json is loaded from the config + # directory. + # Topology = "gen/ISD1/ASff00_0_110/endhost/topology.json" + + # ReconnectToDispatcher can be set to true to enable the snetproxy reconnecter. + # ReconnectToDispatcher = true + +[logging] + [logging.file] + # Location of the logging file. + Path = "logs/sd1-ff00_0_110.log" + + # File logging level (trace|debug|info|warn|error|crit) (default debug) + Level = "debug" + + # Max size of log file in MiB (default 50) + # Size = 50 + + # Max age of log file in days (default 7) + # MaxAge = 7 + + # How frequently to flush to the log file, in seconds. If 0, all messages + # are immediately flushed. If negative, messages are never flushed + # automatically. (default 5) + FlushInterval = 10 + [logging.console] + # Console logging level (trace|debug|info|warn|error|crit) (default crit) + Level = "warn" + +[metrics] + # The address to export prometheus metrics on. If not set, metrics are not + # exported. + # Prometheus = "127.0.0.1:8000" + +[infra] + # Node type. + Type = "SD" + +[trust] + # Database for trust information. If a file already exists, it is treated as + # initial trust information. If a file does not exist, it is created from the + # initial information found under ConfigDir/certs. + TrustDB = "gen-cache/sd1-ff00_0_110.trust.db" + +[sd] + # Path to the path database. + PathDB = "gen-cache/sd1-ff00_0_110.path.db" + + # Address to listen on via the reliable socket protocol. If empty, + # a reliable socket server on the default socket is started. + Reliable = "/run/shm/sciond/default.sock" + + # Address to listen on for normal unixgram messages. If empty, a + # unixgram server on the default socket is started. + Unix = "/run/shm/sciond/default-unix.sock" + + # If set to True, the socket is removed before being created. (default false) + DeleteSocket = false + + # Local address to listen on for SCION messages (if Bind is not set), + # and to send out messages to other nodes. + Public = "1-ff00:0:110,[127.0.0.1]:0" + + # If set, Bind is the preferred local address to listen on for SCION + # messages. + # Bind = "1-ff00:0:110,[127.0.0.1]:0" + + # The time after which segments for a destination are refetched. (default 5m) + QueryInterval = "5m" + +` diff --git a/go/sciond/internal/sdconfig/sample_test.go b/go/sciond/internal/sdconfig/sample_test.go new file mode 100644 index 0000000000..7e11cbfff6 --- /dev/null +++ b/go/sciond/internal/sdconfig/sample_test.go @@ -0,0 +1,65 @@ +// Copyright 2018 Anapaya Systems +// +// 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 +// +// http://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 sdconfig + +import ( + "testing" + "time" + + "github.com/BurntSushi/toml" + . "github.com/smartystreets/goconvey/convey" + + "github.com/scionproto/scion/go/lib/env" +) + +type TestConfig struct { + General env.General + Logging env.Logging + Metrics env.Metrics + Infra env.Infra + Trust env.Trust + SD Config +} + +func TestSampleCorrect(t *testing.T) { + Convey("Load", t, func() { + var cfg TestConfig + // Make sure DeleteSocket is set. + cfg.SD.DeleteSocket = true + _, err := toml.Decode(Sample, &cfg) + SoMsg("err", err, ShouldBeNil) + + // Non-psconfig specific + SoMsg("ID correct", cfg.General.ID, ShouldEqual, "sd1-ff00_0_110") + SoMsg("ConfigDir correct", cfg.General.ConfigDir, ShouldEqual, + "gen/ISD1/ASff00_0_110/endhost") + SoMsg("LogFile correct", cfg.Logging.File.Path, ShouldEqual, "logs/sd1-ff00_0_110.log") + SoMsg("LogLvl correct", cfg.Logging.File.Level, ShouldEqual, "debug") + SoMsg("LogFlush correct", *cfg.Logging.File.FlushInterval, ShouldEqual, 10) + SoMsg("LogConsoleLvl correct", cfg.Logging.Console.Level, ShouldEqual, "warn") + SoMsg("Infra correct", cfg.Infra.Type, ShouldEqual, "SD") + SoMsg("TrustDB correct", cfg.Trust.TrustDB, ShouldEqual, + "gen-cache/sd1-ff00_0_110.trust.db") + + // psconfig specific + SoMsg("PathDB correct", cfg.SD.PathDB, ShouldEqual, "gen-cache/sd1-ff00_0_110.path.db") + SoMsg("Reliable correct", cfg.SD.Reliable, ShouldEqual, "/run/shm/sciond/default.sock") + SoMsg("Unix correct", cfg.SD.Unix, ShouldEqual, "/run/shm/sciond/default-unix.sock") + SoMsg("Public correct", cfg.SD.Public.String(), ShouldEqual, + "1-ff00:0:110,[127.0.0.1]:0 (UDP)") + SoMsg("QueryInterval correct", cfg.SD.QueryInterval.Duration, ShouldEqual, 5*time.Minute) + SoMsg("DeleteSocket set", cfg.SD.DeleteSocket, ShouldBeFalse) + }) +} diff --git a/go/sciond/main.go b/go/sciond/main.go index 32aa9db5b3..afa9134819 100644 --- a/go/sciond/main.go +++ b/go/sciond/main.go @@ -57,24 +57,26 @@ type Config struct { SD sdconfig.Config } -var config Config - -var environment *env.Env - var ( - flagConfig = flag.String("config", "", "Service TOML config file (required)") + flagConfig = env.ConfigFlag() + flagSample = env.SampleFlag() + + config Config + environment *env.Env ) +func init() { + flag.Usage = env.Usage(sdconfig.Sample) +} + func main() { os.Exit(realMain()) } func realMain() int { flag.Parse() - if *flagConfig == "" { - fmt.Fprintln(os.Stderr, "Missing config file") - flag.Usage() - return 1 + if v, ok := env.CheckFlags(*flagConfig, *flagSample, sdconfig.Sample); !ok { + return v } if err := Init(*flagConfig); err != nil { fmt.Fprintln(os.Stderr, err)