From 17fea9ec62ca8bbf0cdd4d7f2ad79ca41d95a505 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Tue, 13 Jun 2023 18:18:48 +0200 Subject: [PATCH 01/76] add new config --- cmd/revad/internal/config/config.go | 44 +++++-- cmd/revad/internal/config/config_test.go | 140 +++++++++++++++++++++++ 2 files changed, 173 insertions(+), 11 deletions(-) create mode 100644 cmd/revad/internal/config/config_test.go diff --git a/cmd/revad/internal/config/config.go b/cmd/revad/internal/config/config.go index 9ece6f8c1e..be439320b8 100644 --- a/cmd/revad/internal/config/config.go +++ b/cmd/revad/internal/config/config.go @@ -20,25 +20,47 @@ package config import ( "io" + "reflect" "github.com/BurntSushi/toml" "github.com/pkg/errors" ) -// Read reads the configuration from the reader. -func Read(r io.Reader) (map[string]interface{}, error) { - data, err := io.ReadAll(r) - if err != nil { - err = errors.Wrap(err, "config: error reading from reader") +type Config struct { + raw map[string]any + + GRPC *GRPC `key:"grpc"` + // Serverless *Serverless // TODO + + // TODO: add log, shared, core +} + +type Serverless struct { +} + +// Load loads the configuration from the reader. +func Load(r io.Reader) (*Config, error) { + var c Config + if _, err := toml.NewDecoder(r).Decode(&c.raw); err != nil { + return nil, errors.Wrap(err, "config: error decoding toml data") + } + if err := c.parse(); err != nil { return nil, err } + return &c, nil +} - v := map[string]interface{}{} - err = toml.Unmarshal(data, &v) - if err != nil { - err = errors.Wrap(err, "config: error decoding toml data") - return nil, err +func (c *Config) parse() error { + if err := c.parseGRPC(); err != nil { + return err } + return nil +} + +func (c *Config) ApplyTemplates() error { + return nil +} - return v, nil +func (c *Config) lookup(key string) (any, error) { + return lookupStruct(key, reflect.ValueOf(c)) } diff --git a/cmd/revad/internal/config/config_test.go b/cmd/revad/internal/config/config_test.go new file mode 100644 index 0000000000..c9ece391a6 --- /dev/null +++ b/cmd/revad/internal/config/config_test.go @@ -0,0 +1,140 @@ +package config + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestLoadGlobalGRPCAddress(t *testing.T) { + config := ` +[grpc] +address = "localhost:9142" + +[[grpc.services.authprovider]] +driver = "demo" +address = "localhost:9000" + +[grpc.services.authprovider.drivers.demo] +key = "value" + +[[grpc.services.authprovider]] +driver = "machine" +address = "localhost:9001" + +[grpc.services.authprovider.drivers.machine] +key = "value" + +[grpc.services.gateway] +something = "test"` + + c, err := Load(strings.NewReader(config)) + if err != nil { + t.Fatalf("not expected error: %v", err) + } + + assert.Equal(t, "localhost:9142", c.GRPC.Address) + + exp := map[string]ServicesConfig{ + "authprovider": []*DriverConfig{ + { + Address: "localhost:9000", + Config: map[string]any{ + "driver": "demo", + "drivers": map[string]any{ + "demo": map[string]any{ + "key": "value", + }, + }, + "address": "localhost:9000", + }, + }, + { + Address: "localhost:9001", + Config: map[string]any{ + "driver": "machine", + "address": "localhost:9001", + "drivers": map[string]any{ + "machine": map[string]any{ + "key": "value", + }, + }, + }, + }, + }, + "gateway": []*DriverConfig{ + { + Address: "localhost:9142", + Config: map[string]any{ + "something": "test", + }, + }, + }, + } + assert.Equal(t, exp, c.GRPC.services) +} + +func TestLoadNoGRPCDefaultAddress(t *testing.T) { + config := ` +[[grpc.services.authprovider]] +driver = "demo" +address = "localhost:9000" + +[grpc.services.authprovider.drivers.demo] +key = "value" + +[[grpc.services.authprovider]] +driver = "machine" +address = "localhost:9001" + +[grpc.services.authprovider.drivers.machine] +key = "value" + +[grpc.services.gateway] +something = "test"` + + c, err := Load(strings.NewReader(config)) + if err != nil { + t.Fatalf("not expected error: %v", err) + } + + assert.Equal(t, "", c.GRPC.Address) + + exp := map[string]ServicesConfig{ + "authprovider": []*DriverConfig{ + { + Address: "localhost:9000", + Config: map[string]any{ + "driver": "demo", + "drivers": map[string]any{ + "demo": map[string]any{ + "key": "value", + }, + }, + "address": "localhost:9000", + }, + }, + { + Address: "localhost:9001", + Config: map[string]any{ + "driver": "machine", + "address": "localhost:9001", + "drivers": map[string]any{ + "machine": map[string]any{ + "key": "value", + }, + }, + }, + }, + }, + "gateway": []*DriverConfig{ + { + Config: map[string]any{ + "something": "test", + }, + }, + }, + } + assert.Equal(t, exp, c.GRPC.services) +} From f3b466eb09103164f04df76b706dfdb101dacaa4 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Tue, 13 Jun 2023 18:19:15 +0200 Subject: [PATCH 02/76] add helper functions to parse template string --- cmd/revad/internal/config/parser.go | 82 ++++++++++++++++++++++++ cmd/revad/internal/config/parser_test.go | 79 +++++++++++++++++++++++ 2 files changed, 161 insertions(+) create mode 100644 cmd/revad/internal/config/parser.go create mode 100644 cmd/revad/internal/config/parser_test.go diff --git a/cmd/revad/internal/config/parser.go b/cmd/revad/internal/config/parser.go new file mode 100644 index 0000000000..5b764d6826 --- /dev/null +++ b/cmd/revad/internal/config/parser.go @@ -0,0 +1,82 @@ +package config + +import ( + "io" + "strconv" + "strings" + + "github.com/pkg/errors" +) + +type Command interface { + isCommand() +} + +type FieldByKey struct { + Key string +} + +func (FieldByKey) isCommand() {} + +type FieldByIndex struct { + Index int +} + +func (FieldByIndex) isCommand() {} + +func parseNext(key string) (Command, string, error) { + // key = ".grpc.services.authprovider[1].address" + + key = strings.TrimSpace(key) + + // first character must be either "." or "[" + // unless the key is empty + if key == "" { + return nil, "", io.EOF + } + + switch { + case strings.HasPrefix(key, "."): + tkn, next := split(key) + return FieldByKey{Key: tkn}, next, nil + case strings.HasPrefix(key, "["): + tkn, next := split(key) + index, err := strconv.ParseInt(tkn, 10, 64) + if err != nil { + return nil, "", errors.Wrap(err, "parsing error:") + } + return FieldByIndex{Index: int(index)}, next, nil + } + + return nil, "", errors.New("parsing error: operator not recognised") +} + +func split(key string) (token string, next string) { + // key = ".grpc.services.authprovider[1].address" + // -> grpc + // key = "[].address" + // -> + if key == "" { + return + } + + i := -1 + s := key[0] + key = key[1:] + + switch s { + case '.': + i = strings.IndexAny(key, ".[") + case '[': + i = strings.IndexByte(key, ']') + } + + if i == -1 { + return key, "" + } + + if key[i] == ']' { + return key[:i], key[i+1:] + } + return key[:i], key[i:] +} diff --git a/cmd/revad/internal/config/parser_test.go b/cmd/revad/internal/config/parser_test.go new file mode 100644 index 0000000000..3c242ae8b8 --- /dev/null +++ b/cmd/revad/internal/config/parser_test.go @@ -0,0 +1,79 @@ +package config + +import ( + "reflect" + "testing" + + "github.com/gdexlab/go-render/render" +) + +func TestSplit(t *testing.T) { + tests := []struct { + key string + exptoken string + expnext string + }{ + { + key: ".grpc.services.authprovider[1].address", + exptoken: "grpc", + expnext: ".services.authprovider[1].address", + }, + { + key: "[1].address", + exptoken: "1", + expnext: ".address", + }, + { + key: "[100].address", + exptoken: "100", + expnext: ".address", + }, + { + key: "", + }, + } + + for _, tt := range tests { + token, next := split(tt.key) + if token != tt.exptoken || next != tt.expnext { + t.Fatalf("unexpected result: token=%s exp=%s | next=%s exp=%s", token, tt.exptoken, next, tt.expnext) + } + } +} + +func TestParseNext(t *testing.T) { + tests := []struct { + key string + expcmd Command + expnext string + experr error + }{ + { + key: ".grpc.services.authprovider[1].address", + expcmd: FieldByKey{Key: "grpc"}, + expnext: ".services.authprovider[1].address", + }, + { + key: ".authprovider[1].address", + expcmd: FieldByKey{Key: "authprovider"}, + expnext: "[1].address", + }, + { + key: "[1].authprovider.address", + expcmd: FieldByIndex{Index: 1}, + expnext: ".authprovider.address", + }, + { + key: ".authprovider", + expcmd: FieldByKey{Key: "authprovider"}, + expnext: "", + }, + } + + for _, tt := range tests { + cmd, next, err := parseNext(tt.key) + if err != tt.experr || !reflect.DeepEqual(cmd, tt.expcmd) || next != tt.expnext { + t.Fatalf("unexpected result: err=%v exp=%v | cmd=%s exp=%s | next=%s exp=%s", err, tt.experr, render.AsCode(cmd), render.AsCode(tt.expcmd), next, tt.expnext) + } + } +} From bb3d3feaffbc3a1f495d82b956b0ca9b76789319 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Tue, 13 Jun 2023 18:19:57 +0200 Subject: [PATCH 03/76] add helper functions to get values from a key expression --- cmd/revad/internal/config/lookup.go | 163 +++++++++++++++++++++++ cmd/revad/internal/config/lookup_test.go | 134 +++++++++++++++++++ 2 files changed, 297 insertions(+) create mode 100644 cmd/revad/internal/config/lookup.go create mode 100644 cmd/revad/internal/config/lookup_test.go diff --git a/cmd/revad/internal/config/lookup.go b/cmd/revad/internal/config/lookup.go new file mode 100644 index 0000000000..531befbef0 --- /dev/null +++ b/cmd/revad/internal/config/lookup.go @@ -0,0 +1,163 @@ +package config + +import ( + "io" + "reflect" + + "github.com/pkg/errors" +) + +var ErrKeyNotFound = errors.New("key not found") + +func lookupStruct(key string, v reflect.Value) (any, error) { + v = resolveRef(v) + + if v.Kind() != reflect.Struct { + panic("called lookupStruct on non struct type") + } + + cmd, next, err := parseNext(key) + if errors.Is(err, io.EOF) { + return v.Interface(), nil + } + if err != nil { + return nil, err + } + + c, ok := cmd.(FieldByKey) + if !ok { + return nil, errors.New("call of index on struct type") + } + + t := v.Type() + for i := 0; i < v.NumField(); i++ { + val := v.Field(i) + field := t.Field(i) + + tag := field.Tag.Get("key") + if tag == "" { + continue + } + + if tag != c.Key { + continue + } + + return lookupByType(next, val) + } + return nil, ErrKeyNotFound +} + +func lookupByType(key string, v reflect.Value) (any, error) { + v = resolveRef(v) + switch v.Kind() { + case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, + reflect.Float32, reflect.Float64, reflect.String: + return lookupPrimitive(key, v) + case reflect.Array, reflect.Slice: + return lookupList(key, v) + case reflect.Struct: + return lookupStruct(key, v) + case reflect.Map: + return lookupMap(key, v) + case reflect.Interface: + return lookupByType(key, v.Elem()) + } + panic("type not supported: " + v.Kind().String()) +} + +func lookupMap(key string, v reflect.Value) (any, error) { + v = resolveRef(v) + + if v.Kind() != reflect.Map { + panic("called lookupMap on non map type") + } + + cmd, next, err := parseNext(key) + if errors.Is(err, io.EOF) { + return v.Interface(), nil + } + if err != nil { + return nil, err + } + + c, ok := cmd.(FieldByKey) + if !ok { + return nil, errors.New("call of index on map type") + } + + // lookup elemen in the map + el := v.MapIndex(reflect.ValueOf(c.Key)) + if !el.IsValid() { + return nil, ErrKeyNotFound + } + + return lookupByType(next, el) +} + +func lookupList(key string, v reflect.Value) (any, error) { + v = resolveRef(v) + + if v.Kind() != reflect.Slice && v.Kind() != reflect.Array { + panic("called lookupList on non array/slice type") + } + + cmd, next, err := parseNext(key) + if errors.Is(err, io.EOF) { + return v.Interface(), nil + } + if err != nil { + return nil, err + } + + var el reflect.Value + switch c := cmd.(type) { + case FieldByIndex: + if c.Index < 0 || c.Index >= v.Len() { + return nil, errors.New("list index out of range") + } + el = v.Index(c.Index) + case FieldByKey: + // only allowed if the list contains only one element + if v.Len() != 1 { + return nil, errors.New("cannot access field by key on a non 1-elem list") + } + el = v.Index(0) + e, err := lookupByType("."+c.Key, el) + if err != nil { + return nil, err + } + el = reflect.ValueOf(e) + } + + return lookupByType(next, el) +} + +func lookupPrimitive(key string, v reflect.Value) (any, error) { + v = resolveRef(v) + if v.Kind() != reflect.Bool && v.Kind() != reflect.Int && v.Kind() != reflect.Int8 && + v.Kind() != reflect.Int16 && v.Kind() != reflect.Int32 && v.Kind() != reflect.Int64 && + v.Kind() != reflect.Uint && v.Kind() != reflect.Uint8 && v.Kind() != reflect.Uint16 && + v.Kind() != reflect.Uint32 && v.Kind() != reflect.Uint64 && v.Kind() != reflect.Float32 && + v.Kind() != reflect.Float64 && v.Kind() != reflect.String { + panic("called lookupPrimitive on non primitive type: " + v.Kind().String()) + } + + _, _, err := parseNext(key) + if errors.Is(err, io.EOF) { + return v.Interface(), nil + } + if err != nil { + return nil, err + } + + return nil, errors.New("cannot address a value of type " + v.Kind().String()) +} + +func resolveRef(v reflect.Value) reflect.Value { + if v.Kind() == reflect.Pointer { + return resolveRef(v.Elem()) + } + return v +} diff --git a/cmd/revad/internal/config/lookup_test.go b/cmd/revad/internal/config/lookup_test.go new file mode 100644 index 0000000000..b745988ed5 --- /dev/null +++ b/cmd/revad/internal/config/lookup_test.go @@ -0,0 +1,134 @@ +package config + +import ( + "reflect" + "testing" +) + +type SimpleStruct struct { + KeyA string `key:"keya"` + KeyB string `key:"keyb"` +} + +type NestedStruct struct { + Nested SimpleStruct `key:"nested"` + Value int `key:"value"` +} + +type StructWithNestedMap struct { + Map map[string]any `key:"map"` +} + +type StructWithNestedList struct { + List []SimpleStruct `key:"list"` +} + +func TestLookupStruct(t *testing.T) { + tests := []struct { + in any + key string + val any + err error + }{ + { + in: SimpleStruct{ + KeyA: "val_a", + KeyB: "val_b", + }, + key: ".keyb", + val: "val_b", + }, + { + in: NestedStruct{ + Nested: SimpleStruct{ + KeyA: "val_a", + KeyB: "val_b", + }, + Value: 10, + }, + key: ".nested.keyb", + val: "val_b", + }, + { + in: NestedStruct{ + Nested: SimpleStruct{ + KeyA: "val_a", + KeyB: "val_b", + }, + Value: 10, + }, + key: ".value", + val: 10, + }, + { + in: StructWithNestedMap{ + Map: map[string]any{ + "key1": "val1", + "key2": "val2", + }, + }, + key: ".map.key1", + val: "val1", + }, + { + in: StructWithNestedList{ + List: []SimpleStruct{ + { + KeyA: "val_a[1]", + KeyB: "val_b[1]", + }, + { + KeyA: "val_a[2]", + KeyB: "val_b[2]", + }, + }, + }, + key: ".list[1].keyb", + val: "val_b[2]", + }, + { + in: StructWithNestedList{ + List: []SimpleStruct{ + { + KeyA: "val_a[1]", + KeyB: "val_b[1]", + }, + }, + }, + key: ".list.keya", + val: "val_a[1]", + }, + { + in: StructWithNestedList{ + List: []SimpleStruct{ + { + KeyA: "val_a[1]", + KeyB: "val_b[1]", + }, + { + KeyA: "val_a[2]", + KeyB: "val_b[2]", + }, + }, + }, + key: ".list[1]", + val: SimpleStruct{ + KeyA: "val_a[2]", + KeyB: "val_b[2]", + }, + }, + } + + for _, tt := range tests { + got, err := lookupStruct(tt.key, reflect.ValueOf(tt.in)) + if err != tt.err { + t.Fatalf("got not expected error: got=%v exp=%v", err, tt.err) + } + if tt.err == nil { + if !reflect.DeepEqual(tt.val, got) { + t.Fatalf("got not expected result. got=%v exp=%v", got, tt.val) + } + } + } + +} From 817a102c6d28a0591cfdedd9e25668cacb63dae5 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Tue, 13 Jun 2023 18:20:13 +0200 Subject: [PATCH 04/76] add grpc config --- cmd/revad/internal/config/grpc.go | 117 ++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 cmd/revad/internal/config/grpc.go diff --git a/cmd/revad/internal/config/grpc.go b/cmd/revad/internal/config/grpc.go new file mode 100644 index 0000000000..cef151fcf1 --- /dev/null +++ b/cmd/revad/internal/config/grpc.go @@ -0,0 +1,117 @@ +package config + +import ( + "fmt" + + "github.com/mitchellh/mapstructure" + "github.com/pkg/errors" +) + +type GRPC struct { + Address string `mapstructure:"address" key:"address"` + + services map[string]ServicesConfig `key:"services"` + middlewares map[string]map[string]any `key:"middlewares"` +} + +type ServicesConfig []*DriverConfig + +func (c ServicesConfig) DriversNumber() int { return len(c) } + +type DriverConfig struct { + Config map[string]any + Address string +} + +func newSvcConfigFromList(l []map[string]any) (ServicesConfig, error) { + var cfg ServicesConfig + for _, c := range l { + cfg = append(cfg, &DriverConfig{Config: c}) + } + return cfg, nil +} + +func newSvcConfigFromMap(m map[string]any) ServicesConfig { + s, _ := newSvcConfigFromList([]map[string]any{m}) + return s +} + +func (c *Config) parseGRPC() error { + cfg, ok := c.raw["grpc"] + if !ok { + return nil + } + var grpc GRPC + if err := mapstructure.Decode(cfg, &grpc); err != nil { + return errors.Wrap(err, "config: error decoding grpc config") + } + + // parse services + svcCfg, ok := cfg.(map[string]any)["services"].(map[string]any) + if !ok { + return errors.New("grpc.services must be a map") + } + + services := make(map[string]ServicesConfig) + for name, cfg := range svcCfg { + // cfg can be a list or a map + cfgLst, ok := cfg.([]map[string]any) + if ok { + s, err := newSvcConfigFromList(cfgLst) + if err != nil { + return err + } + services[name] = s + continue + } + cfgMap, ok := cfg.(map[string]any) + if !ok { + return fmt.Errorf("grpc.services.%s must be a list or a map. got %T", name, cfg) + } + services[name] = newSvcConfigFromMap(cfgMap) + } + grpc.services = services + c.GRPC = &grpc + + for _, c := range grpc.services { + for _, cfg := range c { + cfg.Address = addressForService(grpc.Address, cfg.Config) + } + } + return nil +} + +type Service struct { + Address string + Name string + Config map[string]any + + raw *DriverConfig +} + +func (s *Service) SetAddress(address string) { + s.raw.Address = address +} + +type ServiceFunc func(*Service) + +// ForEach iterates to each service/driver calling the function f. +func (g *GRPC) ForEach(f ServiceFunc) { + for name, c := range g.services { + for _, cfg := range c { + f(&Service{ + raw: cfg, + Address: cfg.Address, + Name: name, + Config: cfg.Config, + }) + } + } +} + +func addressForService(global string, cfg map[string]any) string { + if address, ok := cfg["address"].(string); ok { + return address + } + return global +} From 6012171f0864762c78f0e7f65d280441c09a99c2 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Wed, 14 Jun 2023 10:20:02 +0200 Subject: [PATCH 05/76] add http config and middlewares --- cmd/revad/internal/config/common.go | 133 ++++++++++++++++++++++++++++ cmd/revad/internal/config/config.go | 4 + cmd/revad/internal/config/grpc.go | 106 +++++----------------- cmd/revad/internal/config/http.go | 59 ++++++++++++ 4 files changed, 220 insertions(+), 82 deletions(-) create mode 100644 cmd/revad/internal/config/common.go create mode 100644 cmd/revad/internal/config/http.go diff --git a/cmd/revad/internal/config/common.go b/cmd/revad/internal/config/common.go new file mode 100644 index 0000000000..86f27ffc2c --- /dev/null +++ b/cmd/revad/internal/config/common.go @@ -0,0 +1,133 @@ +package config + +import ( + "fmt" + + "github.com/mitchellh/mapstructure" + "github.com/pkg/errors" +) + +type iterable interface { + services() map[string]ServicesConfig + interceptors() map[string]map[string]any +} + +type iterableImpl struct{ i iterable } + +type ServicesConfig []*DriverConfig + +func (c ServicesConfig) DriversNumber() int { return len(c) } + +type DriverConfig struct { + Config map[string]any + Address string +} + +func newSvcConfigFromList(l []map[string]any) (ServicesConfig, error) { + var cfg ServicesConfig + for _, c := range l { + cfg = append(cfg, &DriverConfig{Config: c}) + } + return cfg, nil +} + +func newSvcConfigFromMap(m map[string]any) ServicesConfig { + s, _ := newSvcConfigFromList([]map[string]any{m}) + return s +} + +func parseServices(cfg map[string]any) (map[string]ServicesConfig, error) { + // parse services + svcCfg, ok := cfg["services"].(map[string]any) + if !ok { + return nil, errors.New("grpc.services must be a map") + } + + services := make(map[string]ServicesConfig) + for name, cfg := range svcCfg { + // cfg can be a list or a map + cfgLst, ok := cfg.([]map[string]any) + if ok { + s, err := newSvcConfigFromList(cfgLst) + if err != nil { + return nil, err + } + services[name] = s + continue + } + cfgMap, ok := cfg.(map[string]any) + if !ok { + return nil, fmt.Errorf("grpc.services.%s must be a list or a map. got %T", name, cfg) + } + services[name] = newSvcConfigFromMap(cfgMap) + } + + return services, nil +} + +func parseMiddlwares(cfg map[string]any, key string) (map[string]map[string]any, error) { + m := make(map[string]map[string]any) + + mid, ok := cfg[key] + if !ok { + return m, nil + } + + if err := mapstructure.Decode(mid, &m); err != nil { + return nil, err + } + return m, nil +} + +type Service struct { + Address string + Name string + Config map[string]any + + raw *DriverConfig +} + +func (s *Service) SetAddress(address string) { + s.Address = address + s.raw.Address = address +} + +type ServiceFunc func(*Service) + +type Interceptor struct { + Name string + Config map[string]any +} + +type InterceptorFunc func(*Interceptor) + +// ForEachService iterates to each service/driver calling the function f. +func (i iterableImpl) ForEachService(f ServiceFunc) { + for name, c := range i.i.services() { + for _, cfg := range c { + f(&Service{ + raw: cfg, + Address: cfg.Address, + Name: name, + Config: cfg.Config, + }) + } + } +} + +// ForEachInterceptor iterates to each middlware calling the function f. +func (i iterableImpl) ForEachInterceptor(f InterceptorFunc) { + for name, c := range i.i.interceptors() { + f(&Interceptor{ + Name: name, + Config: c, + }) + } +} + +func addressForService(global string, cfg map[string]any) string { + if address, ok := cfg["address"].(string); ok { + return address + } + return global +} diff --git a/cmd/revad/internal/config/config.go b/cmd/revad/internal/config/config.go index be439320b8..9e80525646 100644 --- a/cmd/revad/internal/config/config.go +++ b/cmd/revad/internal/config/config.go @@ -30,6 +30,7 @@ type Config struct { raw map[string]any GRPC *GRPC `key:"grpc"` + HTTP *HTTP `key:"http"` // Serverless *Serverless // TODO // TODO: add log, shared, core @@ -54,6 +55,9 @@ func (c *Config) parse() error { if err := c.parseGRPC(); err != nil { return err } + if err := c.parseHTTP(); err != nil { + return err + } return nil } diff --git a/cmd/revad/internal/config/grpc.go b/cmd/revad/internal/config/grpc.go index cef151fcf1..f45468cc80 100644 --- a/cmd/revad/internal/config/grpc.go +++ b/cmd/revad/internal/config/grpc.go @@ -1,40 +1,24 @@ package config import ( - "fmt" - "github.com/mitchellh/mapstructure" "github.com/pkg/errors" ) type GRPC struct { - Address string `mapstructure:"address" key:"address"` - - services map[string]ServicesConfig `key:"services"` - middlewares map[string]map[string]any `key:"middlewares"` -} - -type ServicesConfig []*DriverConfig + Address string `mapstructure:"address" key:"address"` + Network string `mapstructure:"network" key:"network"` + ShutdownDeadline int `mapstructure:"shutdown_deadline" key:"shutdown_deadline"` + EnableReflection bool `mapstructure:"enable_reflection" key:"enable_reflection"` -func (c ServicesConfig) DriversNumber() int { return len(c) } + _services map[string]ServicesConfig `key:"services"` + _interceptors map[string]map[string]any `key:"interceptors"` -type DriverConfig struct { - Config map[string]any - Address string -} - -func newSvcConfigFromList(l []map[string]any) (ServicesConfig, error) { - var cfg ServicesConfig - for _, c := range l { - cfg = append(cfg, &DriverConfig{Config: c}) - } - return cfg, nil + iterableImpl } -func newSvcConfigFromMap(m map[string]any) ServicesConfig { - s, _ := newSvcConfigFromList([]map[string]any{m}) - return s -} +func (g *GRPC) services() map[string]ServicesConfig { return g._services } +func (g *GRPC) interceptors() map[string]map[string]any { return g._interceptors } func (c *Config) parseGRPC() error { cfg, ok := c.raw["grpc"] @@ -46,72 +30,30 @@ func (c *Config) parseGRPC() error { return errors.Wrap(err, "config: error decoding grpc config") } - // parse services - svcCfg, ok := cfg.(map[string]any)["services"].(map[string]any) + cfgGRPC, ok := cfg.(map[string]any) if !ok { - return errors.New("grpc.services must be a map") + return errors.New("grpc must be a map") } - services := make(map[string]ServicesConfig) - for name, cfg := range svcCfg { - // cfg can be a list or a map - cfgLst, ok := cfg.([]map[string]any) - if ok { - s, err := newSvcConfigFromList(cfgLst) - if err != nil { - return err - } - services[name] = s - continue - } - cfgMap, ok := cfg.(map[string]any) - if !ok { - return fmt.Errorf("grpc.services.%s must be a list or a map. got %T", name, cfg) - } - services[name] = newSvcConfigFromMap(cfgMap) + services, err := parseServices(cfgGRPC) + if err != nil { + return err } - grpc.services = services - c.GRPC = &grpc - for _, c := range grpc.services { - for _, cfg := range c { - cfg.Address = addressForService(grpc.Address, cfg.Config) - } + interceptors, err := parseMiddlwares(cfgGRPC, "interceptors") + if err != nil { + return err } - return nil -} - -type Service struct { - Address string - Name string - Config map[string]any - - raw *DriverConfig -} - -func (s *Service) SetAddress(address string) { - s.raw.Address = address -} -type ServiceFunc func(*Service) + grpc._services = services + grpc._interceptors = interceptors + grpc.iterableImpl = iterableImpl{&grpc} + c.GRPC = &grpc -// ForEach iterates to each service/driver calling the function f. -func (g *GRPC) ForEach(f ServiceFunc) { - for name, c := range g.services { + for _, c := range grpc._services { for _, cfg := range c { - f(&Service{ - raw: cfg, - Address: cfg.Address, - Name: name, - Config: cfg.Config, - }) + cfg.Address = addressForService(grpc.Address, cfg.Config) } } -} - -func addressForService(global string, cfg map[string]any) string { - if address, ok := cfg["address"].(string); ok { - return address - } - return global + return nil } diff --git a/cmd/revad/internal/config/http.go b/cmd/revad/internal/config/http.go new file mode 100644 index 0000000000..09ffcb0707 --- /dev/null +++ b/cmd/revad/internal/config/http.go @@ -0,0 +1,59 @@ +package config + +import ( + "github.com/mitchellh/mapstructure" + "github.com/pkg/errors" +) + +type HTTP struct { + Network string `mapstructure:"network" key:"network"` + Address string `mapstructure:"address" key:"address"` + CertFile string `mapstructure:"certfile" key:"certfile"` + KeyFile string `mapstructure:"keyfile" key:"keyfile"` + + _services map[string]ServicesConfig `key:"services"` + _middlewares map[string]map[string]any `key:"middlewares"` + + iterableImpl +} + +func (h *HTTP) services() map[string]ServicesConfig { return h._services } +func (h *HTTP) interceptors() map[string]map[string]any { return h._middlewares } + +func (c *Config) parseHTTP() error { + cfg, ok := c.raw["http"] + if !ok { + return nil + } + var http HTTP + if err := mapstructure.Decode(cfg, &http); err != nil { + return errors.Wrap(err, "config: error decoding http config") + } + + cfgHTTP, ok := cfg.(map[string]any) + if !ok { + return errors.New("http must be a map") + } + + services, err := parseServices(cfgHTTP) + if err != nil { + return err + } + + middlewares, err := parseMiddlwares(cfgHTTP, "middlewares") + if err != nil { + return err + } + + http._services = services + http._middlewares = middlewares + http.iterableImpl = iterableImpl{&http} + c.HTTP = &http + + for _, c := range http._services { + for _, cfg := range c { + cfg.Address = addressForService(http.Address, cfg.Config) + } + } + return nil +} From ec986603b6ebf5d45cad329c10af7b7e92cc6b03 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Wed, 14 Jun 2023 10:46:33 +0200 Subject: [PATCH 06/76] add serverless config --- cmd/revad/internal/config/config.go | 25 ++++++++++---------- cmd/revad/internal/config/grpc.go | 4 ++-- cmd/revad/internal/config/http.go | 4 ++-- cmd/revad/internal/config/serverless.go | 31 +++++++++++++++++++++++++ 4 files changed, 47 insertions(+), 17 deletions(-) create mode 100644 cmd/revad/internal/config/serverless.go diff --git a/cmd/revad/internal/config/config.go b/cmd/revad/internal/config/config.go index 9e80525646..0c11b07227 100644 --- a/cmd/revad/internal/config/config.go +++ b/cmd/revad/internal/config/config.go @@ -27,35 +27,34 @@ import ( ) type Config struct { - raw map[string]any - - GRPC *GRPC `key:"grpc"` - HTTP *HTTP `key:"http"` - // Serverless *Serverless // TODO + GRPC *GRPC `key:"grpc"` + HTTP *HTTP `key:"http"` + Serverless *Serverless `key:"serverless"` // TODO: add log, shared, core } -type Serverless struct { -} - // Load loads the configuration from the reader. func Load(r io.Reader) (*Config, error) { var c Config - if _, err := toml.NewDecoder(r).Decode(&c.raw); err != nil { + var raw map[string]any + if _, err := toml.NewDecoder(r).Decode(&raw); err != nil { return nil, errors.Wrap(err, "config: error decoding toml data") } - if err := c.parse(); err != nil { + if err := c.parse(raw); err != nil { return nil, err } return &c, nil } -func (c *Config) parse() error { - if err := c.parseGRPC(); err != nil { +func (c *Config) parse(raw map[string]any) error { + if err := c.parseGRPC(raw); err != nil { + return err + } + if err := c.parseHTTP(raw); err != nil { return err } - if err := c.parseHTTP(); err != nil { + if err := c.parseServerless(raw); err != nil { return err } return nil diff --git a/cmd/revad/internal/config/grpc.go b/cmd/revad/internal/config/grpc.go index f45468cc80..30b964b215 100644 --- a/cmd/revad/internal/config/grpc.go +++ b/cmd/revad/internal/config/grpc.go @@ -20,8 +20,8 @@ type GRPC struct { func (g *GRPC) services() map[string]ServicesConfig { return g._services } func (g *GRPC) interceptors() map[string]map[string]any { return g._interceptors } -func (c *Config) parseGRPC() error { - cfg, ok := c.raw["grpc"] +func (c *Config) parseGRPC(raw map[string]any) error { + cfg, ok := raw["grpc"] if !ok { return nil } diff --git a/cmd/revad/internal/config/http.go b/cmd/revad/internal/config/http.go index 09ffcb0707..d1262b7781 100644 --- a/cmd/revad/internal/config/http.go +++ b/cmd/revad/internal/config/http.go @@ -20,8 +20,8 @@ type HTTP struct { func (h *HTTP) services() map[string]ServicesConfig { return h._services } func (h *HTTP) interceptors() map[string]map[string]any { return h._middlewares } -func (c *Config) parseHTTP() error { - cfg, ok := c.raw["http"] +func (c *Config) parseHTTP(raw map[string]any) error { + cfg, ok := raw["http"] if !ok { return nil } diff --git a/cmd/revad/internal/config/serverless.go b/cmd/revad/internal/config/serverless.go new file mode 100644 index 0000000000..c807fec6b1 --- /dev/null +++ b/cmd/revad/internal/config/serverless.go @@ -0,0 +1,31 @@ +package config + +import ( + "github.com/mitchellh/mapstructure" + "github.com/pkg/errors" +) + +type Serverless struct { + Services map[string]map[string]any `mapstructure:"services"` +} + +func (c *Config) parseServerless(raw map[string]any) error { + cfg, ok := raw["serverless"] + if !ok { + return nil + } + + var s Serverless + if err := mapstructure.Decode(cfg, &s); err != nil { + return errors.Wrap(err, "config: error decoding serverless config") + } + + c.Serverless = &s + return nil +} + +func (s *Serverless) ForEach(f func(name string, config map[string]any)) { + for name, cfg := range s.Services { + f(name, cfg) + } +} From d42cd6b522dfa7b810bd64136dee5b936f96c85b Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Wed, 14 Jun 2023 10:53:40 +0200 Subject: [PATCH 07/76] improvements in tests --- cmd/revad/internal/config/config_test.go | 4 +- cmd/revad/internal/config/lookup_test.go | 10 ++-- cmd/revad/internal/config/parser_test.go | 70 ++++++++++++------------ 3 files changed, 41 insertions(+), 43 deletions(-) diff --git a/cmd/revad/internal/config/config_test.go b/cmd/revad/internal/config/config_test.go index c9ece391a6..eb4e02867f 100644 --- a/cmd/revad/internal/config/config_test.go +++ b/cmd/revad/internal/config/config_test.go @@ -72,7 +72,7 @@ something = "test"` }, }, } - assert.Equal(t, exp, c.GRPC.services) + assert.Equal(t, exp, c.GRPC._services) } func TestLoadNoGRPCDefaultAddress(t *testing.T) { @@ -136,5 +136,5 @@ something = "test"` }, }, } - assert.Equal(t, exp, c.GRPC.services) + assert.Equal(t, exp, c.GRPC._services) } diff --git a/cmd/revad/internal/config/lookup_test.go b/cmd/revad/internal/config/lookup_test.go index b745988ed5..af557905e9 100644 --- a/cmd/revad/internal/config/lookup_test.go +++ b/cmd/revad/internal/config/lookup_test.go @@ -3,6 +3,8 @@ package config import ( "reflect" "testing" + + "github.com/stretchr/testify/assert" ) type SimpleStruct struct { @@ -121,13 +123,9 @@ func TestLookupStruct(t *testing.T) { for _, tt := range tests { got, err := lookupStruct(tt.key, reflect.ValueOf(tt.in)) - if err != tt.err { - t.Fatalf("got not expected error: got=%v exp=%v", err, tt.err) - } + assert.Equal(t, err, tt.err, "got not expected error") if tt.err == nil { - if !reflect.DeepEqual(tt.val, got) { - t.Fatalf("got not expected result. got=%v exp=%v", got, tt.val) - } + assert.Equal(t, tt.val, got) } } diff --git a/cmd/revad/internal/config/parser_test.go b/cmd/revad/internal/config/parser_test.go index 3c242ae8b8..78678e4469 100644 --- a/cmd/revad/internal/config/parser_test.go +++ b/cmd/revad/internal/config/parser_test.go @@ -1,32 +1,31 @@ package config import ( - "reflect" "testing" - "github.com/gdexlab/go-render/render" + "gotest.tools/assert" ) func TestSplit(t *testing.T) { tests := []struct { - key string - exptoken string - expnext string + key string + token string + next string }{ { - key: ".grpc.services.authprovider[1].address", - exptoken: "grpc", - expnext: ".services.authprovider[1].address", + key: ".grpc.services.authprovider[1].address", + token: "grpc", + next: ".services.authprovider[1].address", }, { - key: "[1].address", - exptoken: "1", - expnext: ".address", + key: "[1].address", + token: "1", + next: ".address", }, { - key: "[100].address", - exptoken: "100", - expnext: ".address", + key: "[100].address", + token: "100", + next: ".address", }, { key: "", @@ -35,45 +34,46 @@ func TestSplit(t *testing.T) { for _, tt := range tests { token, next := split(tt.key) - if token != tt.exptoken || next != tt.expnext { - t.Fatalf("unexpected result: token=%s exp=%s | next=%s exp=%s", token, tt.exptoken, next, tt.expnext) - } + assert.Equal(t, token, tt.token) + assert.Equal(t, next, tt.next) } } func TestParseNext(t *testing.T) { tests := []struct { - key string - expcmd Command - expnext string - experr error + key string + cmd Command + next string + err error }{ { - key: ".grpc.services.authprovider[1].address", - expcmd: FieldByKey{Key: "grpc"}, - expnext: ".services.authprovider[1].address", + key: ".grpc.services.authprovider[1].address", + cmd: FieldByKey{Key: "grpc"}, + next: ".services.authprovider[1].address", }, { - key: ".authprovider[1].address", - expcmd: FieldByKey{Key: "authprovider"}, - expnext: "[1].address", + key: ".authprovider[1].address", + cmd: FieldByKey{Key: "authprovider"}, + next: "[1].address", }, { - key: "[1].authprovider.address", - expcmd: FieldByIndex{Index: 1}, - expnext: ".authprovider.address", + key: "[1].authprovider.address", + cmd: FieldByIndex{Index: 1}, + next: ".authprovider.address", }, { - key: ".authprovider", - expcmd: FieldByKey{Key: "authprovider"}, - expnext: "", + key: ".authprovider", + cmd: FieldByKey{Key: "authprovider"}, + next: "", }, } for _, tt := range tests { cmd, next, err := parseNext(tt.key) - if err != tt.experr || !reflect.DeepEqual(cmd, tt.expcmd) || next != tt.expnext { - t.Fatalf("unexpected result: err=%v exp=%v | cmd=%s exp=%s | next=%s exp=%s", err, tt.experr, render.AsCode(cmd), render.AsCode(tt.expcmd), next, tt.expnext) + assert.Equal(t, err, tt.err) + if tt.err == nil { + assert.Equal(t, cmd, tt.cmd) + assert.Equal(t, next, tt.next) } } } From 9d67abf9c4358072932341b7694bc65b001363e3 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Wed, 14 Jun 2023 11:07:28 +0200 Subject: [PATCH 08/76] add rest of config --- cmd/revad/internal/config/config.go | 40 ++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/cmd/revad/internal/config/config.go b/cmd/revad/internal/config/config.go index 0c11b07227..92e2fdceb3 100644 --- a/cmd/revad/internal/config/config.go +++ b/cmd/revad/internal/config/config.go @@ -23,17 +23,45 @@ import ( "reflect" "github.com/BurntSushi/toml" + "github.com/mitchellh/mapstructure" "github.com/pkg/errors" ) type Config struct { - GRPC *GRPC `key:"grpc"` - HTTP *HTTP `key:"http"` - Serverless *Serverless `key:"serverless"` + GRPC *GRPC `key:"grpc" mapsrtcuture:"-"` + HTTP *HTTP `key:"http" mapstructure:"-"` + Serverless *Serverless `key:"serverless" mapstructure:"-"` + Shared *Shared `key:"shared" mapstructure:"shared"` + Log *Log `key:"log" mapstructure:"log"` + Core *Core `key:"core" mapstructure:"core"` + Vars Vars `key:"vars" mapstructure:"vars"` +} + +type Log struct { + Output string `key:"output" mapstructure:"output"` + Mode string `key:"mode" mapstructure:"mode"` + Level string `key:"level" mapstructure:"level"` +} + +type Shared struct { + JWTSecret string `key:"jwt_secret" mapstructure:"jwt_secret"` + GatewaySVC string `key:"gatewaysvc" mapstructure:"gatewaysvc"` + DataGateway string `key:"datagateway" mapstructure:"datagateway"` + SkipUserGroupsInToken bool `key:"skip_user_groups_in_token" mapstructure:"skip_user_groups_in_token"` + BlockedUsers []string `key:"blocked_users" mapstructure:"blocked_users"` +} - // TODO: add log, shared, core +type Core struct { + MaxCPUs string `key:"max_cpus" mapstructure:"max_cpus"` + TracingEnabled bool `key:"tracing_enabled" mapstructure:"tracing_enabled"` + TracingEndpoint string `key:"tracing_endpoint" mapstructure:"tracing_endpoint"` + TracingCollector string `key:"tracing_collector" mapstructure:"tracing_collector"` + TracingServiceName string `key:"tracing_service_name" mapstructure:"tracing_service_name"` + TracingService string `key:"tracing_service" mapstructure:"tracing_service"` } +type Vars map[string]any + // Load loads the configuration from the reader. func Load(r io.Reader) (*Config, error) { var c Config @@ -44,6 +72,7 @@ func Load(r io.Reader) (*Config, error) { if err := c.parse(raw); err != nil { return nil, err } + c.Vars = make(Vars) return &c, nil } @@ -57,6 +86,9 @@ func (c *Config) parse(raw map[string]any) error { if err := c.parseServerless(raw); err != nil { return err } + if err := mapstructure.Decode(raw, c); err != nil { + return err + } return nil } From 8297b8282668945cf223604238280858bb2042ba Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Wed, 14 Jun 2023 16:11:33 +0200 Subject: [PATCH 09/76] do not reference pointers on lookup --- cmd/revad/internal/config/config.go | 12 ++++++------ cmd/revad/internal/config/lookup.go | 17 +---------------- 2 files changed, 7 insertions(+), 22 deletions(-) diff --git a/cmd/revad/internal/config/config.go b/cmd/revad/internal/config/config.go index 92e2fdceb3..f60918b6a2 100644 --- a/cmd/revad/internal/config/config.go +++ b/cmd/revad/internal/config/config.go @@ -31,10 +31,10 @@ type Config struct { GRPC *GRPC `key:"grpc" mapsrtcuture:"-"` HTTP *HTTP `key:"http" mapstructure:"-"` Serverless *Serverless `key:"serverless" mapstructure:"-"` - Shared *Shared `key:"shared" mapstructure:"shared"` - Log *Log `key:"log" mapstructure:"log"` - Core *Core `key:"core" mapstructure:"core"` - Vars Vars `key:"vars" mapstructure:"vars"` + Shared *Shared `key:"shared" mapstructure:"shared" template:"-"` + Log *Log `key:"log" mapstructure:"log" template:"-"` + Core *Core `key:"core" mapstructure:"core" template:"-"` + Vars Vars `key:"vars" mapstructure:"vars" template:"-"` } type Log struct { @@ -93,9 +93,9 @@ func (c *Config) parse(raw map[string]any) error { } func (c *Config) ApplyTemplates() error { - return nil + return c.applyTemplateByType(reflect.ValueOf(c)) } func (c *Config) lookup(key string) (any, error) { - return lookupStruct(key, reflect.ValueOf(c)) + return lookupByType(key, reflect.ValueOf(c)) } diff --git a/cmd/revad/internal/config/lookup.go b/cmd/revad/internal/config/lookup.go index 531befbef0..79b25f9d2c 100644 --- a/cmd/revad/internal/config/lookup.go +++ b/cmd/revad/internal/config/lookup.go @@ -10,8 +10,6 @@ import ( var ErrKeyNotFound = errors.New("key not found") func lookupStruct(key string, v reflect.Value) (any, error) { - v = resolveRef(v) - if v.Kind() != reflect.Struct { panic("called lookupStruct on non struct type") } @@ -49,7 +47,6 @@ func lookupStruct(key string, v reflect.Value) (any, error) { } func lookupByType(key string, v reflect.Value) (any, error) { - v = resolveRef(v) switch v.Kind() { case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, @@ -61,15 +58,13 @@ func lookupByType(key string, v reflect.Value) (any, error) { return lookupStruct(key, v) case reflect.Map: return lookupMap(key, v) - case reflect.Interface: + case reflect.Interface, reflect.Pointer: return lookupByType(key, v.Elem()) } panic("type not supported: " + v.Kind().String()) } func lookupMap(key string, v reflect.Value) (any, error) { - v = resolveRef(v) - if v.Kind() != reflect.Map { panic("called lookupMap on non map type") } @@ -97,8 +92,6 @@ func lookupMap(key string, v reflect.Value) (any, error) { } func lookupList(key string, v reflect.Value) (any, error) { - v = resolveRef(v) - if v.Kind() != reflect.Slice && v.Kind() != reflect.Array { panic("called lookupList on non array/slice type") } @@ -135,7 +128,6 @@ func lookupList(key string, v reflect.Value) (any, error) { } func lookupPrimitive(key string, v reflect.Value) (any, error) { - v = resolveRef(v) if v.Kind() != reflect.Bool && v.Kind() != reflect.Int && v.Kind() != reflect.Int8 && v.Kind() != reflect.Int16 && v.Kind() != reflect.Int32 && v.Kind() != reflect.Int64 && v.Kind() != reflect.Uint && v.Kind() != reflect.Uint8 && v.Kind() != reflect.Uint16 && @@ -154,10 +146,3 @@ func lookupPrimitive(key string, v reflect.Value) (any, error) { return nil, errors.New("cannot address a value of type " + v.Kind().String()) } - -func resolveRef(v reflect.Value) reflect.Value { - if v.Kind() == reflect.Pointer { - return resolveRef(v.Elem()) - } - return v -} From b2935c62a873c0558aed35e39ebd2b06740e5235 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Wed, 14 Jun 2023 17:33:03 +0200 Subject: [PATCH 10/76] skip unexported field on lookup --- cmd/revad/internal/config/lookup.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cmd/revad/internal/config/lookup.go b/cmd/revad/internal/config/lookup.go index 79b25f9d2c..9483e6a6f7 100644 --- a/cmd/revad/internal/config/lookup.go +++ b/cmd/revad/internal/config/lookup.go @@ -32,6 +32,10 @@ func lookupStruct(key string, v reflect.Value) (any, error) { val := v.Field(i) field := t.Field(i) + if !field.IsExported() { + continue + } + tag := field.Tag.Get("key") if tag == "" { continue From abead30cd7ee05d8e1b7a1eb4811eec009fad3ad Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Wed, 14 Jun 2023 17:33:57 +0200 Subject: [PATCH 11/76] add templates --- cmd/revad/internal/config/config.go | 2 +- cmd/revad/internal/config/config_test.go | 4 +- cmd/revad/internal/config/grpc.go | 14 +- cmd/revad/internal/config/http.go | 14 +- cmd/revad/internal/config/templates.go | 181 +++++++++++++++++++++++ 5 files changed, 198 insertions(+), 17 deletions(-) create mode 100644 cmd/revad/internal/config/templates.go diff --git a/cmd/revad/internal/config/config.go b/cmd/revad/internal/config/config.go index f60918b6a2..becabdd118 100644 --- a/cmd/revad/internal/config/config.go +++ b/cmd/revad/internal/config/config.go @@ -93,7 +93,7 @@ func (c *Config) parse(raw map[string]any) error { } func (c *Config) ApplyTemplates() error { - return c.applyTemplateByType(reflect.ValueOf(c)) + return c.applyTemplateByType(nil, reflect.ValueOf(c)) } func (c *Config) lookup(key string) (any, error) { diff --git a/cmd/revad/internal/config/config_test.go b/cmd/revad/internal/config/config_test.go index eb4e02867f..0eec5fa1bd 100644 --- a/cmd/revad/internal/config/config_test.go +++ b/cmd/revad/internal/config/config_test.go @@ -72,7 +72,7 @@ something = "test"` }, }, } - assert.Equal(t, exp, c.GRPC._services) + assert.Equal(t, exp, c.GRPC.Services) } func TestLoadNoGRPCDefaultAddress(t *testing.T) { @@ -136,5 +136,5 @@ something = "test"` }, }, } - assert.Equal(t, exp, c.GRPC._services) + assert.Equal(t, exp, c.GRPC.Services) } diff --git a/cmd/revad/internal/config/grpc.go b/cmd/revad/internal/config/grpc.go index 30b964b215..1a5a582066 100644 --- a/cmd/revad/internal/config/grpc.go +++ b/cmd/revad/internal/config/grpc.go @@ -11,14 +11,14 @@ type GRPC struct { ShutdownDeadline int `mapstructure:"shutdown_deadline" key:"shutdown_deadline"` EnableReflection bool `mapstructure:"enable_reflection" key:"enable_reflection"` - _services map[string]ServicesConfig `key:"services"` - _interceptors map[string]map[string]any `key:"interceptors"` + Services map[string]ServicesConfig `key:"services"` + Interceptors map[string]map[string]any `key:"interceptors"` iterableImpl } -func (g *GRPC) services() map[string]ServicesConfig { return g._services } -func (g *GRPC) interceptors() map[string]map[string]any { return g._interceptors } +func (g *GRPC) services() map[string]ServicesConfig { return g.Services } +func (g *GRPC) interceptors() map[string]map[string]any { return g.Interceptors } func (c *Config) parseGRPC(raw map[string]any) error { cfg, ok := raw["grpc"] @@ -45,12 +45,12 @@ func (c *Config) parseGRPC(raw map[string]any) error { return err } - grpc._services = services - grpc._interceptors = interceptors + grpc.Services = services + grpc.Interceptors = interceptors grpc.iterableImpl = iterableImpl{&grpc} c.GRPC = &grpc - for _, c := range grpc._services { + for _, c := range grpc.Services { for _, cfg := range c { cfg.Address = addressForService(grpc.Address, cfg.Config) } diff --git a/cmd/revad/internal/config/http.go b/cmd/revad/internal/config/http.go index d1262b7781..e1515afb1a 100644 --- a/cmd/revad/internal/config/http.go +++ b/cmd/revad/internal/config/http.go @@ -11,14 +11,14 @@ type HTTP struct { CertFile string `mapstructure:"certfile" key:"certfile"` KeyFile string `mapstructure:"keyfile" key:"keyfile"` - _services map[string]ServicesConfig `key:"services"` - _middlewares map[string]map[string]any `key:"middlewares"` + Services map[string]ServicesConfig `key:"services"` + Middlewares map[string]map[string]any `key:"middlewares"` iterableImpl } -func (h *HTTP) services() map[string]ServicesConfig { return h._services } -func (h *HTTP) interceptors() map[string]map[string]any { return h._middlewares } +func (h *HTTP) services() map[string]ServicesConfig { return h.Services } +func (h *HTTP) interceptors() map[string]map[string]any { return h.Middlewares } func (c *Config) parseHTTP(raw map[string]any) error { cfg, ok := raw["http"] @@ -45,12 +45,12 @@ func (c *Config) parseHTTP(raw map[string]any) error { return err } - http._services = services - http._middlewares = middlewares + http.Services = services + http.Middlewares = middlewares http.iterableImpl = iterableImpl{&http} c.HTTP = &http - for _, c := range http._services { + for _, c := range http.Services { for _, cfg := range c { cfg.Address = addressForService(http.Address, cfg.Config) } diff --git a/cmd/revad/internal/config/templates.go b/cmd/revad/internal/config/templates.go new file mode 100644 index 0000000000..b3ef47409f --- /dev/null +++ b/cmd/revad/internal/config/templates.go @@ -0,0 +1,181 @@ +package config + +import ( + "reflect" + "strings" + + "github.com/pkg/errors" +) + +type parent interface { + SetValue(v any) +} + +type parentList struct { + List reflect.Value + Index int +} + +type parentMap struct { + Map reflect.Value + Key any +} + +type parentStruct struct { + Struct reflect.Value + Field int +} + +func (p parentList) SetValue(v any) { + el := p.List.Index(p.Index) + el.Set(reflect.ValueOf(v)) +} + +func (p parentMap) SetValue(v any) { + p.Map.SetMapIndex(reflect.ValueOf(p.Key), reflect.ValueOf(v)) +} + +func (p parentStruct) SetValue(v any) { + p.Struct.Field(p.Field).Set(reflect.ValueOf(v)) +} + +func (c *Config) applyTemplateStruct(p parent, v reflect.Value) error { + if v.Kind() != reflect.Struct { + panic("called applyTemplateStruct on non struct type") + } + + t := v.Type() + for i := 0; i < v.NumField(); i++ { + el := v.Field(i) + f := t.Field(i) + + if !f.IsExported() { + continue + } + + if f.Tag.Get("template") == "-" { + // skip this field + continue + } + + if err := c.applyTemplateByType(parentStruct{Struct: v, Field: i}, el); err != nil { + return err + } + } + return nil +} + +func (c *Config) applyTemplateByType(p parent, v reflect.Value) error { + switch v.Kind() { + case reflect.String: + return c.applyTemplateString(p, v) + case reflect.Array, reflect.Slice: + return c.applyTemplateList(p, v) + case reflect.Struct: + return c.applyTemplateStruct(p, v) + case reflect.Map: + return c.applyTemplateMap(p, v) + case reflect.Interface: + return c.applyTemplateInterface(p, v) + case reflect.Pointer: + return c.applyTemplateByType(p, v.Elem()) + } + return nil +} + +func (c *Config) applyTemplateList(p parent, v reflect.Value) error { + if v.Kind() != reflect.Array && v.Kind() != reflect.Slice { + panic("called applyTemplateList on non array/slice type") + } + + for i := 0; i < v.Len(); i++ { + el := v.Index(i) + if err := c.applyTemplateByType(parentList{List: v, Index: i}, el); err != nil { + return err + } + } + return nil +} + +func (c *Config) applyTemplateMap(p parent, v reflect.Value) error { + if v.Kind() != reflect.Map { + panic("called applyTemplateMap on non map type") + } + + iter := v.MapRange() + for iter.Next() { + k := iter.Key() + el := v.MapIndex(k) + if err := c.applyTemplateByType(parentMap{Map: v, Key: k.Interface()}, el); err != nil { + return err + } + } + return nil +} + +func (c *Config) applyTemplateInterface(p parent, v reflect.Value) error { + if v.Kind() != reflect.Interface { + panic("called applyTemplateInterface on non interface value") + } + + s, ok := v.Interface().(string) + if !ok { + return c.applyTemplateByType(p, v.Elem()) + } + + if !isTemplate(s) { + // nothing to do + return nil + } + + key := keyFromTemplate(s) + val, err := c.lookup(key) + if err != nil { + return err + } + + p.SetValue(val) + return nil +} + +func (c *Config) applyTemplateString(p parent, v reflect.Value) error { + if v.Kind() != reflect.String { + panic("called applyTemplateString on non string type") + } + + s := v.Interface().(string) + if !isTemplate(s) { + // nothing to do + return nil + } + + if !v.CanSet() { + panic("value is not addressable") + } + + key := keyFromTemplate(s) + val, err := c.lookup(key) + if err != nil { + return err + } + + str, ok := val.(string) + if ok { + p.SetValue(str) + return nil + } + + return errors.New("value cannot be set on a non string type") +} + +func isTemplate(s string) bool { + s = strings.TrimSpace(s) + return strings.HasPrefix(s, "{{") && strings.HasSuffix(s, "}}") +} + +func keyFromTemplate(s string) string { + s = strings.TrimSpace(s) + s = strings.TrimPrefix(s, "{{") + s = strings.TrimSuffix(s, "}}") + return "." + strings.TrimSpace(s) +} From 83d2c266c80010b82cc71ec688ee89353448f3e1 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Wed, 14 Jun 2023 17:36:35 +0200 Subject: [PATCH 12/76] fix typo --- cmd/revad/internal/config/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/revad/internal/config/config.go b/cmd/revad/internal/config/config.go index becabdd118..0f51199bc8 100644 --- a/cmd/revad/internal/config/config.go +++ b/cmd/revad/internal/config/config.go @@ -28,7 +28,7 @@ import ( ) type Config struct { - GRPC *GRPC `key:"grpc" mapsrtcuture:"-"` + GRPC *GRPC `key:"grpc" mapstructure:"-"` HTTP *HTTP `key:"http" mapstructure:"-"` Serverless *Serverless `key:"serverless" mapstructure:"-"` Shared *Shared `key:"shared" mapstructure:"shared" template:"-"` From f518f09dba009bdccea3f0f4aee19198ead8565d Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Thu, 15 Jun 2023 11:35:49 +0200 Subject: [PATCH 13/76] add tests config + bugfixes --- cmd/revad/internal/config/config_test.go | 188 +++++++++++++++++++++++ cmd/revad/internal/config/grpc.go | 6 +- cmd/revad/internal/config/http.go | 4 +- 3 files changed, 193 insertions(+), 5 deletions(-) diff --git a/cmd/revad/internal/config/config_test.go b/cmd/revad/internal/config/config_test.go index 0eec5fa1bd..c91aff1db1 100644 --- a/cmd/revad/internal/config/config_test.go +++ b/cmd/revad/internal/config/config_test.go @@ -138,3 +138,191 @@ something = "test"` } assert.Equal(t, exp, c.GRPC.Services) } + +func TestLoadFullConfig(t *testing.T) { + config := ` +[shared] +gatewaysvc = "localhost:9142" +jwt_secret = "secret" + +[log] +output = "/var/log/revad/revad-gateway.log" +mode = "json" +level = "trace" + +[core] +max_cpus = 1 +tracing_enabled = true + +[vars] +db_username = "root" +db_password = "secretpassword" + +[grpc] +shutdown_deadline = 10 +enable_reflection = true + +[grpc.services.gateway] +authregistrysvc = "{{ grpc.services.authregistry.address }}" + +[grpc.services.authregistry] +driver = "static" + +[grpc.services.authregistry.drivers.static.rules] +basic = "{{ grpc.services.authprovider[0].address }}" +machine = "{{ grpc.services.authprovider[1].address }}" + +[[grpc.services.authprovider]] +driver = "ldap" +address = "localhost:19000" + +[grpc.services.authprovider.drivers.ldap] +password = "ldap" + +[[grpc.services.authprovider]] +driver = "machine" +address = "localhost:19001" + +[grpc.services.authprovider.drivers.machine] +api_key = "secretapikey" + +[http] +address = "localhost:19002" + +[http.services.dataprovider] +driver = "localhome" + +[http.services.sysinfo] + +[serverless.services.notifications] +nats_address = "nats-server-01.example.com" +nats_token = "secret-token-example"` + + c2, err := Load(strings.NewReader(config)) + assert.ErrorIs(t, err, nil) + + assert.Equal(t, c2.Shared, &Shared{ + GatewaySVC: "localhost:9142", + JWTSecret: "secret", + }) + + assert.Equal(t, c2.Log, &Log{ + Output: "/var/log/revad/revad-gateway.log", + Mode: "json", + Level: "trace", + }) + + assert.Equal(t, c2.Core, &Core{ + MaxCPUs: 1, + TracingEnabled: true, + }) + + assert.Equal(t, c2.Vars, Vars{ + "db_username": "root", + "db_password": "secretpassword", + }) + + assertGRPCEqual(t, c2.GRPC, &GRPC{ + ShutdownDeadline: 10, + EnableReflection: true, + Interceptors: make(map[string]map[string]any), + Services: map[string]ServicesConfig{ + "gateway": { + { + Config: map[string]any{ + "authregistrysvc": "{{ grpc.services.authregistry.address }}", + }, + }, + }, + "authregistry": { + { + Config: map[string]any{ + "driver": "static", + "drivers": map[string]any{ + "static": map[string]any{ + "rules": map[string]any{ + "basic": "{{ grpc.services.authprovider[0].address }}", + "machine": "{{ grpc.services.authprovider[1].address }}", + }, + }, + }, + }, + }, + }, + "authprovider": { + { + Address: "localhost:19000", + Config: map[string]any{ + "driver": "ldap", + "address": "localhost:19000", + "drivers": map[string]any{ + "ldap": map[string]any{ + "password": "ldap", + }, + }, + }, + }, + { + Address: "localhost:19001", + Config: map[string]any{ + "driver": "machine", + "address": "localhost:19001", + "drivers": map[string]any{ + "machine": map[string]any{ + "api_key": "secretapikey", + }, + }, + }, + }, + }, + }, + }) + + assertHTTPEqual(t, c2.HTTP, &HTTP{ + Address: "localhost:19002", + Middlewares: make(map[string]map[string]any), + Services: map[string]ServicesConfig{ + "dataprovider": { + { + Address: "localhost:19002", + Config: map[string]any{ + "driver": "localhome", + }, + }, + }, + "sysinfo": { + { + Address: "localhost:19002", + Config: map[string]any{}, + }, + }, + }, + }) + + assert.Equal(t, c2.Serverless, &Serverless{ + Services: map[string]map[string]any{ + "notifications": { + "nats_address": "nats-server-01.example.com", + "nats_token": "secret-token-example", + }, + }, + }) +} + +func assertGRPCEqual(t *testing.T, g1, g2 *GRPC) { + assert.Equal(t, g1.Address, g2.Address) + assert.Equal(t, g1.Network, g2.Network) + assert.Equal(t, g1.ShutdownDeadline, g2.ShutdownDeadline) + assert.Equal(t, g1.EnableReflection, g2.EnableReflection) + assert.Equal(t, g1.Services, g2.Services) + assert.Equal(t, g1.Interceptors, g2.Interceptors) +} + +func assertHTTPEqual(t *testing.T, h1, h2 *HTTP) { + assert.Equal(t, h1.Network, h2.Network) + assert.Equal(t, h1.Network, h2.Network) + assert.Equal(t, h1.CertFile, h2.CertFile) + assert.Equal(t, h1.KeyFile, h2.KeyFile) + assert.Equal(t, h1.Services, h2.Services) + assert.Equal(t, h1.Middlewares, h2.Middlewares) +} diff --git a/cmd/revad/internal/config/grpc.go b/cmd/revad/internal/config/grpc.go index 1a5a582066..c1fc026e3f 100644 --- a/cmd/revad/internal/config/grpc.go +++ b/cmd/revad/internal/config/grpc.go @@ -11,10 +11,10 @@ type GRPC struct { ShutdownDeadline int `mapstructure:"shutdown_deadline" key:"shutdown_deadline"` EnableReflection bool `mapstructure:"enable_reflection" key:"enable_reflection"` - Services map[string]ServicesConfig `key:"services"` - Interceptors map[string]map[string]any `key:"interceptors"` + Services map[string]ServicesConfig `mapstructure:"-" key:"services"` + Interceptors map[string]map[string]any `mapstructure:"-" key:"interceptors"` - iterableImpl + iterablecmd/revad/internal/config/config_test.gocmd/revad/internal/config/config_test.goImpl } func (g *GRPC) services() map[string]ServicesConfig { return g.Services } diff --git a/cmd/revad/internal/config/http.go b/cmd/revad/internal/config/http.go index e1515afb1a..5e0f47886d 100644 --- a/cmd/revad/internal/config/http.go +++ b/cmd/revad/internal/config/http.go @@ -11,8 +11,8 @@ type HTTP struct { CertFile string `mapstructure:"certfile" key:"certfile"` KeyFile string `mapstructure:"keyfile" key:"keyfile"` - Services map[string]ServicesConfig `key:"services"` - Middlewares map[string]map[string]any `key:"middlewares"` + Services map[string]ServicesConfig `mapstructure:"-" key:"services"` + Middlewares map[string]map[string]any `mapstructure:"-" key:"middlewares"` iterableImpl } From cfd3ffdd732c3e236301ec2092a574150a7574da Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Thu, 15 Jun 2023 11:36:08 +0200 Subject: [PATCH 14/76] add tests for template --- cmd/revad/internal/config/templates_test.go | 71 +++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 cmd/revad/internal/config/templates_test.go diff --git a/cmd/revad/internal/config/templates_test.go b/cmd/revad/internal/config/templates_test.go new file mode 100644 index 0000000000..2f8e029628 --- /dev/null +++ b/cmd/revad/internal/config/templates_test.go @@ -0,0 +1,71 @@ +package config + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestApplyTemplate(t *testing.T) { + cfg1 := &Config{ + GRPC: &GRPC{ + Services: map[string]ServicesConfig{ + "authprovider": { + { + Address: "localhost:1900", + }, + }, + "authregistry": { + { + Address: "localhost:1901", + Config: map[string]any{ + "drivers": map[string]any{ + "static": map[string]any{ + "demo": "{{ grpc.services.authprovider.address }}", + }, + }, + }, + }, + }, + }, + }, + } + err := cfg1.ApplyTemplates() + assert.ErrorIs(t, err, nil) + assert.Equal(t, cfg1.GRPC.Services["authregistry"][0].Config["drivers"].(map[string]any)["static"].(map[string]any)["demo"], "localhost:1900") + + cfg2 := &Config{ + Vars: Vars{ + "db_username": "root", + "db_password": "secretpassword", + }, + GRPC: &GRPC{ + Services: map[string]ServicesConfig{ + "authregistry": { + { + Address: "localhost:1901", + Config: map[string]any{ + "drivers": map[string]any{ + "sql": map[string]any{ + "db_username": "{{ vars.db_username }}", + "db_password": "{{ vars.db_password }}", + "key": "value", + }, + }, + }, + }, + }, + }, + }, + } + + err = cfg2.ApplyTemplates() + assert.ErrorIs(t, err, nil) + assert.Equal(t, cfg2.GRPC.Services["authregistry"][0].Config["drivers"].(map[string]any)["sql"], + map[string]any{ + "db_username": "root", + "db_password": "secretpassword", + "key": "value", + }) + +} From 9729c8d63b5d9e96a9a2377d6f764a96b9ad114c Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Thu, 15 Jun 2023 11:36:38 +0200 Subject: [PATCH 15/76] fix --- cmd/revad/internal/config/grpc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/revad/internal/config/grpc.go b/cmd/revad/internal/config/grpc.go index c1fc026e3f..0f5a0392ba 100644 --- a/cmd/revad/internal/config/grpc.go +++ b/cmd/revad/internal/config/grpc.go @@ -14,7 +14,7 @@ type GRPC struct { Services map[string]ServicesConfig `mapstructure:"-" key:"services"` Interceptors map[string]map[string]any `mapstructure:"-" key:"interceptors"` - iterablecmd/revad/internal/config/config_test.gocmd/revad/internal/config/config_test.goImpl + iterableImpl } func (g *GRPC) services() map[string]ServicesConfig { return g.Services } From 2f42986185b70ed5e8da9c7f00a7deb4e44e5fd4 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Thu, 15 Jun 2023 11:38:39 +0200 Subject: [PATCH 16/76] max cpu is int --- cmd/revad/internal/config/config.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cmd/revad/internal/config/config.go b/cmd/revad/internal/config/config.go index 0f51199bc8..10ff7c665a 100644 --- a/cmd/revad/internal/config/config.go +++ b/cmd/revad/internal/config/config.go @@ -52,7 +52,7 @@ type Shared struct { } type Core struct { - MaxCPUs string `key:"max_cpus" mapstructure:"max_cpus"` + MaxCPUs int `key:"max_cpus" mapstructure:"max_cpus"` TracingEnabled bool `key:"tracing_enabled" mapstructure:"tracing_enabled"` TracingEndpoint string `key:"tracing_endpoint" mapstructure:"tracing_endpoint"` TracingCollector string `key:"tracing_collector" mapstructure:"tracing_collector"` @@ -72,7 +72,6 @@ func Load(r io.Reader) (*Config, error) { if err := c.parse(raw); err != nil { return nil, err } - c.Vars = make(Vars) return &c, nil } From a909cf8e8b58fe30080aa6b537f85fae26f8124b Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Thu, 15 Jun 2023 12:12:03 +0200 Subject: [PATCH 17/76] add support for squashed structs --- cmd/revad/internal/config/common.go | 4 +-- cmd/revad/internal/config/lookup.go | 25 ++++++++++++++++ cmd/revad/internal/config/lookup_test.go | 38 ++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 2 deletions(-) diff --git a/cmd/revad/internal/config/common.go b/cmd/revad/internal/config/common.go index 86f27ffc2c..14ce0f4a2e 100644 --- a/cmd/revad/internal/config/common.go +++ b/cmd/revad/internal/config/common.go @@ -19,8 +19,8 @@ type ServicesConfig []*DriverConfig func (c ServicesConfig) DriversNumber() int { return len(c) } type DriverConfig struct { - Config map[string]any - Address string + Config map[string]any `key:",squash"` + Address string `key:"address"` } func newSvcConfigFromList(l []map[string]any) (ServicesConfig, error) { diff --git a/cmd/revad/internal/config/lookup.go b/cmd/revad/internal/config/lookup.go index 9483e6a6f7..d907e69b2a 100644 --- a/cmd/revad/internal/config/lookup.go +++ b/cmd/revad/internal/config/lookup.go @@ -41,6 +41,31 @@ func lookupStruct(key string, v reflect.Value) (any, error) { continue } + if tag[1:] == "squash" { + if val.Kind() == reflect.Pointer { + val = val.Elem() + } + + var ( + v any + err error + ) + if val.Kind() == reflect.Struct { + v, err = lookupStruct(key, val) + } else if val.Kind() == reflect.Map { + v, err = lookupMap(key, val) + } else { + panic("squash not allowed on non map/struct types") + } + if errors.Is(err, ErrKeyNotFound) { + continue + } + if err != nil { + return nil, err + } + return v, nil + } + if tag != c.Key { continue } diff --git a/cmd/revad/internal/config/lookup_test.go b/cmd/revad/internal/config/lookup_test.go index af557905e9..ea805717ab 100644 --- a/cmd/revad/internal/config/lookup_test.go +++ b/cmd/revad/internal/config/lookup_test.go @@ -25,6 +25,16 @@ type StructWithNestedList struct { List []SimpleStruct `key:"list"` } +type Squashed struct { + Squashed SimpleStruct `key:",squash"` + Simple SimpleStruct +} + +type SquashedMap struct { + Squashed map[string]any `key:",squash"` + Simple SimpleStruct `key:"simple"` +} + func TestLookupStruct(t *testing.T) { tests := []struct { in any @@ -119,6 +129,34 @@ func TestLookupStruct(t *testing.T) { KeyB: "val_b[2]", }, }, + { + in: Squashed{ + Squashed: SimpleStruct{ + KeyA: "val_a[1]", + KeyB: "val_b[1]", + }, + Simple: SimpleStruct{ + KeyA: "val_a[2]", + KeyB: "val_b[2]", + }, + }, + key: ".keya", + val: "val_a[1]", + }, + { + in: SquashedMap{ + Squashed: map[string]any{ + "keya": "val_a[1]", + "keyb": "val_b[1]", + }, + Simple: SimpleStruct{ + KeyA: "val_a[2]", + KeyB: "val_b[2]", + }, + }, + key: ".keya", + val: "val_a[1]", + }, } for _, tt := range tests { From 0fa1dcb6242e1d878c9651452642105a8e7f08db Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Thu, 15 Jun 2023 16:43:51 +0200 Subject: [PATCH 18/76] dump config --- cmd/revad/internal/config/config.go | 10 + cmd/revad/internal/config/config_test.go | 211 +++++++++++++++++++++ cmd/revad/internal/config/dump.go | 122 ++++++++++++ cmd/revad/internal/config/dump_test.go | 229 +++++++++++++++++++++++ cmd/revad/internal/config/serverless.go | 2 +- 5 files changed, 573 insertions(+), 1 deletion(-) create mode 100644 cmd/revad/internal/config/dump.go create mode 100644 cmd/revad/internal/config/dump_test.go diff --git a/cmd/revad/internal/config/config.go b/cmd/revad/internal/config/config.go index 10ff7c665a..6237f2020c 100644 --- a/cmd/revad/internal/config/config.go +++ b/cmd/revad/internal/config/config.go @@ -19,6 +19,7 @@ package config import ( + "fmt" "io" "reflect" @@ -95,6 +96,15 @@ func (c *Config) ApplyTemplates() error { return c.applyTemplateByType(nil, reflect.ValueOf(c)) } +func (c *Config) Dump() map[string]any { + v := dumpByType(reflect.ValueOf(c)) + dump, ok := v.(map[string]any) + if !ok { + panic(fmt.Sprintf("dump should be a map: got %T", dump)) + } + return dump +} + func (c *Config) lookup(key string) (any, error) { return lookupByType(key, reflect.ValueOf(c)) } diff --git a/cmd/revad/internal/config/config_test.go b/cmd/revad/internal/config/config_test.go index c91aff1db1..a3007761c7 100644 --- a/cmd/revad/internal/config/config_test.go +++ b/cmd/revad/internal/config/config_test.go @@ -326,3 +326,214 @@ func assertHTTPEqual(t *testing.T, h1, h2 *HTTP) { assert.Equal(t, h1.Services, h2.Services) assert.Equal(t, h1.Middlewares, h2.Middlewares) } + +func TestDump(t *testing.T) { + config := &Config{ + Shared: &Shared{ + GatewaySVC: "localhost:9142", + JWTSecret: "secret", + }, + Log: &Log{ + Output: "/var/log/revad/revad-gateway.log", + Mode: "json", + Level: "trace", + }, + Core: &Core{ + MaxCPUs: 1, + TracingEnabled: true, + }, + Vars: Vars{ + "db_username": "root", + "db_password": "secretpassword", + }, + GRPC: &GRPC{ + ShutdownDeadline: 10, + EnableReflection: true, + Interceptors: make(map[string]map[string]any), + Services: map[string]ServicesConfig{ + "gateway": { + { + Config: map[string]any{ + "authregistrysvc": "localhost:19000", + }, + }, + }, + "authregistry": { + { + Address: "localhost:19000", + Config: map[string]any{ + "driver": "static", + "drivers": map[string]any{ + "static": map[string]any{ + "rules": map[string]any{ + "basic": "localhost:19001", + "machine": "localhost:19002", + }, + }, + }, + }, + }, + }, + "authprovider": { + { + Address: "localhost:19001", + Config: map[string]any{ + "driver": "ldap", + "address": "localhost:19001", + "drivers": map[string]any{ + "ldap": map[string]any{ + "password": "ldap", + }, + }, + }, + }, + { + Address: "localhost:19002", + Config: map[string]any{ + "driver": "machine", + "address": "localhost:19002", + "drivers": map[string]any{ + "machine": map[string]any{ + "api_key": "secretapikey", + }, + }, + }, + }, + }, + }, + }, + HTTP: &HTTP{ + Address: "localhost:19003", + Middlewares: make(map[string]map[string]any), + Services: map[string]ServicesConfig{ + "dataprovider": { + { + Address: "localhost:19003", + Config: map[string]any{ + "driver": "localhome", + }, + }, + }, + "sysinfo": { + { + Address: "localhost:19003", + Config: map[string]any{}, + }, + }, + }, + }, + Serverless: &Serverless{ + Services: map[string]map[string]any{ + "notifications": { + "nats_address": "nats-server-01.example.com", + "nats_token": "secret-token-example", + }, + }, + }, + } + + m := config.Dump() + assert.Equal(t, map[string]any{ + "shared": map[string]any{ + "jwt_secret": "secret", + "gatewaysvc": "localhost:9142", + "datagateway": "", + "skip_user_groups_in_token": false, + "blocked_users": []any{}, + }, + "log": map[string]any{ + "output": "/var/log/revad/revad-gateway.log", + "mode": "json", + "level": "trace", + }, + "core": map[string]any{ + "max_cpus": 1, + "tracing_enabled": true, + "tracing_endpoint": "", + "tracing_collector": "", + "tracing_service_name": "", + "tracing_service": "", + }, + "vars": map[string]any{ + "db_username": "root", + "db_password": "secretpassword", + }, + "grpc": map[string]any{ + "address": "", + "network": "", + "shutdown_deadline": 10, + "enable_reflection": true, + "interceptors": map[string]any{}, + "services": map[string]any{ + "gateway": []any{ + map[string]any{ + "address": "", + "authregistrysvc": "localhost:19000", + }, + }, + "authregistry": []any{ + map[string]any{ + "address": "localhost:19000", + "driver": "static", + "drivers": map[string]any{ + "static": map[string]any{ + "rules": map[string]any{ + "basic": "localhost:19001", + "machine": "localhost:19002", + }, + }, + }, + }, + }, + "authprovider": []any{ + map[string]any{ + "address": "localhost:19001", + "driver": "ldap", + "drivers": map[string]any{ + "ldap": map[string]any{ + "password": "ldap", + }, + }, + }, + map[string]any{ + "address": "localhost:19002", + "driver": "machine", + "drivers": map[string]any{ + "machine": map[string]any{ + "api_key": "secretapikey", + }, + }, + }, + }, + }, + }, + "http": map[string]any{ + "network": "", + "address": "localhost:19003", + "certfile": "", + "keyfile": "", + "middlewares": map[string]any{}, + "services": map[string]any{ + "dataprovider": []any{ + map[string]any{ + "address": "localhost:19003", + "driver": "localhome", + }, + }, + "sysinfo": []any{ + map[string]any{ + "address": "localhost:19003", + }, + }, + }, + }, + "serverless": map[string]any{ + "services": map[string]any{ + "notifications": map[string]any{ + "nats_address": "nats-server-01.example.com", + "nats_token": "secret-token-example", + }, + }, + }, + }, m) +} diff --git a/cmd/revad/internal/config/dump.go b/cmd/revad/internal/config/dump.go new file mode 100644 index 0000000000..3811594dc9 --- /dev/null +++ b/cmd/revad/internal/config/dump.go @@ -0,0 +1,122 @@ +package config + +import "reflect" + +func dumpStruct(v reflect.Value) map[string]any { + if v.Kind() != reflect.Struct { + panic("called dumpStruct on non struct type") + } + + n := v.NumField() + m := make(map[string]any, n) + + t := v.Type() + for i := 0; i < n; i++ { + e := v.Field(i) + f := t.Field(i) + + if !f.IsExported() { + continue + } + + if isFieldSquashed(f) { + if e.Kind() == reflect.Pointer { + e = e.Elem() + } + + var mm map[string]any + if e.Kind() == reflect.Struct { + mm = dumpStruct(e) + } else if e.Kind() == reflect.Map { + mm = dumpMap(e) + } else { + panic("squash not allowed on non map/struct types") + } + for k, v := range mm { + m[k] = v + } + continue + } + + m[fieldName(f)] = dumpByType(e) + } + return m +} + +func fieldName(f reflect.StructField) string { + fromtag := f.Tag.Get("key") + if fromtag != "" { + return fromtag + } + return f.Name +} + +func isFieldSquashed(f reflect.StructField) bool { + tag := f.Tag.Get("key") + return tag != "" && tag[1:] == "squash" +} + +func dumpMap(v reflect.Value) map[string]any { + if v.Kind() != reflect.Map { + panic("called dumpMap on non map type") + } + + m := make(map[string]any, v.Len()) + iter := v.MapRange() + for iter.Next() { + k := iter.Key() + e := iter.Value() + + key, ok := k.Interface().(string) + if !ok { + panic("key map must be a string") + } + + m[key] = dumpByType(e) + } + return m +} + +func dumpList(v reflect.Value) []any { + if v.Kind() != reflect.Array && v.Kind() != reflect.Slice { + panic("called dumpList on non array/slice type") + } + + n := v.Len() + l := make([]any, 0, n) + + for i := 0; i < n; i++ { + e := v.Index(i) + l = append(l, dumpByType(e)) + } + return l +} + +func dumpPrimitive(v reflect.Value) any { + if v.Kind() != reflect.Bool && v.Kind() != reflect.Int && v.Kind() != reflect.Int8 && + v.Kind() != reflect.Int16 && v.Kind() != reflect.Int32 && v.Kind() != reflect.Int64 && + v.Kind() != reflect.Uint && v.Kind() != reflect.Uint8 && v.Kind() != reflect.Uint16 && + v.Kind() != reflect.Uint32 && v.Kind() != reflect.Uint64 && v.Kind() != reflect.Float32 && + v.Kind() != reflect.Float64 && v.Kind() != reflect.String { + panic("called dumpPrimitive on non primitive type: " + v.Kind().String()) + } + return v.Interface() +} + +func dumpByType(v reflect.Value) any { + switch v.Kind() { + case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, + reflect.Float32, reflect.Float64, reflect.String: + return dumpPrimitive(v) + case reflect.Array, reflect.Slice: + return dumpList(v) + case reflect.Struct: + return dumpStruct(v) + case reflect.Map: + return dumpMap(v) + case reflect.Interface, reflect.Pointer: + return dumpByType(v.Elem()) + } + panic("type not supported: " + v.Kind().String()) +} diff --git a/cmd/revad/internal/config/dump_test.go b/cmd/revad/internal/config/dump_test.go new file mode 100644 index 0000000000..00014ac102 --- /dev/null +++ b/cmd/revad/internal/config/dump_test.go @@ -0,0 +1,229 @@ +package config + +import ( + "reflect" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestDumpMap(t *testing.T) { + tests := []struct { + in map[string]any + exp map[string]any + }{ + { + in: map[string]any{}, + exp: map[string]any{}, + }, + { + in: map[string]any{ + "simple": SimpleStruct{ + KeyA: "value_a", + KeyB: "value_b", + }, + }, + exp: map[string]any{ + "simple": map[string]any{ + "keya": "value_a", + "keyb": "value_b", + }, + }, + }, + { + in: map[string]any{ + "simple": SimpleStruct{ + KeyA: "value_a", + KeyB: "value_b", + }, + "map": map[string]any{ + "mapa": "value_mapa", + "mapb": "value_mapb", + }, + }, + exp: map[string]any{ + "simple": map[string]any{ + "keya": "value_a", + "keyb": "value_b", + }, + "map": map[string]any{ + "mapa": "value_mapa", + "mapb": "value_mapb", + }, + }, + }, + } + + for _, tt := range tests { + m := dumpMap(reflect.ValueOf(tt.in)) + assert.Equal(t, m, tt.exp) + } +} + +func TestDumpList(t *testing.T) { + tests := []struct { + in []any + exp []any + }{ + { + in: []any{}, + exp: []any{}, + }, + { + in: []any{1, 2, 3, 4}, + exp: []any{1, 2, 3, 4}, + }, + { + in: []any{ + map[string]any{ + "map": SimpleStruct{ + KeyA: "value_a", + KeyB: "value_b", + }, + }, + 5, + SimpleStruct{ + KeyA: "value_a", + KeyB: "value_b", + }, + }, + exp: []any{ + map[string]any{ + "map": map[string]any{ + "keya": "value_a", + "keyb": "value_b", + }, + }, + 5, + map[string]any{ + "keya": "value_a", + "keyb": "value_b", + }, + }, + }, + } + + for _, tt := range tests { + l := dumpList(reflect.ValueOf(tt.in)) + assert.Equal(t, l, tt.exp) + } +} + +func TestDumpStruct(t *testing.T) { + tests := []struct { + in any + exp map[string]any + }{ + { + in: SimpleStruct{ + KeyA: "value_a", + KeyB: "value_b", + }, + exp: map[string]any{ + "keya": "value_a", + "keyb": "value_b", + }, + }, + { + in: NestedStruct{ + Nested: SimpleStruct{ + KeyA: "value_a", + KeyB: "value_b", + }, + Value: 12, + }, + exp: map[string]any{ + "nested": map[string]any{ + "keya": "value_a", + "keyb": "value_b", + }, + "value": 12, + }, + }, + { + in: StructWithNestedMap{ + Map: map[string]any{ + "keya": "value_a", + "keyb": "value_b", + }, + }, + exp: map[string]any{ + "map": map[string]any{ + "keya": "value_a", + "keyb": "value_b", + }, + }, + }, + { + in: StructWithNestedList{ + List: []SimpleStruct{ + { + KeyA: "value_a[1]", + KeyB: "value_b[1]", + }, + { + KeyA: "value_a[2]", + KeyB: "value_b[2]", + }, + }, + }, + exp: map[string]any{ + "list": []any{ + map[string]any{ + "keya": "value_a[1]", + "keyb": "value_b[1]", + }, + map[string]any{ + "keya": "value_a[2]", + "keyb": "value_b[2]", + }, + }, + }, + }, + { + in: Squashed{ + Squashed: SimpleStruct{ + KeyA: "value_a[1]", + KeyB: "value_b[1]", + }, + Simple: SimpleStruct{ + KeyA: "value_a[2]", + KeyB: "value_b[2]", + }, + }, + exp: map[string]any{ + "keya": "value_a[1]", + "keyb": "value_b[1]", + "Simple": map[string]any{ + "keya": "value_a[2]", + "keyb": "value_b[2]", + }, + }, + }, + { + in: SquashedMap{ + Squashed: map[string]any{ + "keya": "val_a[1]", + "keyb": "val_b[1]", + }, + Simple: SimpleStruct{ + KeyA: "val_a[2]", + KeyB: "val_b[2]", + }, + }, + exp: map[string]any{ + "keya": "val_a[1]", + "keyb": "val_b[1]", + "simple": map[string]any{ + "keya": "val_a[2]", + "keyb": "val_b[2]", + }, + }, + }, + } + + for _, tt := range tests { + s := dumpStruct(reflect.ValueOf(tt.in)) + assert.Equal(t, tt.exp, s) + } +} diff --git a/cmd/revad/internal/config/serverless.go b/cmd/revad/internal/config/serverless.go index c807fec6b1..510e78dad3 100644 --- a/cmd/revad/internal/config/serverless.go +++ b/cmd/revad/internal/config/serverless.go @@ -6,7 +6,7 @@ import ( ) type Serverless struct { - Services map[string]map[string]any `mapstructure:"services"` + Services map[string]map[string]any `key:"services" mapstructure:"services"` } func (c *Config) parseServerless(raw map[string]any) error { From 2f7ce11fab709c51dad877842ede55f9def5d2a7 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Thu, 15 Jun 2023 17:44:08 +0200 Subject: [PATCH 19/76] add comments --- cmd/revad/internal/config/common.go | 12 +++++++++++- cmd/revad/internal/config/config.go | 9 +++++++++ cmd/revad/internal/config/grpc.go | 1 + cmd/revad/internal/config/http.go | 1 + cmd/revad/internal/config/lookup.go | 2 ++ cmd/revad/internal/config/parser.go | 15 ++++++--------- cmd/revad/internal/config/serverless.go | 2 ++ 7 files changed, 32 insertions(+), 10 deletions(-) diff --git a/cmd/revad/internal/config/common.go b/cmd/revad/internal/config/common.go index 14ce0f4a2e..6cb8c21a68 100644 --- a/cmd/revad/internal/config/common.go +++ b/cmd/revad/internal/config/common.go @@ -14,10 +14,13 @@ type iterable interface { type iterableImpl struct{ i iterable } +// ServicesConfig holds the configuration for reva services. type ServicesConfig []*DriverConfig +// DriversNumber return the number of driver configured for the service. func (c ServicesConfig) DriversNumber() int { return len(c) } +// DriverConfig holds the configuration for a driver. type DriverConfig struct { Config map[string]any `key:",squash"` Address string `key:"address"` @@ -79,6 +82,7 @@ func parseMiddlwares(cfg map[string]any, key string) (map[string]map[string]any, return m, nil } +// Service contains the configuration for a service. type Service struct { Address string Name string @@ -87,18 +91,24 @@ type Service struct { raw *DriverConfig } +// SetAddress sets the address for the service in the configuration. func (s *Service) SetAddress(address string) { s.Address = address s.raw.Address = address } +// ServiceFunc is an helper function used to pass the service config +// to the ForEachService func. type ServiceFunc func(*Service) +// Interceptor contains the configuration for an interceptor. type Interceptor struct { Name string Config map[string]any } +// InterceptorFunc is an helper function used to pass the interface config +// to the ForEachInterceptor func. type InterceptorFunc func(*Interceptor) // ForEachService iterates to each service/driver calling the function f. @@ -115,7 +125,7 @@ func (i iterableImpl) ForEachService(f ServiceFunc) { } } -// ForEachInterceptor iterates to each middlware calling the function f. +// ForEachInterceptor iterates to each middleware calling the function f. func (i iterableImpl) ForEachInterceptor(f InterceptorFunc) { for name, c := range i.i.interceptors() { f(&Interceptor{ diff --git a/cmd/revad/internal/config/config.go b/cmd/revad/internal/config/config.go index 6237f2020c..301c2304e2 100644 --- a/cmd/revad/internal/config/config.go +++ b/cmd/revad/internal/config/config.go @@ -28,6 +28,7 @@ import ( "github.com/pkg/errors" ) +// Config holds the reva configuration. type Config struct { GRPC *GRPC `key:"grpc" mapstructure:"-"` HTTP *HTTP `key:"http" mapstructure:"-"` @@ -38,12 +39,14 @@ type Config struct { Vars Vars `key:"vars" mapstructure:"vars" template:"-"` } +// Log holds the configuration for the logger. type Log struct { Output string `key:"output" mapstructure:"output"` Mode string `key:"mode" mapstructure:"mode"` Level string `key:"level" mapstructure:"level"` } +// Shared holds the shared configuration. type Shared struct { JWTSecret string `key:"jwt_secret" mapstructure:"jwt_secret"` GatewaySVC string `key:"gatewaysvc" mapstructure:"gatewaysvc"` @@ -52,6 +55,7 @@ type Shared struct { BlockedUsers []string `key:"blocked_users" mapstructure:"blocked_users"` } +// Core holds the core configuration. type Core struct { MaxCPUs int `key:"max_cpus" mapstructure:"max_cpus"` TracingEnabled bool `key:"tracing_enabled" mapstructure:"tracing_enabled"` @@ -61,6 +65,8 @@ type Core struct { TracingService string `key:"tracing_service" mapstructure:"tracing_service"` } +// Vars holds the a set of configuration paramenters that +// can be references by other parts of the configuration. type Vars map[string]any // Load loads the configuration from the reader. @@ -92,10 +98,13 @@ func (c *Config) parse(raw map[string]any) error { return nil } +// ApplyTemplates applies the templates defined in the configuration, +// replacing the template string with the value pointed by the given key. func (c *Config) ApplyTemplates() error { return c.applyTemplateByType(nil, reflect.ValueOf(c)) } +// Dump returns the configuration as a map. func (c *Config) Dump() map[string]any { v := dumpByType(reflect.ValueOf(c)) dump, ok := v.(map[string]any) diff --git a/cmd/revad/internal/config/grpc.go b/cmd/revad/internal/config/grpc.go index 0f5a0392ba..e11f798de6 100644 --- a/cmd/revad/internal/config/grpc.go +++ b/cmd/revad/internal/config/grpc.go @@ -5,6 +5,7 @@ import ( "github.com/pkg/errors" ) +// GRPC holds the configuration for the GRPC services. type GRPC struct { Address string `mapstructure:"address" key:"address"` Network string `mapstructure:"network" key:"network"` diff --git a/cmd/revad/internal/config/http.go b/cmd/revad/internal/config/http.go index 5e0f47886d..26cd3fba22 100644 --- a/cmd/revad/internal/config/http.go +++ b/cmd/revad/internal/config/http.go @@ -5,6 +5,7 @@ import ( "github.com/pkg/errors" ) +// HTTP holds the configuration for the HTTP services. type HTTP struct { Network string `mapstructure:"network" key:"network"` Address string `mapstructure:"address" key:"address"` diff --git a/cmd/revad/internal/config/lookup.go b/cmd/revad/internal/config/lookup.go index d907e69b2a..acc9e36a64 100644 --- a/cmd/revad/internal/config/lookup.go +++ b/cmd/revad/internal/config/lookup.go @@ -7,6 +7,8 @@ import ( "github.com/pkg/errors" ) +// ErrKeyNotFound is the error returned when a key does not exist +// in the configuration. var ErrKeyNotFound = errors.New("key not found") func lookupStruct(key string, v reflect.Value) (any, error) { diff --git a/cmd/revad/internal/config/parser.go b/cmd/revad/internal/config/parser.go index 5b764d6826..7476b16216 100644 --- a/cmd/revad/internal/config/parser.go +++ b/cmd/revad/internal/config/parser.go @@ -8,19 +8,16 @@ import ( "github.com/pkg/errors" ) -type Command interface { - isCommand() -} +// Command is the command to execute after parsing the template. +type Command interface{ isCommand() } -type FieldByKey struct { - Key string -} +// FieldByKey instructs the template runner to get a field by a key. +type FieldByKey struct{ Key string } func (FieldByKey) isCommand() {} -type FieldByIndex struct { - Index int -} +// FieldByIndex instructs the template runner to get a field by an index. +type FieldByIndex struct{ Index int } func (FieldByIndex) isCommand() {} diff --git a/cmd/revad/internal/config/serverless.go b/cmd/revad/internal/config/serverless.go index 510e78dad3..fd5ab7ce8c 100644 --- a/cmd/revad/internal/config/serverless.go +++ b/cmd/revad/internal/config/serverless.go @@ -5,6 +5,7 @@ import ( "github.com/pkg/errors" ) +// Serverless holds the configuration for the serverless services. type Serverless struct { Services map[string]map[string]any `key:"services" mapstructure:"services"` } @@ -24,6 +25,7 @@ func (c *Config) parseServerless(raw map[string]any) error { return nil } +// ForEach iterates to each service calling the function f. func (s *Serverless) ForEach(f func(name string, config map[string]any)) { for name, cfg := range s.Services { f(name, cfg) From 2da6f155c7373e66b1f302a1299cc8da1966012a Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Fri, 16 Jun 2023 09:05:41 +0200 Subject: [PATCH 20/76] expose Lookup method from config --- cmd/revad/internal/config/config.go | 2 +- cmd/revad/internal/config/templates.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/revad/internal/config/config.go b/cmd/revad/internal/config/config.go index 301c2304e2..a4f94ffaf2 100644 --- a/cmd/revad/internal/config/config.go +++ b/cmd/revad/internal/config/config.go @@ -114,6 +114,6 @@ func (c *Config) Dump() map[string]any { return dump } -func (c *Config) lookup(key string) (any, error) { +func (c *Config) Lookup(key string) (any, error) { return lookupByType(key, reflect.ValueOf(c)) } diff --git a/cmd/revad/internal/config/templates.go b/cmd/revad/internal/config/templates.go index b3ef47409f..5b85bb1aeb 100644 --- a/cmd/revad/internal/config/templates.go +++ b/cmd/revad/internal/config/templates.go @@ -129,7 +129,7 @@ func (c *Config) applyTemplateInterface(p parent, v reflect.Value) error { } key := keyFromTemplate(s) - val, err := c.lookup(key) + val, err := c.Lookup(key) if err != nil { return err } @@ -154,7 +154,7 @@ func (c *Config) applyTemplateString(p parent, v reflect.Value) error { } key := keyFromTemplate(s) - val, err := c.lookup(key) + val, err := c.Lookup(key) if err != nil { return err } From 98ad8e4e7fddd25ffc42be8b8a892b218533352a Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Fri, 16 Jun 2023 09:28:02 +0200 Subject: [PATCH 21/76] refactor --- cmd/revad/internal/config/config.go | 8 +- cmd/revad/internal/config/templates.go | 100 ++++++++++---------- cmd/revad/internal/config/templates_test.go | 4 +- 3 files changed, 58 insertions(+), 54 deletions(-) diff --git a/cmd/revad/internal/config/config.go b/cmd/revad/internal/config/config.go index a4f94ffaf2..e0711fb385 100644 --- a/cmd/revad/internal/config/config.go +++ b/cmd/revad/internal/config/config.go @@ -69,6 +69,10 @@ type Core struct { // can be references by other parts of the configuration. type Vars map[string]any +type Lookuper interface { + Lookup(key string) (any, error) +} + // Load loads the configuration from the reader. func Load(r io.Reader) (*Config, error) { var c Config @@ -100,8 +104,8 @@ func (c *Config) parse(raw map[string]any) error { // ApplyTemplates applies the templates defined in the configuration, // replacing the template string with the value pointed by the given key. -func (c *Config) ApplyTemplates() error { - return c.applyTemplateByType(nil, reflect.ValueOf(c)) +func (c *Config) ApplyTemplates(l Lookuper) error { + return applyTemplateByType(l, nil, reflect.ValueOf(c)) } // Dump returns the configuration as a map. diff --git a/cmd/revad/internal/config/templates.go b/cmd/revad/internal/config/templates.go index 5b85bb1aeb..27551a78bf 100644 --- a/cmd/revad/internal/config/templates.go +++ b/cmd/revad/internal/config/templates.go @@ -7,39 +7,7 @@ import ( "github.com/pkg/errors" ) -type parent interface { - SetValue(v any) -} - -type parentList struct { - List reflect.Value - Index int -} - -type parentMap struct { - Map reflect.Value - Key any -} - -type parentStruct struct { - Struct reflect.Value - Field int -} - -func (p parentList) SetValue(v any) { - el := p.List.Index(p.Index) - el.Set(reflect.ValueOf(v)) -} - -func (p parentMap) SetValue(v any) { - p.Map.SetMapIndex(reflect.ValueOf(p.Key), reflect.ValueOf(v)) -} - -func (p parentStruct) SetValue(v any) { - p.Struct.Field(p.Field).Set(reflect.ValueOf(v)) -} - -func (c *Config) applyTemplateStruct(p parent, v reflect.Value) error { +func applyTemplateStruct(l Lookuper, p setter, v reflect.Value) error { if v.Kind() != reflect.Struct { panic("called applyTemplateStruct on non struct type") } @@ -58,46 +26,46 @@ func (c *Config) applyTemplateStruct(p parent, v reflect.Value) error { continue } - if err := c.applyTemplateByType(parentStruct{Struct: v, Field: i}, el); err != nil { + if err := applyTemplateByType(l, setterStruct{Struct: v, Field: i}, el); err != nil { return err } } return nil } -func (c *Config) applyTemplateByType(p parent, v reflect.Value) error { +func applyTemplateByType(l Lookuper, p setter, v reflect.Value) error { switch v.Kind() { case reflect.String: - return c.applyTemplateString(p, v) + return applyTemplateString(l, p, v) case reflect.Array, reflect.Slice: - return c.applyTemplateList(p, v) + return applyTemplateList(l, p, v) case reflect.Struct: - return c.applyTemplateStruct(p, v) + return applyTemplateStruct(l, p, v) case reflect.Map: - return c.applyTemplateMap(p, v) + return applyTemplateMap(l, p, v) case reflect.Interface: - return c.applyTemplateInterface(p, v) + return applyTemplateInterface(l, p, v) case reflect.Pointer: - return c.applyTemplateByType(p, v.Elem()) + return applyTemplateByType(l, p, v.Elem()) } return nil } -func (c *Config) applyTemplateList(p parent, v reflect.Value) error { +func applyTemplateList(l Lookuper, p setter, v reflect.Value) error { if v.Kind() != reflect.Array && v.Kind() != reflect.Slice { panic("called applyTemplateList on non array/slice type") } for i := 0; i < v.Len(); i++ { el := v.Index(i) - if err := c.applyTemplateByType(parentList{List: v, Index: i}, el); err != nil { + if err := applyTemplateByType(l, setterList{List: v, Index: i}, el); err != nil { return err } } return nil } -func (c *Config) applyTemplateMap(p parent, v reflect.Value) error { +func applyTemplateMap(l Lookuper, p setter, v reflect.Value) error { if v.Kind() != reflect.Map { panic("called applyTemplateMap on non map type") } @@ -106,21 +74,21 @@ func (c *Config) applyTemplateMap(p parent, v reflect.Value) error { for iter.Next() { k := iter.Key() el := v.MapIndex(k) - if err := c.applyTemplateByType(parentMap{Map: v, Key: k.Interface()}, el); err != nil { + if err := applyTemplateByType(l, setterMap{Map: v, Key: k.Interface()}, el); err != nil { return err } } return nil } -func (c *Config) applyTemplateInterface(p parent, v reflect.Value) error { +func applyTemplateInterface(l Lookuper, p setter, v reflect.Value) error { if v.Kind() != reflect.Interface { panic("called applyTemplateInterface on non interface value") } s, ok := v.Interface().(string) if !ok { - return c.applyTemplateByType(p, v.Elem()) + return applyTemplateByType(l, p, v.Elem()) } if !isTemplate(s) { @@ -129,7 +97,7 @@ func (c *Config) applyTemplateInterface(p parent, v reflect.Value) error { } key := keyFromTemplate(s) - val, err := c.Lookup(key) + val, err := l.Lookup(key) if err != nil { return err } @@ -138,7 +106,7 @@ func (c *Config) applyTemplateInterface(p parent, v reflect.Value) error { return nil } -func (c *Config) applyTemplateString(p parent, v reflect.Value) error { +func applyTemplateString(l Lookuper, p setter, v reflect.Value) error { if v.Kind() != reflect.String { panic("called applyTemplateString on non string type") } @@ -154,7 +122,7 @@ func (c *Config) applyTemplateString(p parent, v reflect.Value) error { } key := keyFromTemplate(s) - val, err := c.Lookup(key) + val, err := l.Lookup(key) if err != nil { return err } @@ -179,3 +147,35 @@ func keyFromTemplate(s string) string { s = strings.TrimSuffix(s, "}}") return "." + strings.TrimSpace(s) } + +type setter interface { + SetValue(v any) +} + +type setterList struct { + List reflect.Value + Index int +} + +type setterMap struct { + Map reflect.Value + Key any +} + +type setterStruct struct { + Struct reflect.Value + Field int +} + +func (s setterList) SetValue(v any) { + el := s.List.Index(s.Index) + el.Set(reflect.ValueOf(v)) +} + +func (s setterMap) SetValue(v any) { + s.Map.SetMapIndex(reflect.ValueOf(s.Key), reflect.ValueOf(v)) +} + +func (s setterStruct) SetValue(v any) { + s.Struct.Field(s.Field).Set(reflect.ValueOf(v)) +} diff --git a/cmd/revad/internal/config/templates_test.go b/cmd/revad/internal/config/templates_test.go index 2f8e029628..820f0a2663 100644 --- a/cmd/revad/internal/config/templates_test.go +++ b/cmd/revad/internal/config/templates_test.go @@ -30,7 +30,7 @@ func TestApplyTemplate(t *testing.T) { }, }, } - err := cfg1.ApplyTemplates() + err := cfg1.ApplyTemplates(cfg1) assert.ErrorIs(t, err, nil) assert.Equal(t, cfg1.GRPC.Services["authregistry"][0].Config["drivers"].(map[string]any)["static"].(map[string]any)["demo"], "localhost:1900") @@ -59,7 +59,7 @@ func TestApplyTemplate(t *testing.T) { }, } - err = cfg2.ApplyTemplates() + err = cfg2.ApplyTemplates(cfg2) assert.ErrorIs(t, err, nil) assert.Equal(t, cfg2.GRPC.Services["authregistry"][0].Config["drivers"].(map[string]any)["sql"], map[string]any{ From 48bbba0c50dae45d1e1f113c72299500cd728d4d Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Fri, 16 Jun 2023 09:34:40 +0200 Subject: [PATCH 22/76] add multi config lookuper --- cmd/revad/internal/config/config.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/cmd/revad/internal/config/config.go b/cmd/revad/internal/config/config.go index e0711fb385..fc8fd9ef98 100644 --- a/cmd/revad/internal/config/config.go +++ b/cmd/revad/internal/config/config.go @@ -121,3 +121,18 @@ func (c *Config) Dump() map[string]any { func (c *Config) Lookup(key string) (any, error) { return lookupByType(key, reflect.ValueOf(c)) } + +// MultiConfigLookuper implements the Lookuper interface, +// to lookup a key from different configs. +type MultiConfigLookuper struct { + configs []*Config +} + +// NewMultiConfigLookuper creates a new MultiConfigLookuper. +func NewMultiConfigLookuper(c ...*Config) MultiConfigLookuper { + return MultiConfigLookuper{configs: c} +} + +func (m MultiConfigLookuper) Lookup(key string) (any, error) { + return nil, nil +} From 43681f23c9adddec037723a08db335b0538858c2 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Fri, 16 Jun 2023 13:42:11 +0200 Subject: [PATCH 23/76] moved internal to pkg dir --- cmd/revad/{internal => pkg}/config/common.go | 0 cmd/revad/{internal => pkg}/config/config.go | 0 cmd/revad/{internal => pkg}/config/config_test.go | 0 cmd/revad/{internal => pkg}/config/dump.go | 0 cmd/revad/{internal => pkg}/config/dump_test.go | 0 cmd/revad/{internal => pkg}/config/grpc.go | 0 cmd/revad/{internal => pkg}/config/http.go | 0 cmd/revad/{internal => pkg}/config/lookup.go | 0 cmd/revad/{internal => pkg}/config/lookup_test.go | 0 cmd/revad/{internal => pkg}/config/parser.go | 0 cmd/revad/{internal => pkg}/config/parser_test.go | 0 cmd/revad/{internal => pkg}/config/serverless.go | 0 cmd/revad/{internal => pkg}/config/templates.go | 0 cmd/revad/{internal => pkg}/config/templates_test.go | 0 cmd/revad/{internal => pkg}/grace/grace.go | 0 15 files changed, 0 insertions(+), 0 deletions(-) rename cmd/revad/{internal => pkg}/config/common.go (100%) rename cmd/revad/{internal => pkg}/config/config.go (100%) rename cmd/revad/{internal => pkg}/config/config_test.go (100%) rename cmd/revad/{internal => pkg}/config/dump.go (100%) rename cmd/revad/{internal => pkg}/config/dump_test.go (100%) rename cmd/revad/{internal => pkg}/config/grpc.go (100%) rename cmd/revad/{internal => pkg}/config/http.go (100%) rename cmd/revad/{internal => pkg}/config/lookup.go (100%) rename cmd/revad/{internal => pkg}/config/lookup_test.go (100%) rename cmd/revad/{internal => pkg}/config/parser.go (100%) rename cmd/revad/{internal => pkg}/config/parser_test.go (100%) rename cmd/revad/{internal => pkg}/config/serverless.go (100%) rename cmd/revad/{internal => pkg}/config/templates.go (100%) rename cmd/revad/{internal => pkg}/config/templates_test.go (100%) rename cmd/revad/{internal => pkg}/grace/grace.go (100%) diff --git a/cmd/revad/internal/config/common.go b/cmd/revad/pkg/config/common.go similarity index 100% rename from cmd/revad/internal/config/common.go rename to cmd/revad/pkg/config/common.go diff --git a/cmd/revad/internal/config/config.go b/cmd/revad/pkg/config/config.go similarity index 100% rename from cmd/revad/internal/config/config.go rename to cmd/revad/pkg/config/config.go diff --git a/cmd/revad/internal/config/config_test.go b/cmd/revad/pkg/config/config_test.go similarity index 100% rename from cmd/revad/internal/config/config_test.go rename to cmd/revad/pkg/config/config_test.go diff --git a/cmd/revad/internal/config/dump.go b/cmd/revad/pkg/config/dump.go similarity index 100% rename from cmd/revad/internal/config/dump.go rename to cmd/revad/pkg/config/dump.go diff --git a/cmd/revad/internal/config/dump_test.go b/cmd/revad/pkg/config/dump_test.go similarity index 100% rename from cmd/revad/internal/config/dump_test.go rename to cmd/revad/pkg/config/dump_test.go diff --git a/cmd/revad/internal/config/grpc.go b/cmd/revad/pkg/config/grpc.go similarity index 100% rename from cmd/revad/internal/config/grpc.go rename to cmd/revad/pkg/config/grpc.go diff --git a/cmd/revad/internal/config/http.go b/cmd/revad/pkg/config/http.go similarity index 100% rename from cmd/revad/internal/config/http.go rename to cmd/revad/pkg/config/http.go diff --git a/cmd/revad/internal/config/lookup.go b/cmd/revad/pkg/config/lookup.go similarity index 100% rename from cmd/revad/internal/config/lookup.go rename to cmd/revad/pkg/config/lookup.go diff --git a/cmd/revad/internal/config/lookup_test.go b/cmd/revad/pkg/config/lookup_test.go similarity index 100% rename from cmd/revad/internal/config/lookup_test.go rename to cmd/revad/pkg/config/lookup_test.go diff --git a/cmd/revad/internal/config/parser.go b/cmd/revad/pkg/config/parser.go similarity index 100% rename from cmd/revad/internal/config/parser.go rename to cmd/revad/pkg/config/parser.go diff --git a/cmd/revad/internal/config/parser_test.go b/cmd/revad/pkg/config/parser_test.go similarity index 100% rename from cmd/revad/internal/config/parser_test.go rename to cmd/revad/pkg/config/parser_test.go diff --git a/cmd/revad/internal/config/serverless.go b/cmd/revad/pkg/config/serverless.go similarity index 100% rename from cmd/revad/internal/config/serverless.go rename to cmd/revad/pkg/config/serverless.go diff --git a/cmd/revad/internal/config/templates.go b/cmd/revad/pkg/config/templates.go similarity index 100% rename from cmd/revad/internal/config/templates.go rename to cmd/revad/pkg/config/templates.go diff --git a/cmd/revad/internal/config/templates_test.go b/cmd/revad/pkg/config/templates_test.go similarity index 100% rename from cmd/revad/internal/config/templates_test.go rename to cmd/revad/pkg/config/templates_test.go diff --git a/cmd/revad/internal/grace/grace.go b/cmd/revad/pkg/grace/grace.go similarity index 100% rename from cmd/revad/internal/grace/grace.go rename to cmd/revad/pkg/grace/grace.go From 525e6b7d6de630a87bd1b3291c0c38b950fed44b Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Fri, 16 Jun 2023 13:50:41 +0200 Subject: [PATCH 24/76] add TakeFirst func --- pkg/utils/list/list.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pkg/utils/list/list.go b/pkg/utils/list/list.go index 2b1962ab2b..6812bfc306 100644 --- a/pkg/utils/list/list.go +++ b/pkg/utils/list/list.go @@ -34,3 +34,15 @@ func Remove[T any](l []T, i int) []T { l[i] = l[len(l)-1] return l[:len(l)-1] } + +// TakeFirst returns the first elemen, if any, that satisfies +// the predicate p. +func TakeFirst[T any](l []T, p func(T) bool) (T, bool) { + for _, e := range l { + if p(e) { + return e, true + } + } + var z T + return z, false +} From 8a472a6597cdd6fe116616b85ec588222d4ec02c Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Fri, 16 Jun 2023 15:17:16 +0200 Subject: [PATCH 25/76] revert max_cpu config to string --- cmd/revad/pkg/config/config.go | 2 +- cmd/revad/pkg/config/config_test.go | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cmd/revad/pkg/config/config.go b/cmd/revad/pkg/config/config.go index fc8fd9ef98..46a225e5f9 100644 --- a/cmd/revad/pkg/config/config.go +++ b/cmd/revad/pkg/config/config.go @@ -57,7 +57,7 @@ type Shared struct { // Core holds the core configuration. type Core struct { - MaxCPUs int `key:"max_cpus" mapstructure:"max_cpus"` + MaxCPUs string `key:"max_cpus" mapstructure:"max_cpus"` TracingEnabled bool `key:"tracing_enabled" mapstructure:"tracing_enabled"` TracingEndpoint string `key:"tracing_endpoint" mapstructure:"tracing_endpoint"` TracingCollector string `key:"tracing_collector" mapstructure:"tracing_collector"` diff --git a/cmd/revad/pkg/config/config_test.go b/cmd/revad/pkg/config/config_test.go index a3007761c7..021c1d7ead 100644 --- a/cmd/revad/pkg/config/config_test.go +++ b/cmd/revad/pkg/config/config_test.go @@ -151,7 +151,7 @@ mode = "json" level = "trace" [core] -max_cpus = 1 +max_cpus = "1" tracing_enabled = true [vars] @@ -213,7 +213,7 @@ nats_token = "secret-token-example"` }) assert.Equal(t, c2.Core, &Core{ - MaxCPUs: 1, + MaxCPUs: "1", TracingEnabled: true, }) @@ -339,7 +339,7 @@ func TestDump(t *testing.T) { Level: "trace", }, Core: &Core{ - MaxCPUs: 1, + MaxCPUs: "1", TracingEnabled: true, }, Vars: Vars{ @@ -447,7 +447,7 @@ func TestDump(t *testing.T) { "level": "trace", }, "core": map[string]any{ - "max_cpus": 1, + "max_cpus": "1", "tracing_enabled": true, "tracing_endpoint": "", "tracing_collector": "", From cbf32acfa93cc7c16238371deee8703f1e9d9123 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Mon, 19 Jun 2023 14:21:14 +0200 Subject: [PATCH 26/76] simplified reva http server --- pkg/rhttp/rhttp.go | 250 +++++++++++++++++++++------------------------ 1 file changed, 116 insertions(+), 134 deletions(-) diff --git a/pkg/rhttp/rhttp.go b/pkg/rhttp/rhttp.go index de000aaac1..423b5bc377 100644 --- a/pkg/rhttp/rhttp.go +++ b/pkg/rhttp/rhttp.go @@ -20,7 +20,6 @@ package rhttp import ( "context" - "fmt" "net" "net/http" "path" @@ -28,81 +27,73 @@ import ( "strings" "time" - "github.com/cs3org/reva/internal/http/interceptors/appctx" - "github.com/cs3org/reva/internal/http/interceptors/auth" - "github.com/cs3org/reva/internal/http/interceptors/log" - "github.com/cs3org/reva/internal/http/interceptors/providerauthorizer" "github.com/cs3org/reva/pkg/rhttp/global" rtrace "github.com/cs3org/reva/pkg/trace" - "github.com/mitchellh/mapstructure" "github.com/pkg/errors" "github.com/rs/zerolog" "go.opentelemetry.io/otel/propagation" ) -// New returns a new server. -func New(m interface{}, l zerolog.Logger) (*Server, error) { - conf := &config{} - if err := mapstructure.Decode(m, conf); err != nil { - return nil, err +type Config func(*Server) + +func WithServices(services map[string]global.Service) Config { + return func(s *Server) { + s.Services = services + } +} + +func WithMiddlewares(middlewares []*MiddlewareTriple) Config { + return func(s *Server) { + s.middlewares = middlewares + } +} + +func WithCertAndKeyFiles(cert, key string) Config { + return func(s *Server) { + s.CertFile = cert + s.KeyFile = key } +} - conf.init() +func WithLogger(log zerolog.Logger) Config { + return func(s *Server) { + s.log = log + } +} +// New returns a new server. +func New(c ...Config) (*Server, error) { httpServer := &http.Server{} s := &Server{ httpServer: httpServer, - conf: conf, svcs: map[string]global.Service{}, unprotected: []string{}, handlers: map[string]http.Handler{}, - log: l, } + for _, cc := range c { + cc(s) + } + s.registerServices() return s, nil } // Server contains the server info. type Server struct { + Services map[string]global.Service // map key is service name + CertFile string + KeyFile string + httpServer *http.Server - conf *config listener net.Listener svcs map[string]global.Service // map key is svc Prefix unprotected []string handlers map[string]http.Handler - middlewares []*middlewareTriple + middlewares []*MiddlewareTriple log zerolog.Logger } -type config struct { - Network string `mapstructure:"network"` - Address string `mapstructure:"address"` - Services map[string]map[string]interface{} `mapstructure:"services"` - Middlewares map[string]map[string]interface{} `mapstructure:"middlewares"` - CertFile string `mapstructure:"certfile"` - KeyFile string `mapstructure:"keyfile"` -} - -func (c *config) init() { - // apply defaults - if c.Network == "" { - c.Network = "tcp" - } - - if c.Address == "" { - c.Address = "0.0.0.0:19001" - } -} - // Start starts the server. func (s *Server) Start(ln net.Listener) error { - if err := s.registerServices(); err != nil { - return err - } - - if err := s.registerMiddlewares(); err != nil { - return err - } - handler, err := s.getHandler() if err != nil { return errors.Wrap(err, "rhttp: error creating http handler") @@ -111,11 +102,11 @@ func (s *Server) Start(ln net.Listener) error { s.httpServer.Handler = handler s.listener = ln - if (s.conf.CertFile != "") && (s.conf.KeyFile != "") { - s.log.Info().Msgf("https server listening at https://%s '%s' '%s'", s.conf.Address, s.conf.CertFile, s.conf.KeyFile) - err = s.httpServer.ServeTLS(s.listener, s.conf.CertFile, s.conf.KeyFile) + if (s.CertFile != "") && (s.KeyFile != "") { + s.log.Info().Msgf("https server listening at https://%s using cert file '%s' and key file '%s'", s.listener.Addr(), s.CertFile, s.KeyFile) + err = s.httpServer.ServeTLS(s.listener, s.CertFile, s.KeyFile) } else { - s.log.Info().Msgf("http server listening at http://%s '%s' '%s'", s.conf.Address, s.conf.CertFile, s.conf.KeyFile) + s.log.Info().Msgf("http server listening at http://%s '%s' '%s'", s.listener.Addr()) err = s.httpServer.Serve(s.listener) } if err == nil || err == http.ErrServerClosed { @@ -148,12 +139,12 @@ func (s *Server) closeServices() { // Network return the network type. func (s *Server) Network() string { - return s.conf.Network + return s.listener.Addr().Network() } // Address returns the network address. func (s *Server) Address() string { - return s.conf.Address + return s.listener.Addr().String() } // GracefulStop gracefully stops the server. @@ -162,69 +153,74 @@ func (s *Server) GracefulStop() error { return s.httpServer.Shutdown(context.Background()) } -// middlewareTriple represents a middleware with the +// MiddlewareTriple represents a middleware with the // priority to be chained. -type middlewareTriple struct { +type MiddlewareTriple struct { Name string Priority int Middleware global.Middleware } -func (s *Server) registerMiddlewares() error { - middlewares := []*middlewareTriple{} - for name, newFunc := range global.NewMiddlewares { - if s.isMiddlewareEnabled(name) { - m, prio, err := newFunc(s.conf.Middlewares[name]) - if err != nil { - err = errors.Wrapf(err, "error creating new middleware: %s,", name) - return err - } - middlewares = append(middlewares, &middlewareTriple{ - Name: name, - Priority: prio, - Middleware: m, - }) - s.log.Info().Msgf("http middleware enabled: %s", name) - } - } - s.middlewares = middlewares - return nil -} - -func (s *Server) isMiddlewareEnabled(name string) bool { - _, ok := s.conf.Middlewares[name] - return ok -} - -func (s *Server) registerServices() error { - for svcName := range s.conf.Services { - if s.isServiceEnabled(svcName) { - newFunc := global.Services[svcName] - svcLogger := s.log.With().Str("service", svcName).Logger() - svc, err := newFunc(s.conf.Services[svcName], &svcLogger) - if err != nil { - err = errors.Wrapf(err, "http service %s could not be started,", svcName) - return err - } - - // instrument services with opencensus tracing. - h := traceHandler(svcName, svc.Handler()) - s.handlers[svc.Prefix()] = h - s.svcs[svc.Prefix()] = svc - s.unprotected = append(s.unprotected, getUnprotected(svc.Prefix(), svc.Unprotected())...) - s.log.Info().Msgf("http service enabled: %s@/%s", svcName, svc.Prefix()) - } else { - message := fmt.Sprintf("http service %s does not exist", svcName) - return errors.New(message) - } +// func (s *Server) registerMiddlewares() error { +// middlewares := []*middlewareTriple{} +// for name, newFunc := range global.NewMiddlewares { +// if s.isMiddlewareEnabled(name) { +// m, prio, err := newFunc(s.conf.Middlewares[name]) +// if err != nil { +// err = errors.Wrapf(err, "error creating new middleware: %s,", name) +// return err +// } +// middlewares = append(middlewares, &middlewareTriple{ +// Name: name, +// Priority: prio, +// Middleware: m, +// }) +// s.log.Info().Msgf("http middleware enabled: %s", name) +// } +// } +// s.middlewares = middlewares +// return nil +// } + +// func (s *Server) isMiddlewareEnabled(name string) bool { +// _, ok := s.conf.Middlewares[name] +// return ok +// } + +func (s *Server) registerServices() { + for name, svc := range s.Services { + // instrument services with opencensus tracing. + h := traceHandler(name, svc.Handler()) + s.handlers[svc.Prefix()] = h + s.svcs[svc.Prefix()] = svc + s.unprotected = append(s.unprotected, getUnprotected(svc.Prefix(), svc.Unprotected())...) } - return nil } -func (s *Server) isServiceEnabled(svcName string) bool { - _, ok := global.Services[svcName] - return ok -} +// if s.isServiceEnabled(svcName) { +// newFunc := global.Services[svcName] +// svcLogger := s.log.With().Str("service", svcName).Logger() +// svc, err := newFunc(s.conf.Services[svcName], &svcLogger) +// if err != nil { +// err = errors.Wrapf(err, "http service %s could not be started,", svcName) +// return err +// } + +// // instrument services with opencensus tracing. +// h := traceHandler(svcName, svc.Handler()) +// s.handlers[svc.Prefix()] = h +// s.svcs[svc.Prefix()] = svc +// s.unprotected = append(s.unprotected, getUnprotected(svc.Prefix(), svc.Unprotected())...) +// s.log.Info().Msgf("http service enabled: %s@/%s", svcName, svc.Prefix()) +// } else { +// message := fmt.Sprintf("http service %s does not exist", svcName) +// return errors.New(message) +// } + +// func (s *Server) isServiceEnabled(svcName string) bool { +// _, ok := global.Services[svcName] +// return ok +// } // TODO(labkode): if the http server is exposed under a basename we need to prepend // to prefix. @@ -327,30 +323,26 @@ func (s *Server) getHandler() (http.Handler, error) { for _, v := range s.unprotected { s.log.Info().Msgf("unprotected URL: %s", v) } - authMiddle, err := auth.New(s.conf.Middlewares["auth"], s.unprotected) - if err != nil { - return nil, errors.Wrap(err, "rhttp: error creating auth middleware") - } - - // add always the logctx middleware as most priority, this middleware is internal - // and cannot be configured from the configuration. - coreMiddlewares := []*middlewareTriple{} - providerAuthMiddle, err := addProviderAuthMiddleware(s.conf, s.unprotected) - if err != nil { - return nil, errors.Wrap(err, "rhttp: error creating providerauthorizer middleware") - } - if providerAuthMiddle != nil { - coreMiddlewares = append(coreMiddlewares, &middlewareTriple{Middleware: providerAuthMiddle, Name: "providerauthorizer"}) + for _, triple := range s.middlewares { + handler = triple.Middleware(handler) } + // authMiddle, err := auth.New(s.Middlewares["auth"], s.unprotected) + // if err != nil { + // return nil, errors.Wrap(err, "rhttp: error creating auth middleware") + // } - coreMiddlewares = append(coreMiddlewares, &middlewareTriple{Middleware: authMiddle, Name: "auth"}) - coreMiddlewares = append(coreMiddlewares, &middlewareTriple{Middleware: log.New(), Name: "log"}) - coreMiddlewares = append(coreMiddlewares, &middlewareTriple{Middleware: appctx.New(s.log), Name: "appctx"}) + // // add always the logctx middleware as most priority, this middleware is internal + // // and cannot be configured from the configuration. + // coreMiddlewares := []*middlewareTriple{} - for _, triple := range coreMiddlewares { - handler = triple.Middleware(traceHandler(triple.Name, handler)) - } + // coreMiddlewares = append(coreMiddlewares, &middlewareTriple{Middleware: authMiddle, Name: "auth"}) + // coreMiddlewares = append(coreMiddlewares, &middlewareTriple{Middleware: log.New(), Name: "log"}) + // coreMiddlewares = append(coreMiddlewares, &middlewareTriple{Middleware: appctx.New(s.log), Name: "appctx"}) + + // for _, triple := range coreMiddlewares { + // handler = triple.Middleware(traceHandler(triple.Name, handler)) + // } return handler, nil } @@ -366,13 +358,3 @@ func traceHandler(name string, h http.Handler) http.Handler { h.ServeHTTP(w, r.WithContext(ctx)) }) } - -func addProviderAuthMiddleware(conf *config, unprotected []string) (global.Middleware, error) { - _, ocmdRegistered := global.Services["ocmd"] - _, ocmdEnabled := conf.Services["ocmd"] - ocmdPrefix, _ := conf.Services["ocmd"]["prefix"].(string) - if ocmdRegistered && ocmdEnabled { - return providerauthorizer.New(conf.Middlewares["providerauthorizer"], unprotected, ocmdPrefix) - } - return nil, nil -} From c3817c5f675e9f611c7f30e615466df47b08e0a8 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Mon, 19 Jun 2023 14:37:59 +0200 Subject: [PATCH 27/76] simplifying grpc server --- pkg/rgrpc/option.go | 31 ++++++++ pkg/rgrpc/rgrpc.go | 186 ++++++++++++++++++-------------------------- 2 files changed, 107 insertions(+), 110 deletions(-) create mode 100644 pkg/rgrpc/option.go diff --git a/pkg/rgrpc/option.go b/pkg/rgrpc/option.go new file mode 100644 index 0000000000..638ac9b9e7 --- /dev/null +++ b/pkg/rgrpc/option.go @@ -0,0 +1,31 @@ +package rgrpc + +import ( + "github.com/rs/zerolog" +) + +type Option func(*Server) + +func WithShutdownDeadline(deadline int) Option { + return func(s *Server) { + s.ShutdownDeadline = deadline + } +} + +func EnableReflection(enable bool) Option { + return func(s *Server) { + s.EnableReflection = enable + } +} + +func WithServices(services map[string]Service) Option { + return func(s *Server) { + s.services = services + } +} + +func WithLogger(logger zerolog.Logger) Option { + return func(s *Server) { + s.log = logger + } +} diff --git a/pkg/rgrpc/rgrpc.go b/pkg/rgrpc/rgrpc.go index bb363b276e..8533bb749e 100644 --- a/pkg/rgrpc/rgrpc.go +++ b/pkg/rgrpc/rgrpc.go @@ -19,21 +19,17 @@ package rgrpc import ( - "fmt" "io" "net" "sort" "github.com/cs3org/reva/internal/grpc/interceptors/appctx" - "github.com/cs3org/reva/internal/grpc/interceptors/auth" "github.com/cs3org/reva/internal/grpc/interceptors/log" "github.com/cs3org/reva/internal/grpc/interceptors/recovery" "github.com/cs3org/reva/internal/grpc/interceptors/token" "github.com/cs3org/reva/internal/grpc/interceptors/useragent" - "github.com/cs3org/reva/pkg/sharedconf" rtrace "github.com/cs3org/reva/pkg/trace" grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware" - "github.com/mitchellh/mapstructure" "github.com/pkg/errors" "github.com/rs/zerolog" "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc" @@ -94,51 +90,30 @@ type streamInterceptorTriple struct { Interceptor grpc.StreamServerInterceptor } -type config struct { - Network string `mapstructure:"network"` - Address string `mapstructure:"address"` - ShutdownDeadline int `mapstructure:"shutdown_deadline"` - Services map[string]map[string]interface{} `mapstructure:"services"` - Interceptors map[string]map[string]interface{} `mapstructure:"interceptors"` - EnableReflection bool `mapstructure:"enable_reflection"` -} - -func (c *config) init() { - if c.Network == "" { - c.Network = "tcp" - } - - if c.Address == "" { - c.Address = sharedconf.GetGatewaySVC("0.0.0.0:19000") - } -} - // Server is a gRPC server. type Server struct { + ShutdownDeadline int + EnableReflection bool + s *grpc.Server - conf *config listener net.Listener log zerolog.Logger services map[string]Service } // NewServer returns a new Server. -func NewServer(m interface{}, log zerolog.Logger) (*Server, error) { - conf := &config{} - if err := mapstructure.Decode(m, conf); err != nil { - return nil, err +func NewServer(o ...Option) (*Server, error) { + server := &Server{} + for _, oo := range o { + oo(server) } - conf.init() - - server := &Server{conf: conf, log: log, services: map[string]Service{}} - return server, nil } // Start starts the server. func (s *Server) Start(ln net.Listener) error { - if err := s.registerServices(); err != nil { + if err := s.initServices(); err != nil { err = errors.Wrap(err, "unable to register services") return err } @@ -153,39 +128,30 @@ func (s *Server) Start(ln net.Listener) error { return nil } -func (s *Server) isInterceptorEnabled(name string) bool { - for k := range s.conf.Interceptors { - if k == name { - return true - } - } - return false -} - -func (s *Server) isServiceEnabled(svcName string) bool { - for key := range Services { - if key == svcName { - return true - } - } - return false -} - -func (s *Server) registerServices() error { - for svcName := range s.conf.Services { - if s.isServiceEnabled(svcName) { - newFunc := Services[svcName] - svc, err := newFunc(s.conf.Services[svcName], s.s) - if err != nil { - return errors.Wrapf(err, "rgrpc: grpc service %s could not be started,", svcName) - } - s.services[svcName] = svc - s.log.Info().Msgf("rgrpc: grpc service enabled: %s", svcName) - } else { - message := fmt.Sprintf("rgrpc: grpc service %s does not exist", svcName) - return errors.New(message) - } - } +// func (s *Server) isInterceptorEnabled(name string) bool { +// for k := range s.iunterceptors { +// if k == name { +// return true +// } +// } +// return false +// } + +func (s *Server) initServices() error { + // for svcName := range s.conf.Services { + // if s.isServiceEnabled(svcName) { + // newFunc := Services[svcName] + // svc, err := newFunc(s.conf.Services[svcName], s.s) + // if err != nil { + // return errors.Wrapf(err, "rgrpc: grpc service %s could not be started,", svcName) + // } + // s.services[svcName] = svc + // s.log.Info().Msgf("rgrpc: grpc service enabled: %s", svcName) + // } else { + // message := fmt.Sprintf("rgrpc: grpc service %s does not exist", svcName) + // return errors.New(message) + // } + // } // obtain list of unprotected endpoints unprotected := []string{} @@ -203,7 +169,7 @@ func (s *Server) registerServices() error { svc.Register(grpcServer) } - if s.conf.EnableReflection { + if s.EnableReflection { s.log.Info().Msg("rgrpc: grpc server reflection enabled") reflection.Register(grpcServer) } @@ -240,43 +206,43 @@ func (s *Server) GracefulStop() error { // Network returns the network type. func (s *Server) Network() string { - return s.conf.Network + return s.listener.Addr().Network() } // Address returns the network address. func (s *Server) Address() string { - return s.conf.Address + return s.listener.Addr().String() } func (s *Server) getInterceptors(unprotected []string) ([]grpc.ServerOption, error) { unaryTriples := []*unaryInterceptorTriple{} - for name, newFunc := range UnaryInterceptors { - if s.isInterceptorEnabled(name) { - inter, prio, err := newFunc(s.conf.Interceptors[name]) - if err != nil { - err = errors.Wrapf(err, "rgrpc: error creating unary interceptor: %s,", name) - return nil, err - } - triple := &unaryInterceptorTriple{ - Name: name, - Priority: prio, - Interceptor: inter, - } - unaryTriples = append(unaryTriples, triple) - } - } + // for name, newFunc := range UnaryInterceptors { + // if s.isInterceptorEnabled(name) { + // inter, prio, err := newFunc(s.conf.Interceptors[name]) + // if err != nil { + // err = errors.Wrapf(err, "rgrpc: error creating unary interceptor: %s,", name) + // return nil, err + // } + // triple := &unaryInterceptorTriple{ + // Name: name, + // Priority: prio, + // Interceptor: inter, + // } + // unaryTriples = append(unaryTriples, triple) + // } + // } // sort unary triples sort.SliceStable(unaryTriples, func(i, j int) bool { return unaryTriples[i].Priority < unaryTriples[j].Priority }) - authUnary, err := auth.NewUnary(s.conf.Interceptors["auth"], unprotected) - if err != nil { - return nil, errors.Wrap(err, "rgrpc: error creating unary auth interceptor") - } + // authUnary, err := auth.NewUnary(s.conf.Interceptors["auth"], unprotected) + // if err != nil { + // return nil, errors.Wrap(err, "rgrpc: error creating unary auth interceptor") + // } - unaryInterceptors := []grpc.UnaryServerInterceptor{authUnary} + unaryInterceptors := []grpc.UnaryServerInterceptor{} // TODO: add auth unary for _, t := range unaryTriples { unaryInterceptors = append(unaryInterceptors, t.Interceptor) s.log.Info().Msgf("rgrpc: chaining grpc unary interceptor %s with priority %d", t.Name, t.Priority) @@ -298,39 +264,39 @@ func (s *Server) getInterceptors(unprotected []string) ([]grpc.ServerOption, err unaryChain := grpc_middleware.ChainUnaryServer(unaryInterceptors...) streamTriples := []*streamInterceptorTriple{} - for name, newFunc := range StreamInterceptors { - if s.isInterceptorEnabled(name) { - inter, prio, err := newFunc(s.conf.Interceptors[name]) - if err != nil { - err = errors.Wrapf(err, "rgrpc: error creating streaming interceptor: %s,", name) - return nil, err - } - triple := &streamInterceptorTriple{ - Name: name, - Priority: prio, - Interceptor: inter, - } - streamTriples = append(streamTriples, triple) - } - } + // for name, newFunc := range StreamInterceptors { + // if s.isInterceptorEnabled(name) { + // inter, prio, err := newFunc(s.conf.Interceptors[name]) + // if err != nil { + // err = errors.Wrapf(err, "rgrpc: error creating streaming interceptor: %s,", name) + // return nil, err + // } + // triple := &streamInterceptorTriple{ + // Name: name, + // Priority: prio, + // Interceptor: inter, + // } + // streamTriples = append(streamTriples, triple) + // } + // } // sort stream triples sort.SliceStable(streamTriples, func(i, j int) bool { return streamTriples[i].Priority < streamTriples[j].Priority }) - authStream, err := auth.NewStream(s.conf.Interceptors["auth"], unprotected) - if err != nil { - return nil, errors.Wrap(err, "rgrpc: error creating stream auth interceptor") - } + // authStream, err := auth.NewStream(s.conf.Interceptors["auth"], unprotected) + // if err != nil { + // return nil, errors.Wrap(err, "rgrpc: error creating stream auth interceptor") + // } - streamInterceptors := []grpc.StreamServerInterceptor{authStream} + streamInterceptors := []grpc.StreamServerInterceptor{} for _, t := range streamTriples { streamInterceptors = append(streamInterceptors, t.Interceptor) s.log.Info().Msgf("rgrpc: chaining grpc streaming interceptor %s with priority %d", t.Name, t.Priority) } streamInterceptors = append([]grpc.StreamServerInterceptor{ - authStream, + // authStream, appctx.NewStream(s.log), token.NewStream(), useragent.NewStream(), From 1369efb8444eacc61bbd4305835580ffe7558592 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Thu, 22 Jun 2023 15:09:12 +0200 Subject: [PATCH 28/76] grace with multiple services and addresses --- cmd/revad/pkg/grace/grace.go | 157 +++++++++++++++++++++++++++-------- 1 file changed, 123 insertions(+), 34 deletions(-) diff --git a/cmd/revad/pkg/grace/grace.go b/cmd/revad/pkg/grace/grace.go index 52cd3619a8..1b11622baa 100644 --- a/cmd/revad/pkg/grace/grace.go +++ b/cmd/revad/pkg/grace/grace.go @@ -46,6 +46,8 @@ type Watcher struct { childPIDs []int } +const revaEnvPrefix = "REVA_FD_" + // Option represent an option. type Option func(w *Watcher) @@ -69,7 +71,7 @@ func NewWatcher(opts ...Option) *Watcher { log: zerolog.Nop(), graceful: os.Getenv("GRACEFUL") == "true", ppid: os.Getppid(), - ss: map[string]Server{}, + ss: make(map[string]Server), } for _, opt := range opts { @@ -186,31 +188,112 @@ func newListener(network, addr string) (net.Listener, error) { return net.Listen(network, addr) } +// implements the net.Listener interface +type inherited struct { + f *os.File + ln net.Listener +} + +func (i *inherited) Accept() (net.Conn, error) { + return i.ln.Accept() +} + +func (i *inherited) Close() error { + // TODO: improve this: if file close has error + // the listener is not closed + if err := i.f.Close(); err != nil { + return err + } + return i.ln.Close() +} + +func (i *inherited) Addr() net.Addr { + return i.ln.Addr() +} + +func inheritedListeners() map[string]net.Listener { + lns := make(map[string]net.Listener) + for _, val := range os.Environ() { + if strings.HasPrefix(val, revaEnvPrefix) { + // env variable is of type REVA_FD_= + env := strings.TrimPrefix(val, revaEnvPrefix) + s := strings.Split(env, "=") + if len(s) != 2 { + continue + } + svcname := s[0] + fd, err := strconv.ParseUint(s[1], 10, 64) + if err != nil { + continue + } + f := os.NewFile(uintptr(fd), "") + ln, err := net.FileListener(f) + if err != nil { + // TODO: log error + continue + } + lns[svcname] = &inherited{f: f, ln: ln} + } + } + return lns +} + +func isRandomAddress(addr string) bool { + return addr == "" +} + +func getAddress(addr string) string { + if isRandomAddress(addr) { + return ":0" + } + return addr +} + // GetListeners return grpc listener first and http listener second. -func (w *Watcher) GetListeners(servers map[string]Server) (map[string]net.Listener, error) { - w.ss = servers - lns := map[string]net.Listener{} +func (w *Watcher) GetListeners(servers map[string]Addressable) (map[string]net.Listener, error) { + lns := make(map[string]net.Listener) + if w.graceful { - w.log.Info().Msg("graceful restart, inheriting parent ln fds for grpc and http") - count := 3 - for k, s := range servers { - network, addr := s.Network(), s.Address() - fd := os.NewFile(uintptr(count), "") // 3 because ExtraFile passed to new process - count++ - ln, err := net.FileListener(fd) - if err != nil { - w.log.Error().Err(err).Msg("error creating net.Listener from fd") - // create new fd - ln, err := newListener(network, addr) - if err != nil { - return nil, err + w.log.Info().Msg("graceful restart, inheriting parent listener fds for grpc and http services") + + inherited := inheritedListeners() + for svc, ln := range inherited { + addr, ok := servers[svc] + if !ok { + continue + } + // for services with random addresses, check and assign if available from inherited + // from the assigned addresses, assing the listener if address correspond + if isRandomAddress(addr.Address()) || addr.Address() == ln.Addr().String() { // TODO: check which is the host here + lns[svc] = ln + } + } + + // close all the listeners not used from inherited + for svc, ln := range inherited { + if _, ok := lns[svc]; !ok { + if err := ln.Close(); err != nil { + w.log.Error().Err(err).Msgf("error closing inherited listener %s", ln.Addr().String()) + return nil, errors.Wrap(err, "error closing inherited listener") } - lns[k] = ln - } else { - lns[k] = ln } } + // create assigned/random listeners for the missing services + for svc, addr := range servers { + _, ok := lns[svc] + if ok { + continue + } + a := getAddress(addr.Address()) + ln, err := newListener(addr.Network(), a) + if err != nil { + w.log.Error().Err(err).Msgf("error getting listener on %s", a) + return nil, errors.Wrap(err, "error getting listener") + } + lns[svc] = ln + } + // kill parent // TODO(labkode): maybe race condition here? // What do we do if we cannot kill the parent but we have valid fds? @@ -233,26 +316,30 @@ func (w *Watcher) GetListeners(servers map[string]Server) (map[string]net.Listen return lns, nil } - // create two listeners for grpc and http - for k, s := range servers { - network, addr := s.Network(), s.Address() + // no graceful + for svc, s := range servers { + network, addr := s.Network(), getAddress(s.Address()) ln, err := newListener(network, addr) if err != nil { return nil, err } - lns[k] = ln + lns[svc] = ln } w.lns = lns return lns, nil } +type Addressable interface { + Network() string + Address() string +} + // Server is the interface that servers like HTTP or gRPC // servers need to implement. type Server interface { - Stop() error - GracefulStop() error - Network() string - Address() string + Start(net.Listener) error + Serverless + Addressable } // Serverless is the interface that the serverless server implements. @@ -350,6 +437,8 @@ func (w *Watcher) TrapSignals() { func getListenerFile(ln net.Listener) (*os.File, error) { switch t := ln.(type) { + case *inherited: + return t.f, nil case *net.TCPListener: return t.File() case *net.UnixListener: @@ -361,13 +450,13 @@ func getListenerFile(ln net.Listener) (*os.File, error) { func forkChild(lns map[string]net.Listener) (*os.Process, error) { // Get the file descriptor for the listener and marshal the metadata to pass // to the child in the environment. - fds := map[string]*os.File{} - for k, ln := range lns { + fds := make(map[string]*os.File, 0) + for name, ln := range lns { fd, err := getListenerFile(ln) if err != nil { return nil, err } - fds[k] = fd + fds[name] = fd } // Pass stdin, stdout, and stderr along with the listener file to the child @@ -379,10 +468,10 @@ func forkChild(lns map[string]net.Listener) (*os.Process, error) { // Get current environment and add in the listener to it. environment := append(os.Environ(), "GRACEFUL=true") - var counter = 3 + counter := 3 for k, fd := range fds { k = strings.ToUpper(k) - environment = append(environment, k+"FD="+fmt.Sprintf("%d", counter)) + environment = append(environment, fmt.Sprintf("%s%s=%d", revaEnvPrefix, k, counter)) files = append(files, fd) counter++ } @@ -402,7 +491,7 @@ func forkChild(lns map[string]net.Listener) (*os.Process, error) { Sys: &syscall.SysProcAttr{}, }) - // TODO(labkode): if the process dies (because config changed and is wrong + // TODO(labkode): if the process dies (because config changed and is wrong) // we need to return an error if err != nil { return nil, err From 1b190f6d33ef307b5a6953cd3cc09a5b8f9d24c9 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Thu, 22 Jun 2023 15:56:15 +0200 Subject: [PATCH 29/76] set servers deps --- cmd/revad/pkg/grace/grace.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmd/revad/pkg/grace/grace.go b/cmd/revad/pkg/grace/grace.go index 1b11622baa..1d793bf35d 100644 --- a/cmd/revad/pkg/grace/grace.go +++ b/cmd/revad/pkg/grace/grace.go @@ -348,6 +348,8 @@ type Serverless interface { GracefulStop() error } +func (w *Watcher) SetServers(s map[string]Server) { w.ss = s } + // TrapSignals captures the OS signal. func (w *Watcher) TrapSignals() { signalCh := make(chan os.Signal, 1024) From e25b2dad37273ab3052165d80aea785c6571ec94 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Fri, 23 Jun 2023 11:19:20 +0200 Subject: [PATCH 30/76] rewritten runtime with new config --- cmd/revad/pkg/config/common.go | 1 + cmd/revad/runtime/log.go | 58 +++++ cmd/revad/runtime/runtime.go | 444 ++++++++++++--------------------- cmd/revad/runtime/server.go | 66 +++++ pkg/utils/maps/maps.go | 12 + 5 files changed, 299 insertions(+), 282 deletions(-) create mode 100644 cmd/revad/runtime/log.go create mode 100644 cmd/revad/runtime/server.go create mode 100644 pkg/utils/maps/maps.go diff --git a/cmd/revad/pkg/config/common.go b/cmd/revad/pkg/config/common.go index 6cb8c21a68..aaa224dede 100644 --- a/cmd/revad/pkg/config/common.go +++ b/cmd/revad/pkg/config/common.go @@ -86,6 +86,7 @@ func parseMiddlwares(cfg map[string]any, key string) (map[string]map[string]any, type Service struct { Address string Name string + Label string Config map[string]any raw *DriverConfig diff --git a/cmd/revad/runtime/log.go b/cmd/revad/runtime/log.go new file mode 100644 index 0000000000..98227b2ecc --- /dev/null +++ b/cmd/revad/runtime/log.go @@ -0,0 +1,58 @@ +package runtime + +import ( + "io" + "os" + + "github.com/cs3org/reva/cmd/revad/pkg/config" + "github.com/cs3org/reva/pkg/logger" + "github.com/pkg/errors" + "github.com/rs/zerolog" +) + +func initLogger(conf *config.Log) *zerolog.Logger { + log, err := newLogger(conf) + if err != nil { + abort("error creating logger: %w", err) + } + return log +} + +func newLogger(conf *config.Log) (*zerolog.Logger, error) { + // TODO(labkode): use debug level rather than info as default until reaching a stable version. + // Helps having smaller development files. + if conf.Level == "" { + conf.Level = zerolog.DebugLevel.String() + } + + var opts []logger.Option + opts = append(opts, logger.WithLevel(conf.Level)) + + w, err := getWriter(conf.Output) + if err != nil { + return nil, err + } + + opts = append(opts, logger.WithWriter(w, logger.Mode(conf.Mode))) + + l := logger.New(opts...) + sub := l.With().Int("pid", os.Getpid()).Logger() + return &sub, nil +} + +func getWriter(out string) (io.Writer, error) { + if out == "stderr" || out == "" { + return os.Stderr, nil + } + + if out == "stdout" { + return os.Stdout, nil + } + + fd, err := os.OpenFile(out, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + return nil, errors.Wrap(err, "error creating log file: "+out) + } + + return fd, nil +} diff --git a/cmd/revad/runtime/runtime.go b/cmd/revad/runtime/runtime.go index 255c55bdf8..90a0cde329 100644 --- a/cmd/revad/runtime/runtime.go +++ b/cmd/revad/runtime/runtime.go @@ -20,100 +20,78 @@ package runtime import ( "fmt" - "io" - "log" "net" "os" + "path" "runtime" "strconv" "strings" - "github.com/cs3org/reva/cmd/revad/internal/grace" - "github.com/cs3org/reva/pkg/logger" - "github.com/cs3org/reva/pkg/registry/memory" - "github.com/cs3org/reva/pkg/rgrpc" - "github.com/cs3org/reva/pkg/rhttp" - "github.com/cs3org/reva/pkg/rserverless" + "github.com/cs3org/reva/cmd/revad/pkg/config" + "github.com/cs3org/reva/cmd/revad/pkg/grace" "github.com/cs3org/reva/pkg/sharedconf" - rtrace "github.com/cs3org/reva/pkg/trace" - "github.com/cs3org/reva/pkg/utils" - "github.com/mitchellh/mapstructure" - "github.com/pkg/errors" + "github.com/cs3org/reva/pkg/utils/maps" + "github.com/google/uuid" "github.com/rs/zerolog" + "golang.org/x/sync/errgroup" ) -// Run runs a reva server with the given config file and pid file. -func Run(mainConf map[string]interface{}, pidFile, logLevel string) { - logConf := parseLogConfOrDie(mainConf["log"], logLevel) - logger := initLogger(logConf) - RunWithOptions(mainConf, pidFile, WithLogger(logger)) -} - -// RunWithOptions runs a reva server with the given config file, pid file and options. -func RunWithOptions(mainConf map[string]interface{}, pidFile string, opts ...Option) { - options := newOptions(opts...) - parseSharedConfOrDie(mainConf["shared"]) - coreConf := parseCoreConfOrDie(mainConf["core"]) - - // TODO: one can pass the options from the config file to registry.New() and initialize a registry based upon config files. - if options.Registry != nil { - utils.GlobalRegistry = options.Registry - } else if _, ok := mainConf["registry"]; ok { - for _, services := range mainConf["registry"].(map[string]interface{}) { - for sName, nodes := range services.(map[string]interface{}) { - for _, instance := range nodes.([]interface{}) { - if err := utils.GlobalRegistry.Add(memory.NewService(sName, instance.(map[string]interface{})["nodes"].([]interface{}))); err != nil { - panic(err) - } - } - } - } - } +type Reva struct { + config *config.Config - run(mainConf, coreConf, options.Logger, pidFile) + servers []*Server + watcher *grace.Watcher + lns map[string]net.Listener } -type coreConf struct { - MaxCPUs string `mapstructure:"max_cpus"` - TracingEnabled bool `mapstructure:"tracing_enabled"` - TracingEndpoint string `mapstructure:"tracing_endpoint"` - TracingCollector string `mapstructure:"tracing_collector"` - TracingServiceName string `mapstructure:"tracing_service_name"` +func New(config *config.Config) (*Reva, error) { + _ = initLogger(config.Log) + initSharedConf(config) - // TracingService specifies the service. i.e OpenCensus, OpenTelemetry, OpenTracing... - TracingService string `mapstructure:"tracing_service"` -} + pidfile := getPidfile() -func run(mainConf map[string]interface{}, coreConf *coreConf, logger *zerolog.Logger, filename string) { - host, _ := os.Hostname() - logger.Info().Msgf("host info: %s", host) + grpc, addrGRPC := groupGRPCByAddress(config) + http, addrHTTP := groupHTTPByAddress(config) - if coreConf.TracingEnabled { - initTracing(coreConf) + log := zerolog.Nop() + watcher, err := initWatcher(&log, pidfile) + if err != nil { + return nil, err } - initCPUCount(coreConf, logger) - servers := initServers(mainConf, logger) - serverless := initServerless(mainConf, logger) + listeners := initListeners(watcher, maps.Merge(addrGRPC, addrHTTP), &log) + setRandomAddresses(config, listeners) + applyTemplates(config) - if len(servers) == 0 && serverless == nil { - logger.Info().Msg("nothing to do, no grpc/http/serverless enabled_services declared in config") - os.Exit(1) - } - - watcher, err := initWatcher(logger, filename) + servers, err := newServers(grpc, http) if err != nil { - log.Panic(err) - } - listeners := initListeners(watcher, servers, logger) - if serverless != nil { - watcher.SL = serverless + return nil, err } - start(mainConf, servers, serverless, listeners, logger, watcher) + return &Reva{ + config: config, + servers: servers, + watcher: watcher, + lns: listeners, + }, nil } -func initListeners(watcher *grace.Watcher, servers map[string]grace.Server, log *zerolog.Logger) map[string]net.Listener { +func setRandomAddresses(c *config.Config, lns map[string]net.Listener) { + f := func(s *config.Service) { + if s.Address != "" { + return + } + ln, ok := lns[s.Label] + if !ok { + abort("port not assigned for service %s", s.Label) + } + s.SetAddress(ln.Addr().String()) + } + c.GRPC.ForEachService(f) + c.HTTP.ForEachService(f) +} + +func initListeners(watcher *grace.Watcher, servers map[string]grace.Addressable, log *zerolog.Logger) map[string]net.Listener { listeners, err := watcher.GetListeners(servers) if err != nil { log.Error().Err(err).Msg("error getting sockets") @@ -122,6 +100,94 @@ func initListeners(watcher *grace.Watcher, servers map[string]grace.Server, log return listeners } +type addr struct { + address string + network string +} + +func (a *addr) Address() string { + return a.address +} + +func (a *addr) Network() string { + return a.network +} + +func groupGRPCByAddress(cfg *config.Config) (map[string]*config.GRPC, map[string]grace.Addressable) { + // TODO: same address cannot be used in different configurations + g := map[string]*config.GRPC{} + a := map[string]grace.Addressable{} + cfg.GRPC.ForEachService(func(s *config.Service) { + if _, ok := g[s.Address]; !ok { + g[s.Address] = &config.GRPC{ + Address: s.Address, + Network: "tcp", // TODO: configure this as well + ShutdownDeadline: cfg.GRPC.ShutdownDeadline, + EnableReflection: cfg.GRPC.EnableReflection, + Services: make(map[string]config.ServicesConfig), + Interceptors: cfg.GRPC.Interceptors, + } + if s.Address == "" { + a[s.Label] = &addr{address: s.Address, network: "tcp"} + } + } + g[s.Address].Services[s.Name] = config.ServicesConfig{ + {Config: s.Config}, + } + }) + return g, a +} + +func groupHTTPByAddress(cfg *config.Config) (map[string]*config.HTTP, map[string]grace.Addressable) { + g := map[string]*config.HTTP{} + a := map[string]grace.Addressable{} + cfg.HTTP.ForEachService(func(s *config.Service) { + if _, ok := g[s.Address]; !ok { + g[s.Address] = &config.HTTP{ + Address: s.Address, + Network: "tpc", // TODO: configure this as well + CertFile: cfg.HTTP.CertFile, + KeyFile: cfg.HTTP.KeyFile, + Services: make(map[string]config.ServicesConfig), + Middlewares: cfg.HTTP.Middlewares, + } + if s.Address == "" { + a[s.Label] = &addr{address: s.Address, network: "tcp"} + } + } + g[s.Address].Services[s.Name] = config.ServicesConfig{ + {Config: s.Config}, + } + }) + return g, a +} + +func (r *Reva) Start() { + var g errgroup.Group + for _, server := range r.servers { + server := server + g.Go(func() error { + return server.Start() + }) + } + + r.watcher.TrapSignals() + if err := g.Wait(); err != nil { + // TODO: log error + } +} + +func getPidfile() string { + uuid := uuid.New().String() + name := fmt.Sprintf("revad-%s.pid", uuid) + + return path.Join(os.TempDir(), name) +} + +func initSharedConf(config *config.Config) { + sharedconf.Init(config.Shared) +} + func initWatcher(log *zerolog.Logger, filename string) (*grace.Watcher, error) { watcher, err := handlePIDFlag(log, filename) // TODO(labkode): maybe pidfile can be created later on? like once a server is going to be created? @@ -132,47 +198,37 @@ func initWatcher(log *zerolog.Logger, filename string) (*grace.Watcher, error) { return watcher, err } -func initServers(mainConf map[string]interface{}, log *zerolog.Logger) map[string]grace.Server { - servers := map[string]grace.Server{} - if isEnabledHTTP(mainConf) { - s, err := getHTTPServer(mainConf["http"], log) - if err != nil { - log.Error().Err(err).Msg("error creating http server") - os.Exit(1) +func assignRandomAddress(configs []*config.Config) (addr []net.Listener) { + assign := func(s *config.Service) { + if s.Address == "" { + random, err := randomListener("tpc") // TODO: take from config + if err != nil { + abort("error assigning random port to service %s: %v", s.Name, err) + } + s.SetAddress(random.Addr().String()) + addr = append(addr, random) } - servers["http"] = s } - - if isEnabledGRPC(mainConf) { - s, err := getGRPCServer(mainConf["grpc"], log) - if err != nil { - log.Error().Err(err).Msg("error creating grpc server") - os.Exit(1) - } - servers["grpc"] = s + for _, c := range configs { + c.GRPC.ForEachService(assign) + c.HTTP.ForEachService(assign) } - - return servers + return } -func initServerless(mainConf map[string]interface{}, log *zerolog.Logger) *rserverless.Serverless { - if isEnabledServerless(mainConf) { - serverless, err := getServerless(mainConf["serverless"], log) - if err != nil { - log.Error().Err(err).Msg("error") - os.Exit(1) - } - return serverless - } - - return nil +func randomListener(network string) (net.Listener, error) { + return net.Listen(network, ":0") } -func initTracing(conf *coreConf) { - rtrace.SetTraceProvider(conf.TracingCollector, conf.TracingEndpoint, conf.TracingServiceName) +func applyTemplates(config *config.Config) { + // TODO: we might want to prefer before the actual config in the lookup + // and then the others + if err := config.ApplyTemplates(config); err != nil { + abort("error applying templated to config: %v", err) + } } -func initCPUCount(conf *coreConf, log *zerolog.Logger) { +func initCPUCount(conf *config.Core, log *zerolog.Logger) { ncpus, err := adjustCPU(conf.MaxCPUs) if err != nil { log.Error().Err(err).Msg("error adjusting number of cpus") @@ -182,13 +238,9 @@ func initCPUCount(conf *coreConf, log *zerolog.Logger) { log.Info().Msgf("running on %d cpus", ncpus) } -func initLogger(conf *logConf) *zerolog.Logger { - log, err := newLogger(conf) - if err != nil { - fmt.Fprintf(os.Stderr, "error creating logger, exiting ...") - os.Exit(1) - } - return log +func abort(msg string, args ...any) { + fmt.Fprintf(os.Stderr, msg, args...) + os.Exit(1) } func handlePIDFlag(l *zerolog.Logger, pidFile string) (*grace.Watcher, error) { @@ -204,100 +256,7 @@ func handlePIDFlag(l *zerolog.Logger, pidFile string) (*grace.Watcher, error) { return w, nil } -func start(mainConf map[string]interface{}, servers map[string]grace.Server, serverless *rserverless.Serverless, listeners map[string]net.Listener, log *zerolog.Logger, watcher *grace.Watcher) { - if isEnabledHTTP(mainConf) { - go func() { - if err := servers["http"].(*rhttp.Server).Start(listeners["http"]); err != nil { - log.Error().Err(err).Msg("error starting the http server") - watcher.Exit(1) - } - }() - } - if isEnabledGRPC(mainConf) { - go func() { - if err := servers["grpc"].(*rgrpc.Server).Start(listeners["grpc"]); err != nil { - log.Error().Err(err).Msg("error starting the grpc server") - watcher.Exit(1) - } - }() - } - if isEnabledServerless(mainConf) { - if err := serverless.Start(); err != nil { - log.Error().Err(err).Msg("error starting serverless services") - watcher.Exit(1) - } - } - - watcher.TrapSignals() -} - -func newLogger(conf *logConf) (*zerolog.Logger, error) { - // TODO(labkode): use debug level rather than info as default until reaching a stable version. - // Helps having smaller development files. - if conf.Level == "" { - conf.Level = zerolog.DebugLevel.String() - } - - var opts []logger.Option - opts = append(opts, logger.WithLevel(conf.Level)) - - w, err := getWriter(conf.Output) - if err != nil { - return nil, err - } - - opts = append(opts, logger.WithWriter(w, logger.Mode(conf.Mode))) - - l := logger.New(opts...) - sub := l.With().Int("pid", os.Getpid()).Logger() - return &sub, nil -} - -func getWriter(out string) (io.Writer, error) { - if out == "stderr" || out == "" { - return os.Stderr, nil - } - - if out == "stdout" { - return os.Stdout, nil - } - - fd, err := os.OpenFile(out, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) - if err != nil { - err = errors.Wrap(err, "error creating log file: "+out) - return nil, err - } - - return fd, nil -} - -func getGRPCServer(conf interface{}, l *zerolog.Logger) (*rgrpc.Server, error) { - sub := l.With().Str("pkg", "rgrpc").Logger() - s, err := rgrpc.NewServer(conf, sub) - if err != nil { - err = errors.Wrap(err, "main: error creating grpc server") - return nil, err - } - return s, nil -} - -func getHTTPServer(conf interface{}, l *zerolog.Logger) (*rhttp.Server, error) { - sub := l.With().Str("pkg", "rhttp").Logger() - s, err := rhttp.New(conf, sub) - if err != nil { - err = errors.Wrap(err, "main: error creating http server") - return nil, err - } - return s, nil -} - -func getServerless(conf interface{}, l *zerolog.Logger) (*rserverless.Serverless, error) { - sub := l.With().Str("pkg", "rserverless").Logger() - return rserverless.New(conf, sub) -} - -// adjustCPU parses string cpu and sets GOMAXPROCS -// +// adjustCPU parses string cpu and sets GOMAXPROCS // according to its value. It accepts either // a number (e.g. 3) or a percent (e.g. 50%). // Default is to use all available cores. @@ -336,82 +295,3 @@ func adjustCPU(cpu string) (int, error) { runtime.GOMAXPROCS(numCPU) return numCPU, nil } - -func parseCoreConfOrDie(v interface{}) *coreConf { - c := &coreConf{} - if err := mapstructure.Decode(v, c); err != nil { - fmt.Fprintf(os.Stderr, "error decoding core config: %s\n", err.Error()) - os.Exit(1) - } - - // tracing defaults to enabled if not explicitly configured - if v == nil { - c.TracingEnabled = true - c.TracingEndpoint = "localhost:6831" - } else if _, ok := v.(map[string]interface{})["tracing_enabled"]; !ok { - c.TracingEnabled = true - c.TracingEndpoint = "localhost:6831" - } - - return c -} - -func parseSharedConfOrDie(v interface{}) { - if err := sharedconf.Decode(v); err != nil { - fmt.Fprintf(os.Stderr, "error decoding shared config: %s\n", err.Error()) - os.Exit(1) - } -} - -func parseLogConfOrDie(v interface{}, logLevel string) *logConf { - c := &logConf{} - if err := mapstructure.Decode(v, c); err != nil { - fmt.Fprintf(os.Stderr, "error decoding log config: %s\n", err.Error()) - os.Exit(1) - } - - // if mode is not set, we use console mode, easier for devs - if c.Mode == "" { - c.Mode = "console" - } - - // Give priority to the log level passed through the command line. - if logLevel != "" { - c.Level = logLevel - } - - return c -} - -type logConf struct { - Output string `mapstructure:"output"` - Mode string `mapstructure:"mode"` - Level string `mapstructure:"level"` -} - -func isEnabledHTTP(conf map[string]interface{}) bool { - return isEnabled("http", conf) -} - -func isEnabledGRPC(conf map[string]interface{}) bool { - return isEnabled("grpc", conf) -} - -func isEnabledServerless(conf map[string]interface{}) bool { - return isEnabled("serverless", conf) -} - -func isEnabled(key string, conf map[string]interface{}) bool { - if a, ok := conf[key]; ok { - if b, ok := a.(map[string]interface{}); ok { - if c, ok := b["services"]; ok { - if d, ok := c.(map[string]interface{}); ok { - if len(d) > 0 { - return true - } - } - } - } - } - return false -} diff --git a/cmd/revad/runtime/server.go b/cmd/revad/runtime/server.go new file mode 100644 index 0000000000..bd8a59dba7 --- /dev/null +++ b/cmd/revad/runtime/server.go @@ -0,0 +1,66 @@ +package runtime + +import ( + "net" + + "github.com/cs3org/reva/cmd/revad/pkg/config" + "github.com/cs3org/reva/cmd/revad/pkg/grace" + "github.com/cs3org/reva/pkg/rgrpc" + "github.com/cs3org/reva/pkg/rhttp" + "github.com/rs/zerolog" +) + +type Server struct { + server grace.Server + listener net.Listener + logger *zerolog.Logger + + services map[string]any +} + +func (s *Server) Start() error { + return s.server.Start(s.listener) +} + +func newServers(grpc map[string]*config.GRPC, http map[string]*config.HTTP) ([]*Server, error) { + var servers []*Server + for _, cfg := range grpc { + services, err := rgrpc.InitServices(cfg.Services) + if err != nil { + return nil, err + } + s, err := rgrpc.NewServer( + rgrpc.EnableReflection(cfg.EnableReflection), + rgrpc.WithShutdownDeadline(cfg.ShutdownDeadline), + rgrpc.WithLogger(zerolog.Nop()), // TODO: set logger + rgrpc.WithServices(services), + ) + if err != nil { + return nil, err + } + server := &Server{ + server: s, + } + servers = append(servers, server) + } + for _, cfg := range http { + services, err := rhttp.InitServices(cfg.Services) + if err != nil { + return nil, err + } + s, err := rhttp.New( + rhttp.WithServices(services), + rhttp.WithLogger(zerolog.Nop()), // TODO: set logger + rhttp.WithCertAndKeyFiles(cfg.CertFile, cfg.KeyFile), + // rhttp.WithMiddlewares(cfg.Middlewares), + ) + if err != nil { + return nil, err + } + server := &Server{ + server: s, + } + servers = append(servers, server) + } + return servers, nil +} diff --git a/pkg/utils/maps/maps.go b/pkg/utils/maps/maps.go new file mode 100644 index 0000000000..886ae27bfb --- /dev/null +++ b/pkg/utils/maps/maps.go @@ -0,0 +1,12 @@ +package maps + +func Merge[K comparable, T any](m, n map[K]T) map[K]T { + r := make(map[K]T, len(m)+len(n)) + for k, v := range m { + r[k] = v + } + for k, v := range n { + r[k] = v + } + return r +} From 2b4e28d0e73706c3145b29b4dab3599e71831fc1 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Mon, 26 Jun 2023 10:19:15 +0200 Subject: [PATCH 31/76] use new reva pkg from runtime in main --- cmd/revad/main.go | 49 ++++++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/cmd/revad/main.go b/cmd/revad/main.go index cfb1a5cdb4..3f8e2f330e 100644 --- a/cmd/revad/main.go +++ b/cmd/revad/main.go @@ -28,11 +28,10 @@ import ( "sync" "syscall" - "github.com/cs3org/reva/cmd/revad/internal/config" - "github.com/cs3org/reva/cmd/revad/internal/grace" + "github.com/cs3org/reva/cmd/revad/pkg/config" + "github.com/cs3org/reva/cmd/revad/pkg/grace" "github.com/cs3org/reva/cmd/revad/runtime" "github.com/cs3org/reva/pkg/sysinfo" - "github.com/google/uuid" ) var ( @@ -48,6 +47,10 @@ var ( gitCommit, buildDate, version, goVersion string ) +var ( + revaProcs []*runtime.Reva +) + func main() { flag.Parse() @@ -134,7 +137,7 @@ func handleSignalFlag() { } } -func getConfigs() ([]map[string]interface{}, error) { +func getConfigs() ([]*config.Config, error) { var confs []string // give priority to read from dev-dir if *dirFlag != "" { @@ -186,8 +189,8 @@ func getConfigsFromDir(dir string) (confs []string, err error) { return } -func readConfigs(files []string) ([]map[string]interface{}, error) { - confs := make([]map[string]interface{}, 0, len(files)) +func readConfigs(files []string) ([]*config.Config, error) { + confs := make([]*config.Config, 0, len(files)) for _, conf := range files { fd, err := os.Open(conf) if err != nil { @@ -195,16 +198,16 @@ func readConfigs(files []string) ([]map[string]interface{}, error) { } defer fd.Close() - v, err := config.Read(fd) + c, err := config.Load(fd) if err != nil { return nil, err } - confs = append(confs, v) + confs = append(confs, c) } return confs, nil } -func runConfigs(confs []map[string]interface{}) { +func runConfigs(confs []*config.Config) { if len(confs) == 1 { runSingle(confs[0]) return @@ -213,29 +216,27 @@ func runConfigs(confs []map[string]interface{}) { runMultiple(confs) } -func runSingle(conf map[string]interface{}) { - if *pidFlag == "" { - *pidFlag = getPidfile() - } - - runtime.Run(conf, *pidFlag, *logFlag) +func registerReva(r *runtime.Reva) { + revaProcs = append(revaProcs, r) } -func getPidfile() string { - uuid := uuid.New().String() - name := fmt.Sprintf("revad-%s.pid", uuid) - - return path.Join(os.TempDir(), name) +func runSingle(conf *config.Config) { + reva, err := runtime.New(conf) + if err != nil { + fmt.Fprintf(os.Stderr, err.Error()) + os.Exit(1) + } + registerReva(reva) + reva.Start() } -func runMultiple(confs []map[string]interface{}) { +func runMultiple(confs []*config.Config) { var wg sync.WaitGroup for _, conf := range confs { wg.Add(1) - pidfile := getPidfile() - go func(wg *sync.WaitGroup, conf map[string]interface{}) { + go func(wg *sync.WaitGroup, conf *config.Config) { defer wg.Done() - runtime.Run(conf, pidfile, *logFlag) + runSingle(conf) }(&wg, conf) } wg.Wait() From dff18a397266a9b9803c79309c52221514659e4e Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Mon, 26 Jun 2023 10:24:52 +0200 Subject: [PATCH 32/76] simplified creation of grpc service implementations --- internal/grpc/services/applicationauth/applicationauth.go | 2 +- internal/grpc/services/appprovider/appprovider.go | 2 +- internal/grpc/services/appregistry/appregistry.go | 2 +- internal/grpc/services/appregistry/appregistry_test.go | 2 +- internal/grpc/services/authprovider/authprovider.go | 2 +- internal/grpc/services/authregistry/authregistry.go | 2 +- internal/grpc/services/datatx/datatx.go | 2 +- internal/grpc/services/gateway/gateway.go | 2 +- internal/grpc/services/groupprovider/groupprovider.go | 2 +- internal/grpc/services/helloworld/helloworld.go | 2 +- internal/grpc/services/ocmcore/ocmcore.go | 2 +- internal/grpc/services/ocminvitemanager/ocminvitemanager.go | 2 +- .../services/ocmproviderauthorizer/ocmproviderauthorizer.go | 2 +- internal/grpc/services/ocmshareprovider/ocmshareprovider.go | 2 +- internal/grpc/services/permissions/permissions.go | 2 +- internal/grpc/services/preferences/preferences.go | 2 +- .../grpc/services/publicshareprovider/publicshareprovider.go | 2 +- .../services/publicstorageprovider/publicstorageprovider.go | 2 +- internal/grpc/services/storageprovider/storageprovider.go | 2 +- internal/grpc/services/storageregistry/storageregistry.go | 2 +- internal/grpc/services/userprovider/userprovider.go | 2 +- internal/grpc/services/usershareprovider/usershareprovider.go | 2 +- 22 files changed, 22 insertions(+), 22 deletions(-) diff --git a/internal/grpc/services/applicationauth/applicationauth.go b/internal/grpc/services/applicationauth/applicationauth.go index 65514b6dcd..47f4c24680 100644 --- a/internal/grpc/services/applicationauth/applicationauth.go +++ b/internal/grpc/services/applicationauth/applicationauth.go @@ -73,7 +73,7 @@ func parseConfig(m map[string]interface{}) (*config, error) { } // New creates a app auth provider svc. -func New(m map[string]interface{}, ss *grpc.Server) (rgrpc.Service, error) { +func New(m map[string]interface{}) (rgrpc.Service, error) { c, err := parseConfig(m) if err != nil { return nil, err diff --git a/internal/grpc/services/appprovider/appprovider.go b/internal/grpc/services/appprovider/appprovider.go index d09440f7f4..533afe4006 100644 --- a/internal/grpc/services/appprovider/appprovider.go +++ b/internal/grpc/services/appprovider/appprovider.go @@ -83,7 +83,7 @@ func parseConfig(m map[string]interface{}) (*config, error) { } // New creates a new AppProviderService. -func New(m map[string]interface{}, ss *grpc.Server) (rgrpc.Service, error) { +func New(m map[string]interface{}) (rgrpc.Service, error) { c, err := parseConfig(m) if err != nil { return nil, err diff --git a/internal/grpc/services/appregistry/appregistry.go b/internal/grpc/services/appregistry/appregistry.go index 619d366942..d70b9e7746 100644 --- a/internal/grpc/services/appregistry/appregistry.go +++ b/internal/grpc/services/appregistry/appregistry.go @@ -63,7 +63,7 @@ func (c *config) init() { } // New creates a new StorageRegistryService. -func New(m map[string]interface{}, ss *grpc.Server) (rgrpc.Service, error) { +func New(m map[string]interface{}) (rgrpc.Service, error) { c, err := parseConfig(m) if err != nil { return nil, err diff --git a/internal/grpc/services/appregistry/appregistry_test.go b/internal/grpc/services/appregistry/appregistry_test.go index 0bf23f8fa3..34b8c5d3d1 100644 --- a/internal/grpc/services/appregistry/appregistry_test.go +++ b/internal/grpc/services/appregistry/appregistry_test.go @@ -355,7 +355,7 @@ func TestNew(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := New(tt.m, nil) + got, err := New(tt.m) if err != nil { assert.Equal(t, tt.wantErr, err.Error()) assert.Nil(t, got) diff --git a/internal/grpc/services/authprovider/authprovider.go b/internal/grpc/services/authprovider/authprovider.go index c080d8135f..135af03745 100644 --- a/internal/grpc/services/authprovider/authprovider.go +++ b/internal/grpc/services/authprovider/authprovider.go @@ -100,7 +100,7 @@ func getAuthManager(manager string, m map[string]map[string]interface{}) (auth.M } // New returns a new AuthProviderServiceServer. -func New(m map[string]interface{}, ss *grpc.Server) (rgrpc.Service, error) { +func New(m map[string]interface{}) (rgrpc.Service, error) { c, err := parseConfig(m) if err != nil { return nil, err diff --git a/internal/grpc/services/authregistry/authregistry.go b/internal/grpc/services/authregistry/authregistry.go index 0abbe63ad5..dcbbd1b471 100644 --- a/internal/grpc/services/authregistry/authregistry.go +++ b/internal/grpc/services/authregistry/authregistry.go @@ -66,7 +66,7 @@ func (c *config) init() { } // New creates a new AuthRegistry. -func New(m map[string]interface{}, ss *grpc.Server) (rgrpc.Service, error) { +func New(m map[string]interface{}) (rgrpc.Service, error) { c, err := parseConfig(m) if err != nil { return nil, err diff --git a/internal/grpc/services/datatx/datatx.go b/internal/grpc/services/datatx/datatx.go index b9f5011a3d..ce82fa5c36 100644 --- a/internal/grpc/services/datatx/datatx.go +++ b/internal/grpc/services/datatx/datatx.go @@ -88,7 +88,7 @@ func parseConfig(m map[string]interface{}) (*config, error) { } // New creates a new datatx svc. -func New(m map[string]interface{}, ss *grpc.Server) (rgrpc.Service, error) { +func New(m map[string]interface{}) (rgrpc.Service, error) { c, err := parseConfig(m) if err != nil { return nil, err diff --git a/internal/grpc/services/gateway/gateway.go b/internal/grpc/services/gateway/gateway.go index 72776e5fb5..445ae7b13a 100644 --- a/internal/grpc/services/gateway/gateway.go +++ b/internal/grpc/services/gateway/gateway.go @@ -124,7 +124,7 @@ type svc struct { // New creates a new gateway svc that acts as a proxy for any grpc operation. // The gateway is responsible for high-level controls: rate-limiting, coordination between svcs // like sharing and storage acls, asynchronous transactions, ... -func New(m map[string]interface{}, ss *grpc.Server) (rgrpc.Service, error) { +func New(m map[string]interface{}) (rgrpc.Service, error) { c, err := parseConfig(m) if err != nil { return nil, err diff --git a/internal/grpc/services/groupprovider/groupprovider.go b/internal/grpc/services/groupprovider/groupprovider.go index 1294e3892e..7e23c2f97b 100644 --- a/internal/grpc/services/groupprovider/groupprovider.go +++ b/internal/grpc/services/groupprovider/groupprovider.go @@ -68,7 +68,7 @@ func getDriver(c *config) (group.Manager, error) { } // New returns a new GroupProviderServiceServer. -func New(m map[string]interface{}, ss *grpc.Server) (rgrpc.Service, error) { +func New(m map[string]interface{}) (rgrpc.Service, error) { c, err := parseConfig(m) if err != nil { return nil, err diff --git a/internal/grpc/services/helloworld/helloworld.go b/internal/grpc/services/helloworld/helloworld.go index f0e9c311be..40aecaa356 100644 --- a/internal/grpc/services/helloworld/helloworld.go +++ b/internal/grpc/services/helloworld/helloworld.go @@ -43,7 +43,7 @@ type service struct { // New returns a new PreferencesServiceServer // It can be tested like this: // prototool grpc --address 0.0.0.0:9999 --method 'revad.helloworld.HelloWorldService/Hello' --data '{"name": "Alice"}'. -func New(m map[string]interface{}, ss *grpc.Server) (rgrpc.Service, error) { +func New(m map[string]interface{}) (rgrpc.Service, error) { c := &conf{} if err := mapstructure.Decode(m, c); err != nil { err = errors.Wrap(err, "helloworld: error decoding conf") diff --git a/internal/grpc/services/ocmcore/ocmcore.go b/internal/grpc/services/ocmcore/ocmcore.go index ebb0d9b9f6..16686bc9fd 100644 --- a/internal/grpc/services/ocmcore/ocmcore.go +++ b/internal/grpc/services/ocmcore/ocmcore.go @@ -78,7 +78,7 @@ func parseConfig(m map[string]interface{}) (*config, error) { } // New creates a new ocm core svc. -func New(m map[string]interface{}, ss *grpc.Server) (rgrpc.Service, error) { +func New(m map[string]interface{}) (rgrpc.Service, error) { c, err := parseConfig(m) if err != nil { return nil, err diff --git a/internal/grpc/services/ocminvitemanager/ocminvitemanager.go b/internal/grpc/services/ocminvitemanager/ocminvitemanager.go index 80d8ff1d01..38cf922c5a 100644 --- a/internal/grpc/services/ocminvitemanager/ocminvitemanager.go +++ b/internal/grpc/services/ocminvitemanager/ocminvitemanager.go @@ -103,7 +103,7 @@ func parseConfig(m map[string]interface{}) (*config, error) { } // New creates a new OCM invite manager svc. -func New(m map[string]interface{}, ss *grpc.Server) (rgrpc.Service, error) { +func New(m map[string]interface{}) (rgrpc.Service, error) { c, err := parseConfig(m) if err != nil { return nil, err diff --git a/internal/grpc/services/ocmproviderauthorizer/ocmproviderauthorizer.go b/internal/grpc/services/ocmproviderauthorizer/ocmproviderauthorizer.go index 9d0892068d..e14645fe5a 100644 --- a/internal/grpc/services/ocmproviderauthorizer/ocmproviderauthorizer.go +++ b/internal/grpc/services/ocmproviderauthorizer/ocmproviderauthorizer.go @@ -74,7 +74,7 @@ func parseConfig(m map[string]interface{}) (*config, error) { } // New creates a new OCM provider authorizer svc. -func New(m map[string]interface{}, ss *grpc.Server) (rgrpc.Service, error) { +func New(m map[string]interface{}) (rgrpc.Service, error) { c, err := parseConfig(m) if err != nil { return nil, err diff --git a/internal/grpc/services/ocmshareprovider/ocmshareprovider.go b/internal/grpc/services/ocmshareprovider/ocmshareprovider.go index 9051f1dba2..c8f39962f0 100644 --- a/internal/grpc/services/ocmshareprovider/ocmshareprovider.go +++ b/internal/grpc/services/ocmshareprovider/ocmshareprovider.go @@ -110,7 +110,7 @@ func parseConfig(m map[string]interface{}) (*config, error) { } // New creates a new ocm share provider svc. -func New(m map[string]interface{}, ss *grpc.Server) (rgrpc.Service, error) { +func New(m map[string]interface{}) (rgrpc.Service, error) { c, err := parseConfig(m) if err != nil { return nil, err diff --git a/internal/grpc/services/permissions/permissions.go b/internal/grpc/services/permissions/permissions.go index 490d4c78fa..428e5457f1 100644 --- a/internal/grpc/services/permissions/permissions.go +++ b/internal/grpc/services/permissions/permissions.go @@ -55,7 +55,7 @@ type service struct { } // New returns a new PermissionsServiceServer. -func New(m map[string]interface{}, ss *grpc.Server) (rgrpc.Service, error) { +func New(m map[string]interface{}) (rgrpc.Service, error) { c, err := parseConfig(m) if err != nil { return nil, err diff --git a/internal/grpc/services/preferences/preferences.go b/internal/grpc/services/preferences/preferences.go index 25c814b1cc..f2b096cbf7 100644 --- a/internal/grpc/services/preferences/preferences.go +++ b/internal/grpc/services/preferences/preferences.go @@ -69,7 +69,7 @@ func parseConfig(m map[string]interface{}) (*config, error) { } // New returns a new PreferencesServiceServer. -func New(m map[string]interface{}, ss *grpc.Server) (rgrpc.Service, error) { +func New(m map[string]interface{}) (rgrpc.Service, error) { c, err := parseConfig(m) if err != nil { return nil, err diff --git a/internal/grpc/services/publicshareprovider/publicshareprovider.go b/internal/grpc/services/publicshareprovider/publicshareprovider.go index 5829b050d8..1d513e5554 100644 --- a/internal/grpc/services/publicshareprovider/publicshareprovider.go +++ b/internal/grpc/services/publicshareprovider/publicshareprovider.go @@ -87,7 +87,7 @@ func parseConfig(m map[string]interface{}) (*config, error) { } // New creates a new user share provider svc. -func New(m map[string]interface{}, ss *grpc.Server) (rgrpc.Service, error) { +func New(m map[string]interface{}) (rgrpc.Service, error) { c, err := parseConfig(m) if err != nil { return nil, err diff --git a/internal/grpc/services/publicstorageprovider/publicstorageprovider.go b/internal/grpc/services/publicstorageprovider/publicstorageprovider.go index bf913e4136..43c141e6bf 100644 --- a/internal/grpc/services/publicstorageprovider/publicstorageprovider.go +++ b/internal/grpc/services/publicstorageprovider/publicstorageprovider.go @@ -83,7 +83,7 @@ func parseConfig(m map[string]interface{}) (*config, error) { } // New creates a new IsPublic Storage Provider service. -func New(m map[string]interface{}, ss *grpc.Server) (rgrpc.Service, error) { +func New(m map[string]interface{}) (rgrpc.Service, error) { c, err := parseConfig(m) if err != nil { return nil, err diff --git a/internal/grpc/services/storageprovider/storageprovider.go b/internal/grpc/services/storageprovider/storageprovider.go index 572cf2bdd0..9bb1011817 100644 --- a/internal/grpc/services/storageprovider/storageprovider.go +++ b/internal/grpc/services/storageprovider/storageprovider.go @@ -162,7 +162,7 @@ func registerMimeTypes(mappingFile string) error { } // New creates a new storage provider svc. -func New(m map[string]interface{}, ss *grpc.Server) (rgrpc.Service, error) { +func New(m map[string]interface{}) (rgrpc.Service, error) { c, err := parseConfig(m) if err != nil { return nil, err diff --git a/internal/grpc/services/storageregistry/storageregistry.go b/internal/grpc/services/storageregistry/storageregistry.go index b7304ba571..685a8c0004 100644 --- a/internal/grpc/services/storageregistry/storageregistry.go +++ b/internal/grpc/services/storageregistry/storageregistry.go @@ -64,7 +64,7 @@ func (c *config) init() { } // New creates a new StorageBrokerService. -func New(m map[string]interface{}, ss *grpc.Server) (rgrpc.Service, error) { +func New(m map[string]interface{}) (rgrpc.Service, error) { c, err := parseConfig(m) if err != nil { return nil, err diff --git a/internal/grpc/services/userprovider/userprovider.go b/internal/grpc/services/userprovider/userprovider.go index 27907db225..fa6cb78abc 100644 --- a/internal/grpc/services/userprovider/userprovider.go +++ b/internal/grpc/services/userprovider/userprovider.go @@ -87,7 +87,7 @@ func getDriver(c *config) (user.Manager, *plugin.RevaPlugin, error) { } // New returns a new UserProviderServiceServer. -func New(m map[string]interface{}, ss *grpc.Server) (rgrpc.Service, error) { +func New(m map[string]interface{}) (rgrpc.Service, error) { c, err := parseConfig(m) if err != nil { return nil, err diff --git a/internal/grpc/services/usershareprovider/usershareprovider.go b/internal/grpc/services/usershareprovider/usershareprovider.go index 523a7b6c1f..074397364e 100644 --- a/internal/grpc/services/usershareprovider/usershareprovider.go +++ b/internal/grpc/services/usershareprovider/usershareprovider.go @@ -89,7 +89,7 @@ func parseConfig(m map[string]interface{}) (*config, error) { } // New creates a new user share provider svc. -func New(m map[string]interface{}, ss *grpc.Server) (rgrpc.Service, error) { +func New(m map[string]interface{}) (rgrpc.Service, error) { c, err := parseConfig(m) if err != nil { return nil, err From e0cf040d5f899e6e703c1fb1e3c83a284455a7b6 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Mon, 26 Jun 2023 10:26:27 +0200 Subject: [PATCH 33/76] refactoring --- pkg/rgrpc/rgrpc.go | 37 ++++++++++------- pkg/rhttp/rhttp.go | 47 ++++++++++----------- pkg/sharedconf/sharedconf.go | 68 +++++++++++++++---------------- pkg/sharedconf/sharedconf_test.go | 62 ++++++++++++++-------------- pkg/utils/list/list.go | 10 +++++ 5 files changed, 118 insertions(+), 106 deletions(-) diff --git a/pkg/rgrpc/rgrpc.go b/pkg/rgrpc/rgrpc.go index 8533bb749e..c57ce24fbc 100644 --- a/pkg/rgrpc/rgrpc.go +++ b/pkg/rgrpc/rgrpc.go @@ -19,10 +19,12 @@ package rgrpc import ( + "fmt" "io" "net" "sort" + "github.com/cs3org/reva/cmd/revad/pkg/config" "github.com/cs3org/reva/internal/grpc/interceptors/appctx" "github.com/cs3org/reva/internal/grpc/interceptors/log" "github.com/cs3org/reva/internal/grpc/interceptors/recovery" @@ -69,7 +71,7 @@ func Register(name string, newFunc NewService) { // NewService is the function that gRPC services need to register at init time. // It returns an io.Closer to close the service and a list of service endpoints that need to be unprotected. -type NewService func(conf map[string]interface{}, ss *grpc.Server) (Service, error) +type NewService func(conf map[string]interface{}) (Service, error) // Service represents a grpc service. type Service interface { @@ -101,6 +103,25 @@ type Server struct { services map[string]Service } +func InitServices(services map[string]config.ServicesConfig) (map[string]Service, error) { + s := make(map[string]Service) + for name, cfg := range services { + new, ok := Services[name] + if !ok { + return nil, fmt.Errorf("rgrpc: grpc service %s does not exist", name) + } + if cfg.DriversNumber() > 1 { + return nil, fmt.Errorf("rgrp: service %s cannot have more than one driver in same server", name) + } + svc, err := new(cfg[0].Config) + if err != nil { + return nil, errors.Wrapf(err, "rgrpc: grpc service %s could not be started,", name) + } + s[name] = svc + } + return s, nil +} + // NewServer returns a new Server. func NewServer(o ...Option) (*Server, error) { server := &Server{} @@ -138,20 +159,6 @@ func (s *Server) Start(ln net.Listener) error { // } func (s *Server) initServices() error { - // for svcName := range s.conf.Services { - // if s.isServiceEnabled(svcName) { - // newFunc := Services[svcName] - // svc, err := newFunc(s.conf.Services[svcName], s.s) - // if err != nil { - // return errors.Wrapf(err, "rgrpc: grpc service %s could not be started,", svcName) - // } - // s.services[svcName] = svc - // s.log.Info().Msgf("rgrpc: grpc service enabled: %s", svcName) - // } else { - // message := fmt.Sprintf("rgrpc: grpc service %s does not exist", svcName) - // return errors.New(message) - // } - // } // obtain list of unprotected endpoints unprotected := []string{} diff --git a/pkg/rhttp/rhttp.go b/pkg/rhttp/rhttp.go index 423b5bc377..a375ce4a07 100644 --- a/pkg/rhttp/rhttp.go +++ b/pkg/rhttp/rhttp.go @@ -20,6 +20,7 @@ package rhttp import ( "context" + "fmt" "net" "net/http" "path" @@ -27,6 +28,7 @@ import ( "strings" "time" + "github.com/cs3org/reva/cmd/revad/pkg/config" "github.com/cs3org/reva/pkg/rhttp/global" rtrace "github.com/cs3org/reva/pkg/trace" "github.com/pkg/errors" @@ -61,6 +63,26 @@ func WithLogger(log zerolog.Logger) Config { } } +func InitServices(services map[string]config.ServicesConfig) (map[string]global.Service, error) { + s := make(map[string]global.Service) + for name, cfg := range services { + new, ok := global.Services[name] + if !ok { + return nil, fmt.Errorf("http service %s does not exist", name) + } + if cfg.DriversNumber() > 1 { + return nil, fmt.Errorf("service %s cannot have more than one driver in the same server", name) + } + log := zerolog.Nop() // TODO: pass correct log + svc, err := new(cfg[0].Config, &log) + if err != nil { + return nil, errors.Wrapf(err, "http service %s could not be started", name) + } + s[name] = svc + } + return s, nil +} + // New returns a new server. func New(c ...Config) (*Server, error) { httpServer := &http.Server{} @@ -197,31 +219,6 @@ func (s *Server) registerServices() { } } -// if s.isServiceEnabled(svcName) { -// newFunc := global.Services[svcName] -// svcLogger := s.log.With().Str("service", svcName).Logger() -// svc, err := newFunc(s.conf.Services[svcName], &svcLogger) -// if err != nil { -// err = errors.Wrapf(err, "http service %s could not be started,", svcName) -// return err -// } - -// // instrument services with opencensus tracing. -// h := traceHandler(svcName, svc.Handler()) -// s.handlers[svc.Prefix()] = h -// s.svcs[svc.Prefix()] = svc -// s.unprotected = append(s.unprotected, getUnprotected(svc.Prefix(), svc.Unprotected())...) -// s.log.Info().Msgf("http service enabled: %s@/%s", svcName, svc.Prefix()) -// } else { -// message := fmt.Sprintf("http service %s does not exist", svcName) -// return errors.New(message) -// } - -// func (s *Server) isServiceEnabled(svcName string) bool { -// _, ok := global.Services[svcName] -// return ok -// } - // TODO(labkode): if the http server is exposed under a basename we need to prepend // to prefix. func getUnprotected(prefix string, unprotected []string) []string { diff --git a/pkg/sharedconf/sharedconf.go b/pkg/sharedconf/sharedconf.go index 0503a5a806..d33bf1479f 100644 --- a/pkg/sharedconf/sharedconf.go +++ b/pkg/sharedconf/sharedconf.go @@ -19,51 +19,49 @@ package sharedconf import ( - "fmt" - "os" + "sync" - "github.com/mitchellh/mapstructure" + "github.com/cs3org/reva/cmd/revad/pkg/config" ) -var sharedConf = &conf{} +var sharedConf *config.Shared +var once sync.Once -type conf struct { - JWTSecret string `mapstructure:"jwt_secret"` - GatewaySVC string `mapstructure:"gatewaysvc"` - DataGateway string `mapstructure:"datagateway"` - SkipUserGroupsInToken bool `mapstructure:"skip_user_groups_in_token"` - BlockedUsers []string `mapstructure:"blocked_users"` +func Init(c *config.Shared) { + once.Do(func() { + sharedConf = c + }) } -// Decode decodes the configuration. -func Decode(v interface{}) error { - if err := mapstructure.Decode(v, sharedConf); err != nil { - return err - } +// // Decode decodes the configuration. +// func Decode(v interface{}) error { +// if err := mapstructure.Decode(v, sharedConf); err != nil { +// return err +// } - // add some defaults - if sharedConf.GatewaySVC == "" { - sharedConf.GatewaySVC = "0.0.0.0:19000" - } +// // add some defaults +// if sharedConf.GatewaySVC == "" { +// sharedConf.GatewaySVC = "0.0.0.0:19000" +// } - // this is the default address we use for the data gateway HTTP service - if sharedConf.DataGateway == "" { - host, err := os.Hostname() - if err != nil || host == "" { - sharedConf.DataGateway = "http://0.0.0.0:19001/datagateway" - } else { - sharedConf.DataGateway = fmt.Sprintf("http://%s:19001/datagateway", host) - } - } +// // this is the default address we use for the data gateway HTTP service +// if sharedConf.DataGateway == "" { +// host, err := os.Hostname() +// if err != nil || host == "" { +// sharedConf.DataGateway = "http://0.0.0.0:19001/datagateway" +// } else { +// sharedConf.DataGateway = fmt.Sprintf("http://%s:19001/datagateway", host) +// } +// } - // TODO(labkode): would be cool to autogenerate one secret and print - // it on init time. - if sharedConf.JWTSecret == "" { - sharedConf.JWTSecret = "changemeplease" - } +// // TODO(labkode): would be cool to autogenerate one secret and print +// // it on init time. +// if sharedConf.JWTSecret == "" { +// sharedConf.JWTSecret = "changemeplease" +// } - return nil -} +// return nil +// } // GetJWTSecret returns the package level configured jwt secret if not overwritten. func GetJWTSecret(val string) string { diff --git a/pkg/sharedconf/sharedconf_test.go b/pkg/sharedconf/sharedconf_test.go index e6ad94a777..0140e045bf 100644 --- a/pkg/sharedconf/sharedconf_test.go +++ b/pkg/sharedconf/sharedconf_test.go @@ -23,42 +23,42 @@ import ( ) func Test(t *testing.T) { - conf := map[string]interface{}{ - "jwt_secret": "", - "gateway": "", - } + // conf := map[string]interface{}{ + // "jwt_secret": "", + // "gateway": "", + // } - err := Decode(conf) - if err != nil { - t.Fatal(err) - } + // err := Decode(conf) + // if err != nil { + // t.Fatal(err) + // } - got := GetJWTSecret("secret") - if got != "secret" { - t.Fatalf("expected %q got %q", "secret", got) - } + // got := GetJWTSecret("secret") + // if got != "secret" { + // t.Fatalf("expected %q got %q", "secret", got) + // } - got = GetJWTSecret("") - if got != "changemeplease" { - t.Fatalf("expected %q got %q", "changemeplease", got) - } + // got = GetJWTSecret("") + // if got != "changemeplease" { + // t.Fatalf("expected %q got %q", "changemeplease", got) + // } - conf = map[string]interface{}{ - "jwt_secret": "dummy", - } + // conf = map[string]interface{}{ + // "jwt_secret": "dummy", + // } - err = Decode(conf) - if err != nil { - t.Fatal(err) - } + // err = Decode(conf) + // if err != nil { + // t.Fatal(err) + // } - got = GetJWTSecret("secret") - if got != "secret" { - t.Fatalf("expected %q got %q", "secret", got) - } + // got = GetJWTSecret("secret") + // if got != "secret" { + // t.Fatalf("expected %q got %q", "secret", got) + // } - got = GetJWTSecret("") - if got != "dummy" { - t.Fatalf("expected %q got %q", "dummy", got) - } + // got = GetJWTSecret("") + // if got != "dummy" { + // t.Fatalf("expected %q got %q", "dummy", got) + // } } diff --git a/pkg/utils/list/list.go b/pkg/utils/list/list.go index 6812bfc306..de279ebf77 100644 --- a/pkg/utils/list/list.go +++ b/pkg/utils/list/list.go @@ -46,3 +46,13 @@ func TakeFirst[T any](l []T, p func(T) bool) (T, bool) { var z T return z, false } + +// ToMap returns a map from l where the keys are obtainined applying +// the func k to the elements of l. +func ToMap[K comparable, T any](l []T, k func(T) K) map[K]T { + m := make(map[K]T, len(l)) + for _, e := range l { + m[k(e)] = e + } + return m +} From 2a08e555c7a1a1ed261e2f073e7e5747b97a221b Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Mon, 26 Jun 2023 11:21:44 +0200 Subject: [PATCH 34/76] cleanup code --- cmd/revad/main.go | 22 ++++-- cmd/revad/pkg/config/common.go | 1 + cmd/revad/runtime/log.go | 7 +- cmd/revad/runtime/option.go | 8 +++ cmd/revad/runtime/runtime.go | 122 +++++++++++++-------------------- 5 files changed, 77 insertions(+), 83 deletions(-) diff --git a/cmd/revad/main.go b/cmd/revad/main.go index 3f8e2f330e..3465e22d79 100644 --- a/cmd/revad/main.go +++ b/cmd/revad/main.go @@ -32,6 +32,7 @@ import ( "github.com/cs3org/reva/cmd/revad/pkg/grace" "github.com/cs3org/reva/cmd/revad/runtime" "github.com/cs3org/reva/pkg/sysinfo" + "github.com/google/uuid" ) var ( @@ -40,7 +41,6 @@ var ( signalFlag = flag.String("s", "", "send signal to a master process: stop, quit, reload") configFlag = flag.String("c", "/etc/revad/revad.toml", "set configuration file") pidFlag = flag.String("p", "", "pid file. If empty defaults to a random file in the OS temporary directory") - logFlag = flag.String("log", "", "log messages with the given severity or above. One of: [trace, debug, info, warn, error, fatal, panic]") dirFlag = flag.String("dev-dir", "", "runs any toml file in the specified directory. Intended for development use only") // Compile time variables initialized with gcc flags. @@ -208,8 +208,9 @@ func readConfigs(files []string) ([]*config.Config, error) { } func runConfigs(confs []*config.Config) { + pidfile := getPidfile() if len(confs) == 1 { - runSingle(confs[0]) + runSingle(confs[0], pidfile) return } @@ -220,10 +221,10 @@ func registerReva(r *runtime.Reva) { revaProcs = append(revaProcs, r) } -func runSingle(conf *config.Config) { - reva, err := runtime.New(conf) +func runSingle(conf *config.Config, pidfile string) { + reva, err := runtime.New(conf, runtime.WithPidFile(pidfile)) if err != nil { - fmt.Fprintf(os.Stderr, err.Error()) + fmt.Fprint(os.Stderr, err.Error()) os.Exit(1) } registerReva(reva) @@ -232,13 +233,22 @@ func runSingle(conf *config.Config) { func runMultiple(confs []*config.Config) { var wg sync.WaitGroup + pidfile := getPidfile() + for _, conf := range confs { wg.Add(1) go func(wg *sync.WaitGroup, conf *config.Config) { defer wg.Done() - runSingle(conf) + runSingle(conf, pidfile) }(&wg, conf) } wg.Wait() os.Exit(0) } + +func getPidfile() string { + uuid := uuid.New().String() + name := fmt.Sprintf("revad-%s.pid", uuid) + + return path.Join(os.TempDir(), name) +} diff --git a/cmd/revad/pkg/config/common.go b/cmd/revad/pkg/config/common.go index aaa224dede..5371fe066d 100644 --- a/cmd/revad/pkg/config/common.go +++ b/cmd/revad/pkg/config/common.go @@ -85,6 +85,7 @@ func parseMiddlwares(cfg map[string]any, key string) (map[string]map[string]any, // Service contains the configuration for a service. type Service struct { Address string + Network string Name string Label string Config map[string]any diff --git a/cmd/revad/runtime/log.go b/cmd/revad/runtime/log.go index 98227b2ecc..8731202669 100644 --- a/cmd/revad/runtime/log.go +++ b/cmd/revad/runtime/log.go @@ -10,10 +10,13 @@ import ( "github.com/rs/zerolog" ) -func initLogger(conf *config.Log) *zerolog.Logger { +func initLogger(conf *config.Log, opts Options) *zerolog.Logger { + if opts.Logger != nil { + return opts.Logger + } log, err := newLogger(conf) if err != nil { - abort("error creating logger: %w", err) + abort("error creating logger: %v", err) } return log } diff --git a/cmd/revad/runtime/option.go b/cmd/revad/runtime/option.go index 6a662e9fa9..9c500b2e1e 100644 --- a/cmd/revad/runtime/option.go +++ b/cmd/revad/runtime/option.go @@ -30,6 +30,7 @@ type Option func(o *Options) type Options struct { Logger *zerolog.Logger Registry registry.Registry + PidFile string } // newOptions initializes the available default options. @@ -50,6 +51,13 @@ func WithLogger(logger *zerolog.Logger) Option { } } +// WithPidFile sets to pidfile to use. +func WithPidFile(pidfile string) Option { + return func(o *Options) { + o.PidFile = pidfile + } +} + // WithRegistry provides a function to set the registry. func WithRegistry(r registry.Registry) Option { return func(o *Options) { diff --git a/cmd/revad/runtime/runtime.go b/cmd/revad/runtime/runtime.go index 90a0cde329..a606e6b1a6 100644 --- a/cmd/revad/runtime/runtime.go +++ b/cmd/revad/runtime/runtime.go @@ -22,16 +22,16 @@ import ( "fmt" "net" "os" - "path" "runtime" "strconv" "strings" + "github.com/pkg/errors" + "github.com/cs3org/reva/cmd/revad/pkg/config" "github.com/cs3org/reva/cmd/revad/pkg/grace" "github.com/cs3org/reva/pkg/sharedconf" "github.com/cs3org/reva/pkg/utils/maps" - "github.com/google/uuid" "github.com/rs/zerolog" "golang.org/x/sync/errgroup" ) @@ -42,29 +42,49 @@ type Reva struct { servers []*Server watcher *grace.Watcher lns map[string]net.Listener + + pidfile string + log *zerolog.Logger } -func New(config *config.Config) (*Reva, error) { - _ = initLogger(config.Log) +func New(config *config.Config, opt ...Option) (*Reva, error) { + opts := newOptions(opt...) + + log := initLogger(config.Log, opts) initSharedConf(config) - pidfile := getPidfile() + if err := initCPUCount(config.Core, log); err != nil { + return nil, err + } grpc, addrGRPC := groupGRPCByAddress(config) http, addrHTTP := groupHTTPByAddress(config) - log := zerolog.Nop() - watcher, err := initWatcher(&log, pidfile) + if opts.PidFile == "" { + return nil, errors.New("pid file not provided") + } + + watcher, err := initWatcher(opts.PidFile, log) if err != nil { return nil, err } - listeners := initListeners(watcher, maps.Merge(addrGRPC, addrHTTP), &log) - setRandomAddresses(config, listeners) - applyTemplates(config) + listeners, err := watcher.GetListeners(maps.Merge(addrGRPC, addrHTTP)) + if err != nil { + watcher.Exit(1) + return nil, err + } + + setRandomAddresses(config, listeners, log) + + if err := applyTemplates(config); err != nil { + watcher.Exit(1) + return nil, err + } servers, err := newServers(grpc, http) if err != nil { + watcher.Exit(1) return nil, err } @@ -73,17 +93,19 @@ func New(config *config.Config) (*Reva, error) { servers: servers, watcher: watcher, lns: listeners, + pidfile: opts.PidFile, + log: log, }, nil } -func setRandomAddresses(c *config.Config, lns map[string]net.Listener) { +func setRandomAddresses(c *config.Config, lns map[string]net.Listener, log *zerolog.Logger) { f := func(s *config.Service) { if s.Address != "" { return } ln, ok := lns[s.Label] if !ok { - abort("port not assigned for service %s", s.Label) + log.Fatal().Msg("port not assigned for service " + s.Label) } s.SetAddress(ln.Addr().String()) } @@ -91,15 +113,6 @@ func setRandomAddresses(c *config.Config, lns map[string]net.Listener) { c.HTTP.ForEachService(f) } -func initListeners(watcher *grace.Watcher, servers map[string]grace.Addressable, log *zerolog.Logger) map[string]net.Listener { - listeners, err := watcher.GetListeners(servers) - if err != nil { - log.Error().Err(err).Msg("error getting sockets") - watcher.Exit(1) - } - return listeners -} - type addr struct { address string network string @@ -121,14 +134,14 @@ func groupGRPCByAddress(cfg *config.Config) (map[string]*config.GRPC, map[string if _, ok := g[s.Address]; !ok { g[s.Address] = &config.GRPC{ Address: s.Address, - Network: "tcp", // TODO: configure this as well + Network: s.Network, ShutdownDeadline: cfg.GRPC.ShutdownDeadline, EnableReflection: cfg.GRPC.EnableReflection, Services: make(map[string]config.ServicesConfig), Interceptors: cfg.GRPC.Interceptors, } if s.Address == "" { - a[s.Label] = &addr{address: s.Address, network: "tcp"} + a[s.Label] = &addr{address: s.Address, network: s.Network} } } g[s.Address].Services[s.Name] = config.ServicesConfig{ @@ -145,14 +158,14 @@ func groupHTTPByAddress(cfg *config.Config) (map[string]*config.HTTP, map[string if _, ok := g[s.Address]; !ok { g[s.Address] = &config.HTTP{ Address: s.Address, - Network: "tpc", // TODO: configure this as well + Network: s.Network, CertFile: cfg.HTTP.CertFile, KeyFile: cfg.HTTP.KeyFile, Services: make(map[string]config.ServicesConfig), Middlewares: cfg.HTTP.Middlewares, } if s.Address == "" { - a[s.Label] = &addr{address: s.Address, network: "tcp"} + a[s.Label] = &addr{address: s.Address, network: s.Network} } } g[s.Address].Services[s.Name] = config.ServicesConfig{ @@ -162,7 +175,7 @@ func groupHTTPByAddress(cfg *config.Config) (map[string]*config.HTTP, map[string return g, a } -func (r *Reva) Start() { +func (r *Reva) Start() error { var g errgroup.Group for _, server := range r.servers { server := server @@ -172,70 +185,29 @@ func (r *Reva) Start() { } r.watcher.TrapSignals() - if err := g.Wait(); err != nil { - // TODO: log error - } -} - -func getPidfile() string { - uuid := uuid.New().String() - name := fmt.Sprintf("revad-%s.pid", uuid) - - return path.Join(os.TempDir(), name) + return g.Wait() } func initSharedConf(config *config.Config) { sharedconf.Init(config.Shared) } -func initWatcher(log *zerolog.Logger, filename string) (*grace.Watcher, error) { - watcher, err := handlePIDFlag(log, filename) +func initWatcher(filename string, log *zerolog.Logger) (*grace.Watcher, error) { + return handlePIDFlag(log, filename) // TODO(labkode): maybe pidfile can be created later on? like once a server is going to be created? - if err != nil { - log.Error().Err(err).Msg("error creating grace watcher") - os.Exit(1) - } - return watcher, err } -func assignRandomAddress(configs []*config.Config) (addr []net.Listener) { - assign := func(s *config.Service) { - if s.Address == "" { - random, err := randomListener("tpc") // TODO: take from config - if err != nil { - abort("error assigning random port to service %s: %v", s.Name, err) - } - s.SetAddress(random.Addr().String()) - addr = append(addr, random) - } - } - for _, c := range configs { - c.GRPC.ForEachService(assign) - c.HTTP.ForEachService(assign) - } - return -} - -func randomListener(network string) (net.Listener, error) { - return net.Listen(network, ":0") -} - -func applyTemplates(config *config.Config) { - // TODO: we might want to prefer before the actual config in the lookup - // and then the others - if err := config.ApplyTemplates(config); err != nil { - abort("error applying templated to config: %v", err) - } +func applyTemplates(config *config.Config) error { + return config.ApplyTemplates(config) } -func initCPUCount(conf *config.Core, log *zerolog.Logger) { +func initCPUCount(conf *config.Core, log *zerolog.Logger) error { ncpus, err := adjustCPU(conf.MaxCPUs) if err != nil { - log.Error().Err(err).Msg("error adjusting number of cpus") - os.Exit(1) + return errors.Wrap(err, "error adjusting number of cpus") } - // log.Info().Msgf("%s", getVersionString()) log.Info().Msgf("running on %d cpus", ncpus) + return nil } func abort(msg string, args ...any) { From 11589e04a3ea37255ad044711a1fb17229771c43 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Mon, 26 Jun 2023 11:25:59 +0200 Subject: [PATCH 35/76] set logger to http and grpc services --- cmd/revad/runtime/runtime.go | 2 +- cmd/revad/runtime/server.go | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/cmd/revad/runtime/runtime.go b/cmd/revad/runtime/runtime.go index a606e6b1a6..85409098fb 100644 --- a/cmd/revad/runtime/runtime.go +++ b/cmd/revad/runtime/runtime.go @@ -82,7 +82,7 @@ func New(config *config.Config, opt ...Option) (*Reva, error) { return nil, err } - servers, err := newServers(grpc, http) + servers, err := newServers(grpc, http, log) if err != nil { watcher.Exit(1) return nil, err diff --git a/cmd/revad/runtime/server.go b/cmd/revad/runtime/server.go index bd8a59dba7..e4bfe0e8cc 100644 --- a/cmd/revad/runtime/server.go +++ b/cmd/revad/runtime/server.go @@ -13,7 +13,6 @@ import ( type Server struct { server grace.Server listener net.Listener - logger *zerolog.Logger services map[string]any } @@ -22,7 +21,7 @@ func (s *Server) Start() error { return s.server.Start(s.listener) } -func newServers(grpc map[string]*config.GRPC, http map[string]*config.HTTP) ([]*Server, error) { +func newServers(grpc map[string]*config.GRPC, http map[string]*config.HTTP, log *zerolog.Logger) ([]*Server, error) { var servers []*Server for _, cfg := range grpc { services, err := rgrpc.InitServices(cfg.Services) @@ -32,7 +31,7 @@ func newServers(grpc map[string]*config.GRPC, http map[string]*config.HTTP) ([]* s, err := rgrpc.NewServer( rgrpc.EnableReflection(cfg.EnableReflection), rgrpc.WithShutdownDeadline(cfg.ShutdownDeadline), - rgrpc.WithLogger(zerolog.Nop()), // TODO: set logger + rgrpc.WithLogger(log.With().Str("pkg", "grpc").Logger()), rgrpc.WithServices(services), ) if err != nil { @@ -50,7 +49,7 @@ func newServers(grpc map[string]*config.GRPC, http map[string]*config.HTTP) ([]* } s, err := rhttp.New( rhttp.WithServices(services), - rhttp.WithLogger(zerolog.Nop()), // TODO: set logger + rhttp.WithLogger(log.With().Str("pkg", "http").Logger()), rhttp.WithCertAndKeyFiles(cfg.CertFile, cfg.KeyFile), // rhttp.WithMiddlewares(cfg.Middlewares), ) From 0abaaf37b758ef57c88dfce7a18dfec3c2ffdf0e Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Mon, 26 Jun 2023 11:35:19 +0200 Subject: [PATCH 36/76] set services in grpc and http servers --- cmd/revad/runtime/server.go | 8 ++++++-- pkg/utils/maps/maps.go | 8 ++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/cmd/revad/runtime/server.go b/cmd/revad/runtime/server.go index e4bfe0e8cc..033a38ffd1 100644 --- a/cmd/revad/runtime/server.go +++ b/cmd/revad/runtime/server.go @@ -7,6 +7,8 @@ import ( "github.com/cs3org/reva/cmd/revad/pkg/grace" "github.com/cs3org/reva/pkg/rgrpc" "github.com/cs3org/reva/pkg/rhttp" + "github.com/cs3org/reva/pkg/rhttp/global" + "github.com/cs3org/reva/pkg/utils/maps" "github.com/rs/zerolog" ) @@ -38,7 +40,8 @@ func newServers(grpc map[string]*config.GRPC, http map[string]*config.HTTP, log return nil, err } server := &Server{ - server: s, + server: s, + services: maps.MapValues(services, func(s rgrpc.Service) any { return s }), } servers = append(servers, server) } @@ -57,7 +60,8 @@ func newServers(grpc map[string]*config.GRPC, http map[string]*config.HTTP, log return nil, err } server := &Server{ - server: s, + server: s, + services: maps.MapValues(services, func(s global.Service) any { return s }), } servers = append(servers, server) } diff --git a/pkg/utils/maps/maps.go b/pkg/utils/maps/maps.go index 886ae27bfb..7d4fbd9890 100644 --- a/pkg/utils/maps/maps.go +++ b/pkg/utils/maps/maps.go @@ -10,3 +10,11 @@ func Merge[K comparable, T any](m, n map[K]T) map[K]T { } return r } + +func MapValues[K comparable, T, V any](m map[K]T, f func(T) V) map[K]V { + r := make(map[K]V, len(m)) + for k, v := range m { + r[k] = f(v) + } + return r +} From 8164c088cafa366abd7203315e1eaf0a66ff2aa6 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Mon, 26 Jun 2023 15:19:34 +0200 Subject: [PATCH 37/76] initialize grpc interceptors --- cmd/revad/runtime/server.go | 133 +++++++++++++++++++++++++++++++++ pkg/rgrpc/option.go | 13 ++++ pkg/rgrpc/rgrpc.go | 144 +++--------------------------------- 3 files changed, 155 insertions(+), 135 deletions(-) diff --git a/cmd/revad/runtime/server.go b/cmd/revad/runtime/server.go index 033a38ffd1..063d0c6e8a 100644 --- a/cmd/revad/runtime/server.go +++ b/cmd/revad/runtime/server.go @@ -1,15 +1,27 @@ package runtime import ( + "fmt" "net" + "sort" "github.com/cs3org/reva/cmd/revad/pkg/config" "github.com/cs3org/reva/cmd/revad/pkg/grace" + "github.com/cs3org/reva/internal/grpc/interceptors/appctx" + "github.com/cs3org/reva/internal/grpc/interceptors/auth" + "github.com/cs3org/reva/internal/grpc/interceptors/log" + "github.com/cs3org/reva/internal/grpc/interceptors/recovery" + "github.com/cs3org/reva/internal/grpc/interceptors/token" + "github.com/cs3org/reva/internal/grpc/interceptors/useragent" "github.com/cs3org/reva/pkg/rgrpc" "github.com/cs3org/reva/pkg/rhttp" "github.com/cs3org/reva/pkg/rhttp/global" + rtrace "github.com/cs3org/reva/pkg/trace" "github.com/cs3org/reva/pkg/utils/maps" + "github.com/pkg/errors" "github.com/rs/zerolog" + "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc" + "google.golang.org/grpc" ) type Server struct { @@ -23,6 +35,109 @@ func (s *Server) Start() error { return s.server.Start(s.listener) } +func initGRPCInterceptors(conf map[string]map[string]any, unprotected []string, logger *zerolog.Logger) ([]grpc.UnaryServerInterceptor, []grpc.StreamServerInterceptor, error) { + unaryTriples := []*unaryInterceptorTriple{} + for name, c := range conf { + new, ok := rgrpc.UnaryInterceptors[name] + if !ok { + return nil, nil, fmt.Errorf("unary interceptor %s not found", name) + } + inter, prio, err := new(c) + if err != nil { + return nil, nil, errors.Wrap(err, "error creating unary interceptor: "+name) + } + triple := &unaryInterceptorTriple{ + Name: name, + Priority: prio, + Interceptor: inter, + } + unaryTriples = append(unaryTriples, triple) + } + + sort.SliceStable(unaryTriples, func(i, j int) bool { + return unaryTriples[i].Priority < unaryTriples[j].Priority + }) + + authUnary, err := auth.NewUnary(conf["auth"], unprotected) + if err != nil { + return nil, nil, errors.Wrap(err, "error creating unary auth interceptor") + } + + unaryInterceptors := []grpc.UnaryServerInterceptor{authUnary} + for _, t := range unaryTriples { + unaryInterceptors = append(unaryInterceptors, t.Interceptor) + logger.Info().Msgf("rgrpc: chaining grpc unary interceptor %s with priority %d", t.Name, t.Priority) + } + + unaryInterceptors = append(unaryInterceptors, + otelgrpc.UnaryServerInterceptor( + otelgrpc.WithTracerProvider(rtrace.Provider), + otelgrpc.WithPropagators(rtrace.Propagator)), + ) + + unaryInterceptors = append([]grpc.UnaryServerInterceptor{ + appctx.NewUnary(*logger), + token.NewUnary(), + useragent.NewUnary(), + log.NewUnary(), + recovery.NewUnary(), + }, unaryInterceptors...) + + streamTriples := []*streamInterceptorTriple{} + for name, c := range conf { + new, ok := rgrpc.StreamInterceptors[name] + if !ok { + return nil, nil, fmt.Errorf("stream interceptor %s not found", name) + } + inter, prio, err := new(c) + if err != nil { + if err != nil { + return nil, nil, errors.Wrapf(err, "error creating streaming interceptor: %s,", name) + } + triple := &streamInterceptorTriple{ + Name: name, + Priority: prio, + Interceptor: inter, + } + streamTriples = append(streamTriples, triple) + } + + } + // sort stream triples + sort.SliceStable(streamTriples, func(i, j int) bool { + return streamTriples[i].Priority < streamTriples[j].Priority + }) + + authStream, err := auth.NewStream(conf["auth"], unprotected) + if err != nil { + return nil, nil, errors.Wrap(err, "error creating stream auth interceptor") + } + + streamInterceptors := []grpc.StreamServerInterceptor{authStream} + for _, t := range streamTriples { + streamInterceptors = append(streamInterceptors, t.Interceptor) + logger.Info().Msgf("rgrpc: chaining grpc streaming interceptor %s with priority %d", t.Name, t.Priority) + } + + streamInterceptors = append([]grpc.StreamServerInterceptor{ + authStream, + appctx.NewStream(*logger), + token.NewStream(), + useragent.NewStream(), + log.NewStream(), + recovery.NewStream(), + }, streamInterceptors...) + + return unaryInterceptors, streamInterceptors, nil +} + +func grpcUnprotected(s map[string]rgrpc.Service) (unprotected []string) { + for _, svc := range s { + unprotected = append(unprotected, svc.UnprotectedEndpoints()...) + } + return +} + func newServers(grpc map[string]*config.GRPC, http map[string]*config.HTTP, log *zerolog.Logger) ([]*Server, error) { var servers []*Server for _, cfg := range grpc { @@ -30,11 +145,17 @@ func newServers(grpc map[string]*config.GRPC, http map[string]*config.HTTP, log if err != nil { return nil, err } + unaryChain, streamChain, err := initGRPCInterceptors(cfg.Interceptors, grpcUnprotected(services), log) + if err != nil { + return nil, err + } s, err := rgrpc.NewServer( rgrpc.EnableReflection(cfg.EnableReflection), rgrpc.WithShutdownDeadline(cfg.ShutdownDeadline), rgrpc.WithLogger(log.With().Str("pkg", "grpc").Logger()), rgrpc.WithServices(services), + rgrpc.WithUnaryServerInterceptors(unaryChain), + rgrpc.WithStreamServerInterceptors(streamChain), ) if err != nil { return nil, err @@ -67,3 +188,15 @@ func newServers(grpc map[string]*config.GRPC, http map[string]*config.HTTP, log } return servers, nil } + +type unaryInterceptorTriple struct { + Name string + Priority int + Interceptor grpc.UnaryServerInterceptor +} + +type streamInterceptorTriple struct { + Name string + Priority int + Interceptor grpc.StreamServerInterceptor +} diff --git a/pkg/rgrpc/option.go b/pkg/rgrpc/option.go index 638ac9b9e7..7a7c553b76 100644 --- a/pkg/rgrpc/option.go +++ b/pkg/rgrpc/option.go @@ -2,6 +2,7 @@ package rgrpc import ( "github.com/rs/zerolog" + "google.golang.org/grpc" ) type Option func(*Server) @@ -29,3 +30,15 @@ func WithLogger(logger zerolog.Logger) Option { s.log = logger } } + +func WithStreamServerInterceptors(in []grpc.StreamServerInterceptor) Option { + return func(s *Server) { + s.StreamServerInterceptors = in + } +} + +func WithUnaryServerInterceptors(in []grpc.UnaryServerInterceptor) Option { + return func(s *Server) { + s.UnaryServerInterceptors = in + } +} \ No newline at end of file diff --git a/pkg/rgrpc/rgrpc.go b/pkg/rgrpc/rgrpc.go index c57ce24fbc..5508bcb36a 100644 --- a/pkg/rgrpc/rgrpc.go +++ b/pkg/rgrpc/rgrpc.go @@ -22,19 +22,11 @@ import ( "fmt" "io" "net" - "sort" "github.com/cs3org/reva/cmd/revad/pkg/config" - "github.com/cs3org/reva/internal/grpc/interceptors/appctx" - "github.com/cs3org/reva/internal/grpc/interceptors/log" - "github.com/cs3org/reva/internal/grpc/interceptors/recovery" - "github.com/cs3org/reva/internal/grpc/interceptors/token" - "github.com/cs3org/reva/internal/grpc/interceptors/useragent" - rtrace "github.com/cs3org/reva/pkg/trace" grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware" "github.com/pkg/errors" "github.com/rs/zerolog" - "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc" "google.golang.org/grpc" "google.golang.org/grpc/reflection" ) @@ -80,22 +72,12 @@ type Service interface { UnprotectedEndpoints() []string } -type unaryInterceptorTriple struct { - Name string - Priority int - Interceptor grpc.UnaryServerInterceptor -} - -type streamInterceptorTriple struct { - Name string - Priority int - Interceptor grpc.StreamServerInterceptor -} - // Server is a gRPC server. type Server struct { - ShutdownDeadline int - EnableReflection bool + ShutdownDeadline int + EnableReflection bool + UnaryServerInterceptors []grpc.UnaryServerInterceptor + StreamServerInterceptors []grpc.StreamServerInterceptor s *grpc.Server listener net.Listener @@ -149,27 +131,8 @@ func (s *Server) Start(ln net.Listener) error { return nil } -// func (s *Server) isInterceptorEnabled(name string) bool { -// for k := range s.iunterceptors { -// if k == name { -// return true -// } -// } -// return false -// } - func (s *Server) initServices() error { - - // obtain list of unprotected endpoints - unprotected := []string{} - for _, svc := range s.services { - unprotected = append(unprotected, svc.UnprotectedEndpoints()...) - } - - opts, err := s.getInterceptors(unprotected) - if err != nil { - return err - } + opts := s.getInterceptors() grpcServer := grpc.NewServer(opts...) for _, svc := range s.services { @@ -221,101 +184,12 @@ func (s *Server) Address() string { return s.listener.Addr().String() } -func (s *Server) getInterceptors(unprotected []string) ([]grpc.ServerOption, error) { - unaryTriples := []*unaryInterceptorTriple{} - // for name, newFunc := range UnaryInterceptors { - // if s.isInterceptorEnabled(name) { - // inter, prio, err := newFunc(s.conf.Interceptors[name]) - // if err != nil { - // err = errors.Wrapf(err, "rgrpc: error creating unary interceptor: %s,", name) - // return nil, err - // } - // triple := &unaryInterceptorTriple{ - // Name: name, - // Priority: prio, - // Interceptor: inter, - // } - // unaryTriples = append(unaryTriples, triple) - // } - // } - - // sort unary triples - sort.SliceStable(unaryTriples, func(i, j int) bool { - return unaryTriples[i].Priority < unaryTriples[j].Priority - }) - - // authUnary, err := auth.NewUnary(s.conf.Interceptors["auth"], unprotected) - // if err != nil { - // return nil, errors.Wrap(err, "rgrpc: error creating unary auth interceptor") - // } - - unaryInterceptors := []grpc.UnaryServerInterceptor{} // TODO: add auth unary - for _, t := range unaryTriples { - unaryInterceptors = append(unaryInterceptors, t.Interceptor) - s.log.Info().Msgf("rgrpc: chaining grpc unary interceptor %s with priority %d", t.Name, t.Priority) - } - - unaryInterceptors = append(unaryInterceptors, - otelgrpc.UnaryServerInterceptor( - otelgrpc.WithTracerProvider(rtrace.Provider), - otelgrpc.WithPropagators(rtrace.Propagator)), - ) - - unaryInterceptors = append([]grpc.UnaryServerInterceptor{ - appctx.NewUnary(s.log), - token.NewUnary(), - useragent.NewUnary(), - log.NewUnary(), - recovery.NewUnary(), - }, unaryInterceptors...) - unaryChain := grpc_middleware.ChainUnaryServer(unaryInterceptors...) - - streamTriples := []*streamInterceptorTriple{} - // for name, newFunc := range StreamInterceptors { - // if s.isInterceptorEnabled(name) { - // inter, prio, err := newFunc(s.conf.Interceptors[name]) - // if err != nil { - // err = errors.Wrapf(err, "rgrpc: error creating streaming interceptor: %s,", name) - // return nil, err - // } - // triple := &streamInterceptorTriple{ - // Name: name, - // Priority: prio, - // Interceptor: inter, - // } - // streamTriples = append(streamTriples, triple) - // } - // } - // sort stream triples - sort.SliceStable(streamTriples, func(i, j int) bool { - return streamTriples[i].Priority < streamTriples[j].Priority - }) - - // authStream, err := auth.NewStream(s.conf.Interceptors["auth"], unprotected) - // if err != nil { - // return nil, errors.Wrap(err, "rgrpc: error creating stream auth interceptor") - // } - - streamInterceptors := []grpc.StreamServerInterceptor{} - for _, t := range streamTriples { - streamInterceptors = append(streamInterceptors, t.Interceptor) - s.log.Info().Msgf("rgrpc: chaining grpc streaming interceptor %s with priority %d", t.Name, t.Priority) - } +func (s *Server) getInterceptors() []grpc.ServerOption { + unaryChain := grpc_middleware.ChainUnaryServer(s.UnaryServerInterceptors...) + streamChain := grpc_middleware.ChainStreamServer(s.StreamServerInterceptors...) - streamInterceptors = append([]grpc.StreamServerInterceptor{ - // authStream, - appctx.NewStream(s.log), - token.NewStream(), - useragent.NewStream(), - log.NewStream(), - recovery.NewStream(), - }, streamInterceptors...) - streamChain := grpc_middleware.ChainStreamServer(streamInterceptors...) - - opts := []grpc.ServerOption{ + return []grpc.ServerOption{ grpc.UnaryInterceptor(unaryChain), grpc.StreamInterceptor(streamChain), } - - return opts, nil } From 91310b81e564ea16edc8e0c350a1773e8bf0159e Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Mon, 26 Jun 2023 16:19:58 +0200 Subject: [PATCH 38/76] set middlewares to http services --- cmd/revad/runtime/{server.go => grpc.go} | 84 +++--------------------- cmd/revad/runtime/http.go | 71 ++++++++++++++++++++ cmd/revad/runtime/runtime.go | 69 +++++++++++++++++++ pkg/rhttp/rhttp.go | 77 ++-------------------- 4 files changed, 154 insertions(+), 147 deletions(-) rename cmd/revad/runtime/{server.go => grpc.go} (68%) create mode 100644 cmd/revad/runtime/http.go diff --git a/cmd/revad/runtime/server.go b/cmd/revad/runtime/grpc.go similarity index 68% rename from cmd/revad/runtime/server.go rename to cmd/revad/runtime/grpc.go index 063d0c6e8a..1695bc5dc5 100644 --- a/cmd/revad/runtime/server.go +++ b/cmd/revad/runtime/grpc.go @@ -2,11 +2,8 @@ package runtime import ( "fmt" - "net" "sort" - "github.com/cs3org/reva/cmd/revad/pkg/config" - "github.com/cs3org/reva/cmd/revad/pkg/grace" "github.com/cs3org/reva/internal/grpc/interceptors/appctx" "github.com/cs3org/reva/internal/grpc/interceptors/auth" "github.com/cs3org/reva/internal/grpc/interceptors/log" @@ -14,25 +11,23 @@ import ( "github.com/cs3org/reva/internal/grpc/interceptors/token" "github.com/cs3org/reva/internal/grpc/interceptors/useragent" "github.com/cs3org/reva/pkg/rgrpc" - "github.com/cs3org/reva/pkg/rhttp" - "github.com/cs3org/reva/pkg/rhttp/global" rtrace "github.com/cs3org/reva/pkg/trace" - "github.com/cs3org/reva/pkg/utils/maps" "github.com/pkg/errors" "github.com/rs/zerolog" "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc" "google.golang.org/grpc" ) -type Server struct { - server grace.Server - listener net.Listener - - services map[string]any +type unaryInterceptorTriple struct { + Name string + Priority int + Interceptor grpc.UnaryServerInterceptor } -func (s *Server) Start() error { - return s.server.Start(s.listener) +type streamInterceptorTriple struct { + Name string + Priority int + Interceptor grpc.StreamServerInterceptor } func initGRPCInterceptors(conf map[string]map[string]any, unprotected []string, logger *zerolog.Logger) ([]grpc.UnaryServerInterceptor, []grpc.StreamServerInterceptor, error) { @@ -137,66 +132,3 @@ func grpcUnprotected(s map[string]rgrpc.Service) (unprotected []string) { } return } - -func newServers(grpc map[string]*config.GRPC, http map[string]*config.HTTP, log *zerolog.Logger) ([]*Server, error) { - var servers []*Server - for _, cfg := range grpc { - services, err := rgrpc.InitServices(cfg.Services) - if err != nil { - return nil, err - } - unaryChain, streamChain, err := initGRPCInterceptors(cfg.Interceptors, grpcUnprotected(services), log) - if err != nil { - return nil, err - } - s, err := rgrpc.NewServer( - rgrpc.EnableReflection(cfg.EnableReflection), - rgrpc.WithShutdownDeadline(cfg.ShutdownDeadline), - rgrpc.WithLogger(log.With().Str("pkg", "grpc").Logger()), - rgrpc.WithServices(services), - rgrpc.WithUnaryServerInterceptors(unaryChain), - rgrpc.WithStreamServerInterceptors(streamChain), - ) - if err != nil { - return nil, err - } - server := &Server{ - server: s, - services: maps.MapValues(services, func(s rgrpc.Service) any { return s }), - } - servers = append(servers, server) - } - for _, cfg := range http { - services, err := rhttp.InitServices(cfg.Services) - if err != nil { - return nil, err - } - s, err := rhttp.New( - rhttp.WithServices(services), - rhttp.WithLogger(log.With().Str("pkg", "http").Logger()), - rhttp.WithCertAndKeyFiles(cfg.CertFile, cfg.KeyFile), - // rhttp.WithMiddlewares(cfg.Middlewares), - ) - if err != nil { - return nil, err - } - server := &Server{ - server: s, - services: maps.MapValues(services, func(s global.Service) any { return s }), - } - servers = append(servers, server) - } - return servers, nil -} - -type unaryInterceptorTriple struct { - Name string - Priority int - Interceptor grpc.UnaryServerInterceptor -} - -type streamInterceptorTriple struct { - Name string - Priority int - Interceptor grpc.StreamServerInterceptor -} diff --git a/cmd/revad/runtime/http.go b/cmd/revad/runtime/http.go new file mode 100644 index 0000000000..5860610129 --- /dev/null +++ b/cmd/revad/runtime/http.go @@ -0,0 +1,71 @@ +package runtime + +import ( + "fmt" + "path" + "sort" + + "github.com/cs3org/reva/internal/http/interceptors/appctx" + "github.com/cs3org/reva/internal/http/interceptors/auth" + "github.com/cs3org/reva/internal/http/interceptors/log" + "github.com/cs3org/reva/pkg/rhttp/global" + "github.com/pkg/errors" + "github.com/rs/zerolog" +) + +// middlewareTriple represents a middleware with the +// priority to be chained. +type middlewareTriple struct { + Name string + Priority int + Middleware global.Middleware +} + +func initHTTPMiddlewares(conf map[string]map[string]any, unprotected []string, logger *zerolog.Logger) ([]global.Middleware, error) { + triples := []*middlewareTriple{} + for name, c := range conf { + new, ok := global.NewMiddlewares[name] + if !ok { + return nil, fmt.Errorf("http middleware %s not found", name) + } + m, prio, err := new(c) + if err != nil { + return nil, errors.Wrapf(err, "error creating new middleware: %s,", name) + } + triples = append(triples, &middlewareTriple{ + Name: name, + Priority: prio, + Middleware: m, + }) + logger.Info().Msgf("http middleware enabled: %s", name) + } + + sort.SliceStable(triples, func(i, j int) bool { + return triples[i].Priority > triples[j].Priority + }) + + authMiddle, err := auth.New(conf["auth"], unprotected) + if err != nil { + return nil, errors.Wrap(err, "rhttp: error creating auth middleware") + } + + middlewares := []global.Middleware{ + authMiddle, + log.New(), + appctx.New(*logger), + } + + for _, triple := range triples { + middlewares = append(middlewares, triple.Middleware) + } + return middlewares, nil +} + +func httpUnprotected(s map[string]global.Service) (unprotected []string) { + for _, svc := range s { + for _, url := range svc.Unprotected() { + unprotected = append(unprotected, path.Join("/", svc.Prefix(), url)) + } + } + return +} diff --git a/cmd/revad/runtime/runtime.go b/cmd/revad/runtime/runtime.go index 85409098fb..e128434f49 100644 --- a/cmd/revad/runtime/runtime.go +++ b/cmd/revad/runtime/runtime.go @@ -30,6 +30,9 @@ import ( "github.com/cs3org/reva/cmd/revad/pkg/config" "github.com/cs3org/reva/cmd/revad/pkg/grace" + "github.com/cs3org/reva/pkg/rgrpc" + "github.com/cs3org/reva/pkg/rhttp" + "github.com/cs3org/reva/pkg/rhttp/global" "github.com/cs3org/reva/pkg/sharedconf" "github.com/cs3org/reva/pkg/utils/maps" "github.com/rs/zerolog" @@ -47,6 +50,17 @@ type Reva struct { log *zerolog.Logger } +type Server struct { + server grace.Server + listener net.Listener + + services map[string]any +} + +func (s *Server) Start() error { + return s.server.Start(s.listener) +} + func New(config *config.Config, opt ...Option) (*Reva, error) { opts := newOptions(opt...) @@ -267,3 +281,58 @@ func adjustCPU(cpu string) (int, error) { runtime.GOMAXPROCS(numCPU) return numCPU, nil } + +func newServers(grpc map[string]*config.GRPC, http map[string]*config.HTTP, log *zerolog.Logger) ([]*Server, error) { + var servers []*Server + for _, cfg := range grpc { + services, err := rgrpc.InitServices(cfg.Services) + if err != nil { + return nil, err + } + unaryChain, streamChain, err := initGRPCInterceptors(cfg.Interceptors, grpcUnprotected(services), log) + if err != nil { + return nil, err + } + s, err := rgrpc.NewServer( + rgrpc.EnableReflection(cfg.EnableReflection), + rgrpc.WithShutdownDeadline(cfg.ShutdownDeadline), + rgrpc.WithLogger(log.With().Str("pkg", "grpc").Logger()), + rgrpc.WithServices(services), + rgrpc.WithUnaryServerInterceptors(unaryChain), + rgrpc.WithStreamServerInterceptors(streamChain), + ) + if err != nil { + return nil, err + } + server := &Server{ + server: s, + services: maps.MapValues(services, func(s rgrpc.Service) any { return s }), + } + servers = append(servers, server) + } + for _, cfg := range http { + services, err := rhttp.InitServices(cfg.Services) + if err != nil { + return nil, err + } + middlewares, err := initHTTPMiddlewares(cfg.Middlewares, httpUnprotected(services), log) + if err != nil { + return nil, err + } + s, err := rhttp.New( + rhttp.WithServices(services), + rhttp.WithLogger(log.With().Str("pkg", "http").Logger()), + rhttp.WithCertAndKeyFiles(cfg.CertFile, cfg.KeyFile), + rhttp.WithMiddlewares(middlewares), + ) + if err != nil { + return nil, err + } + server := &Server{ + server: s, + services: maps.MapValues(services, func(s global.Service) any { return s }), + } + servers = append(servers, server) + } + return servers, nil +} diff --git a/pkg/rhttp/rhttp.go b/pkg/rhttp/rhttp.go index a375ce4a07..92984dada9 100644 --- a/pkg/rhttp/rhttp.go +++ b/pkg/rhttp/rhttp.go @@ -24,7 +24,6 @@ import ( "net" "net/http" "path" - "sort" "strings" "time" @@ -44,7 +43,7 @@ func WithServices(services map[string]global.Service) Config { } } -func WithMiddlewares(middlewares []*MiddlewareTriple) Config { +func WithMiddlewares(middlewares []global.Middleware) Config { return func(s *Server) { s.middlewares = middlewares } @@ -91,6 +90,7 @@ func New(c ...Config) (*Server, error) { svcs: map[string]global.Service{}, unprotected: []string{}, handlers: map[string]http.Handler{}, + middlewares: []global.Middleware{}, } for _, cc := range c { cc(s) @@ -110,7 +110,7 @@ type Server struct { svcs map[string]global.Service // map key is svc Prefix unprotected []string handlers map[string]http.Handler - middlewares []*MiddlewareTriple + middlewares []global.Middleware log zerolog.Logger } @@ -128,7 +128,7 @@ func (s *Server) Start(ln net.Listener) error { s.log.Info().Msgf("https server listening at https://%s using cert file '%s' and key file '%s'", s.listener.Addr(), s.CertFile, s.KeyFile) err = s.httpServer.ServeTLS(s.listener, s.CertFile, s.KeyFile) } else { - s.log.Info().Msgf("http server listening at http://%s '%s' '%s'", s.listener.Addr()) + s.log.Info().Msgf("http server listening at http://%s", s.listener.Addr()) err = s.httpServer.Serve(s.listener) } if err == nil || err == http.ErrServerClosed { @@ -175,40 +175,6 @@ func (s *Server) GracefulStop() error { return s.httpServer.Shutdown(context.Background()) } -// MiddlewareTriple represents a middleware with the -// priority to be chained. -type MiddlewareTriple struct { - Name string - Priority int - Middleware global.Middleware -} - -// func (s *Server) registerMiddlewares() error { -// middlewares := []*middlewareTriple{} -// for name, newFunc := range global.NewMiddlewares { -// if s.isMiddlewareEnabled(name) { -// m, prio, err := newFunc(s.conf.Middlewares[name]) -// if err != nil { -// err = errors.Wrapf(err, "error creating new middleware: %s,", name) -// return err -// } -// middlewares = append(middlewares, &middlewareTriple{ -// Name: name, -// Priority: prio, -// Middleware: m, -// }) -// s.log.Info().Msgf("http middleware enabled: %s", name) -// } -// } -// s.middlewares = middlewares -// return nil -// } - -// func (s *Server) isMiddlewareEnabled(name string) bool { -// _, ok := s.conf.Middlewares[name] -// return ok -// } - func (s *Server) registerServices() { for name, svc := range s.Services { // instrument services with opencensus tracing. @@ -305,41 +271,10 @@ func (s *Server) getHandler() (http.Handler, error) { w.WriteHeader(http.StatusNotFound) }) - // sort middlewares by priority. - sort.SliceStable(s.middlewares, func(i, j int) bool { - return s.middlewares[i].Priority > s.middlewares[j].Priority - }) - handler := http.Handler(h) - - for _, triple := range s.middlewares { - s.log.Info().Msgf("chaining http middleware %s with priority %d", triple.Name, triple.Priority) - handler = triple.Middleware(traceHandler(triple.Name, handler)) - } - - for _, v := range s.unprotected { - s.log.Info().Msgf("unprotected URL: %s", v) - } - - for _, triple := range s.middlewares { - handler = triple.Middleware(handler) + for _, m := range s.middlewares { + handler = m(handler) } - // authMiddle, err := auth.New(s.Middlewares["auth"], s.unprotected) - // if err != nil { - // return nil, errors.Wrap(err, "rhttp: error creating auth middleware") - // } - - // // add always the logctx middleware as most priority, this middleware is internal - // // and cannot be configured from the configuration. - // coreMiddlewares := []*middlewareTriple{} - - // coreMiddlewares = append(coreMiddlewares, &middlewareTriple{Middleware: authMiddle, Name: "auth"}) - // coreMiddlewares = append(coreMiddlewares, &middlewareTriple{Middleware: log.New(), Name: "log"}) - // coreMiddlewares = append(coreMiddlewares, &middlewareTriple{Middleware: appctx.New(s.log), Name: "appctx"}) - - // for _, triple := range coreMiddlewares { - // handler = triple.Middleware(traceHandler(triple.Name, handler)) - // } return handler, nil } From e374cb417d94980a08ff9ae89176b806c5343601 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Mon, 26 Jun 2023 16:32:54 +0200 Subject: [PATCH 39/76] removed unused code --- cmd/revad/pkg/config/config.go | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/cmd/revad/pkg/config/config.go b/cmd/revad/pkg/config/config.go index 46a225e5f9..0a4a30e77f 100644 --- a/cmd/revad/pkg/config/config.go +++ b/cmd/revad/pkg/config/config.go @@ -121,18 +121,3 @@ func (c *Config) Dump() map[string]any { func (c *Config) Lookup(key string) (any, error) { return lookupByType(key, reflect.ValueOf(c)) } - -// MultiConfigLookuper implements the Lookuper interface, -// to lookup a key from different configs. -type MultiConfigLookuper struct { - configs []*Config -} - -// NewMultiConfigLookuper creates a new MultiConfigLookuper. -func NewMultiConfigLookuper(c ...*Config) MultiConfigLookuper { - return MultiConfigLookuper{configs: c} -} - -func (m MultiConfigLookuper) Lookup(key string) (any, error) { - return nil, nil -} From f5fdb620e3a2382097bb6737847f243adb6c5681 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Mon, 26 Jun 2023 16:37:13 +0200 Subject: [PATCH 40/76] set label to services --- cmd/revad/pkg/config/common.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/cmd/revad/pkg/config/common.go b/cmd/revad/pkg/config/common.go index 5371fe066d..9e861be403 100644 --- a/cmd/revad/pkg/config/common.go +++ b/cmd/revad/pkg/config/common.go @@ -24,6 +24,7 @@ func (c ServicesConfig) DriversNumber() int { return len(c) } type DriverConfig struct { Config map[string]any `key:",squash"` Address string `key:"address"` + Network string `key:"network"` } func newSvcConfigFromList(l []map[string]any) (ServicesConfig, error) { @@ -116,10 +117,12 @@ type InterceptorFunc func(*Interceptor) // ForEachService iterates to each service/driver calling the function f. func (i iterableImpl) ForEachService(f ServiceFunc) { for name, c := range i.i.services() { - for _, cfg := range c { + for i, cfg := range c { f(&Service{ raw: cfg, Address: cfg.Address, + Network: cfg.Network, + Label: label(name, i, len(c)), Name: name, Config: cfg.Config, }) @@ -127,6 +130,13 @@ func (i iterableImpl) ForEachService(f ServiceFunc) { } } +func label(name string, i, tot int) string { + if tot == 1 { + return name + } + return fmt.Sprintf("%s_%d", name, i) +} + // ForEachInterceptor iterates to each middleware calling the function f. func (i iterableImpl) ForEachInterceptor(f InterceptorFunc) { for name, c := range i.i.interceptors() { From 1e2b7cc313cbc2e56e6b4bd77225cb70ce27a617 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Mon, 26 Jun 2023 16:41:56 +0200 Subject: [PATCH 41/76] add header --- cmd/revad/pkg/config/common.go | 18 ++++++++++++++++++ cmd/revad/pkg/config/config_test.go | 18 ++++++++++++++++++ cmd/revad/pkg/config/dump.go | 18 ++++++++++++++++++ cmd/revad/pkg/config/dump_test.go | 18 ++++++++++++++++++ cmd/revad/pkg/config/grpc.go | 18 ++++++++++++++++++ cmd/revad/pkg/config/http.go | 18 ++++++++++++++++++ cmd/revad/pkg/config/lookup.go | 18 ++++++++++++++++++ cmd/revad/pkg/config/lookup_test.go | 18 ++++++++++++++++++ cmd/revad/pkg/config/parser.go | 18 ++++++++++++++++++ cmd/revad/pkg/config/parser_test.go | 18 ++++++++++++++++++ cmd/revad/pkg/config/serverless.go | 18 ++++++++++++++++++ cmd/revad/pkg/config/templates.go | 18 ++++++++++++++++++ cmd/revad/pkg/config/templates_test.go | 18 ++++++++++++++++++ cmd/revad/runtime/grpc.go | 18 ++++++++++++++++++ cmd/revad/runtime/http.go | 18 ++++++++++++++++++ cmd/revad/runtime/log.go | 18 ++++++++++++++++++ 16 files changed, 288 insertions(+) diff --git a/cmd/revad/pkg/config/common.go b/cmd/revad/pkg/config/common.go index 9e861be403..d90db56ea8 100644 --- a/cmd/revad/pkg/config/common.go +++ b/cmd/revad/pkg/config/common.go @@ -1,3 +1,21 @@ +// Copyright 2018-2023 CERN +// +// 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. +// +// In applying this license, CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + package config import ( diff --git a/cmd/revad/pkg/config/config_test.go b/cmd/revad/pkg/config/config_test.go index 021c1d7ead..dfb550c0f0 100644 --- a/cmd/revad/pkg/config/config_test.go +++ b/cmd/revad/pkg/config/config_test.go @@ -1,3 +1,21 @@ +// Copyright 2018-2023 CERN +// +// 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. +// +// In applying this license, CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + package config import ( diff --git a/cmd/revad/pkg/config/dump.go b/cmd/revad/pkg/config/dump.go index 3811594dc9..4c36d69d5f 100644 --- a/cmd/revad/pkg/config/dump.go +++ b/cmd/revad/pkg/config/dump.go @@ -1,3 +1,21 @@ +// Copyright 2018-2023 CERN +// +// 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. +// +// In applying this license, CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + package config import "reflect" diff --git a/cmd/revad/pkg/config/dump_test.go b/cmd/revad/pkg/config/dump_test.go index 00014ac102..cc214afab8 100644 --- a/cmd/revad/pkg/config/dump_test.go +++ b/cmd/revad/pkg/config/dump_test.go @@ -1,3 +1,21 @@ +// Copyright 2018-2023 CERN +// +// 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. +// +// In applying this license, CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + package config import ( diff --git a/cmd/revad/pkg/config/grpc.go b/cmd/revad/pkg/config/grpc.go index e11f798de6..dd6414df91 100644 --- a/cmd/revad/pkg/config/grpc.go +++ b/cmd/revad/pkg/config/grpc.go @@ -1,3 +1,21 @@ +// Copyright 2018-2023 CERN +// +// 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. +// +// In applying this license, CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + package config import ( diff --git a/cmd/revad/pkg/config/http.go b/cmd/revad/pkg/config/http.go index 26cd3fba22..546552f58b 100644 --- a/cmd/revad/pkg/config/http.go +++ b/cmd/revad/pkg/config/http.go @@ -1,3 +1,21 @@ +// Copyright 2018-2023 CERN +// +// 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. +// +// In applying this license, CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + package config import ( diff --git a/cmd/revad/pkg/config/lookup.go b/cmd/revad/pkg/config/lookup.go index acc9e36a64..426ef16d04 100644 --- a/cmd/revad/pkg/config/lookup.go +++ b/cmd/revad/pkg/config/lookup.go @@ -1,3 +1,21 @@ +// Copyright 2018-2023 CERN +// +// 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. +// +// In applying this license, CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + package config import ( diff --git a/cmd/revad/pkg/config/lookup_test.go b/cmd/revad/pkg/config/lookup_test.go index ea805717ab..e3360b2f30 100644 --- a/cmd/revad/pkg/config/lookup_test.go +++ b/cmd/revad/pkg/config/lookup_test.go @@ -1,3 +1,21 @@ +// Copyright 2018-2023 CERN +// +// 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. +// +// In applying this license, CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + package config import ( diff --git a/cmd/revad/pkg/config/parser.go b/cmd/revad/pkg/config/parser.go index 7476b16216..8d786dfa73 100644 --- a/cmd/revad/pkg/config/parser.go +++ b/cmd/revad/pkg/config/parser.go @@ -1,3 +1,21 @@ +// Copyright 2018-2023 CERN +// +// 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. +// +// In applying this license, CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + package config import ( diff --git a/cmd/revad/pkg/config/parser_test.go b/cmd/revad/pkg/config/parser_test.go index 78678e4469..87ba592aec 100644 --- a/cmd/revad/pkg/config/parser_test.go +++ b/cmd/revad/pkg/config/parser_test.go @@ -1,3 +1,21 @@ +// Copyright 2018-2023 CERN +// +// 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. +// +// In applying this license, CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + package config import ( diff --git a/cmd/revad/pkg/config/serverless.go b/cmd/revad/pkg/config/serverless.go index fd5ab7ce8c..71f3f2483d 100644 --- a/cmd/revad/pkg/config/serverless.go +++ b/cmd/revad/pkg/config/serverless.go @@ -1,3 +1,21 @@ +// Copyright 2018-2023 CERN +// +// 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. +// +// In applying this license, CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + package config import ( diff --git a/cmd/revad/pkg/config/templates.go b/cmd/revad/pkg/config/templates.go index 27551a78bf..49738b74c8 100644 --- a/cmd/revad/pkg/config/templates.go +++ b/cmd/revad/pkg/config/templates.go @@ -1,3 +1,21 @@ +// Copyright 2018-2023 CERN +// +// 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. +// +// In applying this license, CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + package config import ( diff --git a/cmd/revad/pkg/config/templates_test.go b/cmd/revad/pkg/config/templates_test.go index 820f0a2663..97f490eb77 100644 --- a/cmd/revad/pkg/config/templates_test.go +++ b/cmd/revad/pkg/config/templates_test.go @@ -1,3 +1,21 @@ +// Copyright 2018-2023 CERN +// +// 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. +// +// In applying this license, CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + package config import ( diff --git a/cmd/revad/runtime/grpc.go b/cmd/revad/runtime/grpc.go index 1695bc5dc5..9f76efed7c 100644 --- a/cmd/revad/runtime/grpc.go +++ b/cmd/revad/runtime/grpc.go @@ -1,3 +1,21 @@ +// Copyright 2018-2023 CERN +// +// 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. +// +// In applying this license, CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + package runtime import ( diff --git a/cmd/revad/runtime/http.go b/cmd/revad/runtime/http.go index 5860610129..c110d3ddaa 100644 --- a/cmd/revad/runtime/http.go +++ b/cmd/revad/runtime/http.go @@ -1,3 +1,21 @@ +// Copyright 2018-2023 CERN +// +// 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. +// +// In applying this license, CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + package runtime import ( diff --git a/cmd/revad/runtime/log.go b/cmd/revad/runtime/log.go index 8731202669..daa08dfb1e 100644 --- a/cmd/revad/runtime/log.go +++ b/cmd/revad/runtime/log.go @@ -1,3 +1,21 @@ +// Copyright 2018-2023 CERN +// +// 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. +// +// In applying this license, CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + package runtime import ( From e802a5d35e4153b92e68b3dc1d88bf02be583392 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Tue, 27 Jun 2023 10:50:36 +0200 Subject: [PATCH 42/76] add serverless --- cmd/revad/pkg/config/serverless.go | 7 +++- cmd/revad/runtime/runtime.go | 60 ++++++++++++++++++++++----- pkg/rserverless/option.go | 35 ++++++++++++++++ pkg/rserverless/rserverless.go | 65 ++++++++---------------------- 4 files changed, 107 insertions(+), 60 deletions(-) create mode 100644 pkg/rserverless/option.go diff --git a/cmd/revad/pkg/config/serverless.go b/cmd/revad/pkg/config/serverless.go index 71f3f2483d..ea0c574d34 100644 --- a/cmd/revad/pkg/config/serverless.go +++ b/cmd/revad/pkg/config/serverless.go @@ -44,8 +44,11 @@ func (c *Config) parseServerless(raw map[string]any) error { } // ForEach iterates to each service calling the function f. -func (s *Serverless) ForEach(f func(name string, config map[string]any)) { +func (s *Serverless) ForEach(f func(name string, config map[string]any) error) error { for name, cfg := range s.Services { - f(name, cfg) + if err := f(name, cfg); err != nil { + return err + } } + return nil } diff --git a/cmd/revad/runtime/runtime.go b/cmd/revad/runtime/runtime.go index e128434f49..5cc13518e8 100644 --- a/cmd/revad/runtime/runtime.go +++ b/cmd/revad/runtime/runtime.go @@ -33,6 +33,7 @@ import ( "github.com/cs3org/reva/pkg/rgrpc" "github.com/cs3org/reva/pkg/rhttp" "github.com/cs3org/reva/pkg/rhttp/global" + "github.com/cs3org/reva/pkg/rserverless" "github.com/cs3org/reva/pkg/sharedconf" "github.com/cs3org/reva/pkg/utils/maps" "github.com/rs/zerolog" @@ -42,9 +43,10 @@ import ( type Reva struct { config *config.Config - servers []*Server - watcher *grace.Watcher - lns map[string]net.Listener + servers []*Server + serverless *rserverless.Serverless + watcher *grace.Watcher + lns map[string]net.Listener pidfile string log *zerolog.Logger @@ -102,16 +104,52 @@ func New(config *config.Config, opt ...Option) (*Reva, error) { return nil, err } + serverless, err := newServerless(config, log) + if err != nil { + watcher.Exit(1) + return nil, err + } + return &Reva{ - config: config, - servers: servers, - watcher: watcher, - lns: listeners, - pidfile: opts.PidFile, - log: log, + config: config, + servers: servers, + serverless: serverless, + watcher: watcher, + lns: listeners, + pidfile: opts.PidFile, + log: log, }, nil } +func newServerless(config *config.Config, log *zerolog.Logger) (*rserverless.Serverless, error) { + sl := make(map[string]rserverless.Service) + logger := log.With().Str("pkg", "serverless").Logger() + if err := config.Serverless.ForEach(func(name string, config map[string]any) error { + new, ok := rserverless.Services[name] + if !ok { + return fmt.Errorf("serverless service %s does not exist", name) + } + log := logger.With().Str("service", name).Logger() + svc, err := new(config, &log) + if err != nil { + return errors.Wrapf(err, "serverless service %s could not be initialized", name) + } + sl[name] = svc + return nil + }); err != nil { + return nil, err + } + + ss, err := rserverless.New( + rserverless.WithLogger(&logger), + rserverless.WithServices(sl), + ) + if err != nil { + return nil, err + } + return ss, nil +} + func setRandomAddresses(c *config.Config, lns map[string]net.Listener, log *zerolog.Logger) { f := func(s *config.Service) { if s.Address != "" { @@ -198,6 +236,10 @@ func (r *Reva) Start() error { }) } + g.Go(func() error { + return r.serverless.Start() + }) + r.watcher.TrapSignals() return g.Wait() } diff --git a/pkg/rserverless/option.go b/pkg/rserverless/option.go new file mode 100644 index 0000000000..937b1eaa40 --- /dev/null +++ b/pkg/rserverless/option.go @@ -0,0 +1,35 @@ +// Copyright 2018-2023 CERN +// +// 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. +// +// In applying this license, CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +package rserverless + +import "github.com/rs/zerolog" + +type Option func(*Serverless) + +func WithLogger(log *zerolog.Logger) Option { + return func(s *Serverless) { + s.log = log + } +} + +func WithServices(svc map[string]Service) Option { + return func(s *Serverless) { + s.Services = svc + } +} diff --git a/pkg/rserverless/rserverless.go b/pkg/rserverless/rserverless.go index 7af26640c5..b69c389ccc 100644 --- a/pkg/rserverless/rserverless.go +++ b/pkg/rserverless/rserverless.go @@ -20,12 +20,9 @@ package rserverless import ( "context" - "fmt" "sync" "time" - "github.com/mitchellh/mapstructure" - "github.com/pkg/errors" "github.com/rs/zerolog" ) @@ -48,45 +45,37 @@ type NewService func(conf map[string]interface{}, log *zerolog.Logger) (Service, // Serverless contains the serveless collection of services. type Serverless struct { - conf *config - log zerolog.Logger - services map[string]Service -} - -type config struct { - Services map[string]map[string]interface{} `mapstructure:"services"` + log *zerolog.Logger + Services map[string]Service } // New returns a new serverless collection of services. -func New(m interface{}, l zerolog.Logger) (*Serverless, error) { - conf := &config{} - if err := mapstructure.Decode(m, conf); err != nil { - return nil, err - } - +func New(opt ...Option) (*Serverless, error) { + l := zerolog.Nop() n := &Serverless{ - conf: conf, - log: l, - services: map[string]Service{}, + Services: map[string]Service{}, + log: &l, + } + for _, o := range opt { + o(n) } return n, nil } -func (s *Serverless) isServiceEnabled(svcName string) bool { - _, ok := Services[svcName] - return ok -} - // Start starts the serverless service collection. func (s *Serverless) Start() error { - return s.registerAndStartServices() + for name, svc := range s.Services { + go svc.Start() + s.log.Info().Msgf("serverless service enabled: %s", name) + } + return nil } // GracefulStop gracefully stops the serverless services. func (s *Serverless) GracefulStop() error { var wg sync.WaitGroup - for svcName, svc := range s.services { + for svcName, svc := range s.Services { wg.Add(1) go func(svcName string, svc Service) { @@ -113,7 +102,7 @@ func (s *Serverless) GracefulStop() error { func (s *Serverless) Stop() error { var wg sync.WaitGroup - for svcName, svc := range s.services { + for svcName, svc := range s.Services { wg.Add(1) go func(svcName string, svc Service) { @@ -137,25 +126,3 @@ func (s *Serverless) Stop() error { return nil } -func (s *Serverless) registerAndStartServices() error { - for svcName := range s.conf.Services { - if s.isServiceEnabled(svcName) { - newFunc := Services[svcName] - svcLogger := s.log.With().Str("service", svcName).Logger() - svc, err := newFunc(s.conf.Services[svcName], &svcLogger) - if err != nil { - return errors.Wrapf(err, "serverless service %s could not be initialized", svcName) - } - - go svc.Start() - - s.services[svcName] = svc - - s.log.Info().Msgf("serverless service enabled: %s", svcName) - } else { - return fmt.Errorf("serverless service %s does not exist", svcName) - } - } - - return nil -} From c74d8e4ba92777ba646be058869dcac6a56f8c5b Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Tue, 27 Jun 2023 11:04:21 +0200 Subject: [PATCH 43/76] Fix linter --- cmd/revad/main.go | 5 ++++- cmd/revad/pkg/config/common.go | 2 +- cmd/revad/pkg/config/dump.go | 7 ++++--- cmd/revad/pkg/config/lookup.go | 7 ++++--- cmd/revad/pkg/config/lookup_test.go | 1 - cmd/revad/pkg/config/parser.go | 2 +- cmd/revad/pkg/config/templates_test.go | 1 - cmd/revad/pkg/grace/grace.go | 2 +- cmd/revad/runtime/grpc.go | 1 - cmd/revad/runtime/runtime.go | 2 +- pkg/rgrpc/option.go | 20 +++++++++++++++++++- pkg/rserverless/rserverless.go | 1 - pkg/utils/maps/maps.go | 22 ++++++++++++++++++++++ 13 files changed, 57 insertions(+), 16 deletions(-) diff --git a/cmd/revad/main.go b/cmd/revad/main.go index 3465e22d79..566de3e973 100644 --- a/cmd/revad/main.go +++ b/cmd/revad/main.go @@ -228,7 +228,10 @@ func runSingle(conf *config.Config, pidfile string) { os.Exit(1) } registerReva(reva) - reva.Start() + if err := reva.Start(); err != nil { + fmt.Fprint(os.Stderr, err.Error()) + os.Exit(1) + } } func runMultiple(confs []*config.Config) { diff --git a/cmd/revad/pkg/config/common.go b/cmd/revad/pkg/config/common.go index d90db56ea8..9444bddddc 100644 --- a/cmd/revad/pkg/config/common.go +++ b/cmd/revad/pkg/config/common.go @@ -46,7 +46,7 @@ type DriverConfig struct { } func newSvcConfigFromList(l []map[string]any) (ServicesConfig, error) { - var cfg ServicesConfig + cfg := make(ServicesConfig, 0, len(l)) for _, c := range l { cfg = append(cfg, &DriverConfig{Config: c}) } diff --git a/cmd/revad/pkg/config/dump.go b/cmd/revad/pkg/config/dump.go index 4c36d69d5f..a87896bb71 100644 --- a/cmd/revad/pkg/config/dump.go +++ b/cmd/revad/pkg/config/dump.go @@ -43,11 +43,12 @@ func dumpStruct(v reflect.Value) map[string]any { } var mm map[string]any - if e.Kind() == reflect.Struct { + switch e.Kind() { + case reflect.Struct: mm = dumpStruct(e) - } else if e.Kind() == reflect.Map { + case reflect.Map: mm = dumpMap(e) - } else { + default: panic("squash not allowed on non map/struct types") } for k, v := range mm { diff --git a/cmd/revad/pkg/config/lookup.go b/cmd/revad/pkg/config/lookup.go index 426ef16d04..29af8e477d 100644 --- a/cmd/revad/pkg/config/lookup.go +++ b/cmd/revad/pkg/config/lookup.go @@ -70,11 +70,12 @@ func lookupStruct(key string, v reflect.Value) (any, error) { v any err error ) - if val.Kind() == reflect.Struct { + switch val.Kind() { + case reflect.Struct: v, err = lookupStruct(key, val) - } else if val.Kind() == reflect.Map { + case reflect.Map: v, err = lookupMap(key, val) - } else { + default: panic("squash not allowed on non map/struct types") } if errors.Is(err, ErrKeyNotFound) { diff --git a/cmd/revad/pkg/config/lookup_test.go b/cmd/revad/pkg/config/lookup_test.go index e3360b2f30..c831f183eb 100644 --- a/cmd/revad/pkg/config/lookup_test.go +++ b/cmd/revad/pkg/config/lookup_test.go @@ -184,5 +184,4 @@ func TestLookupStruct(t *testing.T) { assert.Equal(t, tt.val, got) } } - } diff --git a/cmd/revad/pkg/config/parser.go b/cmd/revad/pkg/config/parser.go index 8d786dfa73..25976b804b 100644 --- a/cmd/revad/pkg/config/parser.go +++ b/cmd/revad/pkg/config/parser.go @@ -58,7 +58,7 @@ func parseNext(key string) (Command, string, error) { tkn, next := split(key) index, err := strconv.ParseInt(tkn, 10, 64) if err != nil { - return nil, "", errors.Wrap(err, "parsing error:") + return nil, "", errors.Wrap(err, "parsing error") } return FieldByIndex{Index: int(index)}, next, nil } diff --git a/cmd/revad/pkg/config/templates_test.go b/cmd/revad/pkg/config/templates_test.go index 97f490eb77..a4b0b34ba7 100644 --- a/cmd/revad/pkg/config/templates_test.go +++ b/cmd/revad/pkg/config/templates_test.go @@ -85,5 +85,4 @@ func TestApplyTemplate(t *testing.T) { "db_password": "secretpassword", "key": "value", }) - } diff --git a/cmd/revad/pkg/grace/grace.go b/cmd/revad/pkg/grace/grace.go index 1d793bf35d..e46608301f 100644 --- a/cmd/revad/pkg/grace/grace.go +++ b/cmd/revad/pkg/grace/grace.go @@ -188,7 +188,7 @@ func newListener(network, addr string) (net.Listener, error) { return net.Listen(network, addr) } -// implements the net.Listener interface +// implements the net.Listener interface. type inherited struct { f *os.File ln net.Listener diff --git a/cmd/revad/runtime/grpc.go b/cmd/revad/runtime/grpc.go index 9f76efed7c..8ff7c10158 100644 --- a/cmd/revad/runtime/grpc.go +++ b/cmd/revad/runtime/grpc.go @@ -114,7 +114,6 @@ func initGRPCInterceptors(conf map[string]map[string]any, unprotected []string, } streamTriples = append(streamTriples, triple) } - } // sort stream triples sort.SliceStable(streamTriples, func(i, j int) bool { diff --git a/cmd/revad/runtime/runtime.go b/cmd/revad/runtime/runtime.go index 5cc13518e8..231be2507f 100644 --- a/cmd/revad/runtime/runtime.go +++ b/cmd/revad/runtime/runtime.go @@ -325,7 +325,7 @@ func adjustCPU(cpu string) (int, error) { } func newServers(grpc map[string]*config.GRPC, http map[string]*config.HTTP, log *zerolog.Logger) ([]*Server, error) { - var servers []*Server + servers := make([]*Server, 0, len(grpc)+len(http)) for _, cfg := range grpc { services, err := rgrpc.InitServices(cfg.Services) if err != nil { diff --git a/pkg/rgrpc/option.go b/pkg/rgrpc/option.go index 7a7c553b76..b59492dda0 100644 --- a/pkg/rgrpc/option.go +++ b/pkg/rgrpc/option.go @@ -1,3 +1,21 @@ +// Copyright 2018-2023 CERN +// +// 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. +// +// In applying this license, CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + package rgrpc import ( @@ -41,4 +59,4 @@ func WithUnaryServerInterceptors(in []grpc.UnaryServerInterceptor) Option { return func(s *Server) { s.UnaryServerInterceptors = in } -} \ No newline at end of file +} diff --git a/pkg/rserverless/rserverless.go b/pkg/rserverless/rserverless.go index b69c389ccc..c68f1d6142 100644 --- a/pkg/rserverless/rserverless.go +++ b/pkg/rserverless/rserverless.go @@ -125,4 +125,3 @@ func (s *Serverless) Stop() error { return nil } - diff --git a/pkg/utils/maps/maps.go b/pkg/utils/maps/maps.go index 7d4fbd9890..7567be8236 100644 --- a/pkg/utils/maps/maps.go +++ b/pkg/utils/maps/maps.go @@ -1,5 +1,26 @@ +// Copyright 2018-2023 CERN +// +// 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. +// +// In applying this license, CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + package maps +// Merge returns a map containing the keys and values from both maps. +// If the two maps share a set of keys, the result map will contain +// only the value of the second map. func Merge[K comparable, T any](m, n map[K]T) map[K]T { r := make(map[K]T, len(m)+len(n)) for k, v := range m { @@ -11,6 +32,7 @@ func Merge[K comparable, T any](m, n map[K]T) map[K]T { return r } +// MapValues returns a map with vales mapped using the function f. func MapValues[K comparable, T, V any](m map[K]T, f func(T) V) map[K]V { r := make(map[K]V, len(m)) for k, v := range m { From d3c487ef9ffd6ce5cb0afedce1f7b885d8592e68 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Tue, 27 Jun 2023 11:28:08 +0200 Subject: [PATCH 44/76] fix unit tests --- cmd/revad/pkg/config/config_test.go | 6 ++++++ pkg/sharedconf/sharedconf.go | 32 +---------------------------- 2 files changed, 7 insertions(+), 31 deletions(-) diff --git a/cmd/revad/pkg/config/config_test.go b/cmd/revad/pkg/config/config_test.go index dfb550c0f0..91a11de5b7 100644 --- a/cmd/revad/pkg/config/config_test.go +++ b/cmd/revad/pkg/config/config_test.go @@ -486,12 +486,14 @@ func TestDump(t *testing.T) { "gateway": []any{ map[string]any{ "address": "", + "network": "", "authregistrysvc": "localhost:19000", }, }, "authregistry": []any{ map[string]any{ "address": "localhost:19000", + "network": "", "driver": "static", "drivers": map[string]any{ "static": map[string]any{ @@ -506,6 +508,7 @@ func TestDump(t *testing.T) { "authprovider": []any{ map[string]any{ "address": "localhost:19001", + "network": "", "driver": "ldap", "drivers": map[string]any{ "ldap": map[string]any{ @@ -515,6 +518,7 @@ func TestDump(t *testing.T) { }, map[string]any{ "address": "localhost:19002", + "network": "", "driver": "machine", "drivers": map[string]any{ "machine": map[string]any{ @@ -535,12 +539,14 @@ func TestDump(t *testing.T) { "dataprovider": []any{ map[string]any{ "address": "localhost:19003", + "network": "", "driver": "localhome", }, }, "sysinfo": []any{ map[string]any{ "address": "localhost:19003", + "network": "", }, }, }, diff --git a/pkg/sharedconf/sharedconf.go b/pkg/sharedconf/sharedconf.go index d33bf1479f..c40aa6c0c0 100644 --- a/pkg/sharedconf/sharedconf.go +++ b/pkg/sharedconf/sharedconf.go @@ -24,7 +24,7 @@ import ( "github.com/cs3org/reva/cmd/revad/pkg/config" ) -var sharedConf *config.Shared +var sharedConf *config.Shared = &config.Shared{} var once sync.Once func Init(c *config.Shared) { @@ -33,36 +33,6 @@ func Init(c *config.Shared) { }) } -// // Decode decodes the configuration. -// func Decode(v interface{}) error { -// if err := mapstructure.Decode(v, sharedConf); err != nil { -// return err -// } - -// // add some defaults -// if sharedConf.GatewaySVC == "" { -// sharedConf.GatewaySVC = "0.0.0.0:19000" -// } - -// // this is the default address we use for the data gateway HTTP service -// if sharedConf.DataGateway == "" { -// host, err := os.Hostname() -// if err != nil || host == "" { -// sharedConf.DataGateway = "http://0.0.0.0:19001/datagateway" -// } else { -// sharedConf.DataGateway = fmt.Sprintf("http://%s:19001/datagateway", host) -// } -// } - -// // TODO(labkode): would be cool to autogenerate one secret and print -// // it on init time. -// if sharedConf.JWTSecret == "" { -// sharedConf.JWTSecret = "changemeplease" -// } - -// return nil -// } - // GetJWTSecret returns the package level configured jwt secret if not overwritten. func GetJWTSecret(val string) string { if val == "" { From 003feab2a35738e7dc344c131f37b8a7afb06bcb Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Tue, 27 Jun 2023 12:16:58 +0200 Subject: [PATCH 45/76] add defaults --- cmd/revad/pkg/config/config.go | 32 ++++++++++++++++++-------------- cmd/revad/pkg/config/grpc.go | 4 ++-- cmd/revad/pkg/config/http.go | 6 +++--- go.mod | 1 + go.sum | 2 ++ 5 files changed, 26 insertions(+), 19 deletions(-) diff --git a/cmd/revad/pkg/config/config.go b/cmd/revad/pkg/config/config.go index 0a4a30e77f..7cac11536d 100644 --- a/cmd/revad/pkg/config/config.go +++ b/cmd/revad/pkg/config/config.go @@ -24,35 +24,36 @@ import ( "reflect" "github.com/BurntSushi/toml" + "github.com/creasty/defaults" "github.com/mitchellh/mapstructure" "github.com/pkg/errors" ) // Config holds the reva configuration. type Config struct { - GRPC *GRPC `key:"grpc" mapstructure:"-"` - HTTP *HTTP `key:"http" mapstructure:"-"` - Serverless *Serverless `key:"serverless" mapstructure:"-"` - Shared *Shared `key:"shared" mapstructure:"shared" template:"-"` - Log *Log `key:"log" mapstructure:"log" template:"-"` - Core *Core `key:"core" mapstructure:"core" template:"-"` - Vars Vars `key:"vars" mapstructure:"vars" template:"-"` + GRPC *GRPC `key:"grpc" mapstructure:"-" default:"{}"` + HTTP *HTTP `key:"http" mapstructure:"-" default:"{}"` + Serverless *Serverless `key:"serverless" mapstructure:"-" default:"{}"` + Shared *Shared `key:"shared" mapstructure:"shared" default:"{}" template:"-"` + Log *Log `key:"log" mapstructure:"log" default:"{}" template:"-"` + Core *Core `key:"core" mapstructure:"core" default:"{}" template:"-"` + Vars Vars `key:"vars" mapstructure:"vars" default:"{}" template:"-"` } // Log holds the configuration for the logger. type Log struct { - Output string `key:"output" mapstructure:"output"` - Mode string `key:"mode" mapstructure:"mode"` - Level string `key:"level" mapstructure:"level"` + Output string `key:"output" mapstructure:"output" default:"stdout"` + Mode string `key:"mode" mapstructure:"mode" default:"console"` + Level string `key:"level" mapstructure:"level" default:"info"` } // Shared holds the shared configuration. type Shared struct { - JWTSecret string `key:"jwt_secret" mapstructure:"jwt_secret"` - GatewaySVC string `key:"gatewaysvc" mapstructure:"gatewaysvc"` - DataGateway string `key:"datagateway" mapstructure:"datagateway"` + JWTSecret string `key:"jwt_secret" mapstructure:"jwt_secret" default:"changemeplease"` + GatewaySVC string `key:"gatewaysvc" mapstructure:"gatewaysvc" default:"0.0.0.0:19000"` + DataGateway string `key:"datagateway" mapstructure:"datagateway" default:"http://0.0.0.0:19001/datagateway"` SkipUserGroupsInToken bool `key:"skip_user_groups_in_token" mapstructure:"skip_user_groups_in_token"` - BlockedUsers []string `key:"blocked_users" mapstructure:"blocked_users"` + BlockedUsers []string `key:"blocked_users" mapstructure:"blocked_users" default:"[]"` } // Core holds the core configuration. @@ -76,6 +77,9 @@ type Lookuper interface { // Load loads the configuration from the reader. func Load(r io.Reader) (*Config, error) { var c Config + if err := defaults.Set(&c); err != nil { + return nil, err + } var raw map[string]any if _, err := toml.NewDecoder(r).Decode(&raw); err != nil { return nil, errors.Wrap(err, "config: error decoding toml data") diff --git a/cmd/revad/pkg/config/grpc.go b/cmd/revad/pkg/config/grpc.go index dd6414df91..85786b8288 100644 --- a/cmd/revad/pkg/config/grpc.go +++ b/cmd/revad/pkg/config/grpc.go @@ -25,8 +25,8 @@ import ( // GRPC holds the configuration for the GRPC services. type GRPC struct { - Address string `mapstructure:"address" key:"address"` - Network string `mapstructure:"network" key:"network"` + Address string `mapstructure:"address" key:"address" default:"0.0.0.0:19000"` + Network string `mapstructure:"network" key:"network" default:"tcp"` ShutdownDeadline int `mapstructure:"shutdown_deadline" key:"shutdown_deadline"` EnableReflection bool `mapstructure:"enable_reflection" key:"enable_reflection"` diff --git a/cmd/revad/pkg/config/http.go b/cmd/revad/pkg/config/http.go index 546552f58b..f265bc2c41 100644 --- a/cmd/revad/pkg/config/http.go +++ b/cmd/revad/pkg/config/http.go @@ -25,10 +25,10 @@ import ( // HTTP holds the configuration for the HTTP services. type HTTP struct { - Network string `mapstructure:"network" key:"network"` - Address string `mapstructure:"address" key:"address"` + Network string `mapstructure:"network" key:"network" default:"tcp"` + Address string `mapstructure:"address" key:"address" default:"0.0.0.0:19001"` CertFile string `mapstructure:"certfile" key:"certfile"` - KeyFile string `mapstructure:"keyfile" key:"keyfile"` + KeyFile string `mapstructure:"keyfile" key:"keyfile"` Services map[string]ServicesConfig `mapstructure:"-" key:"services"` Middlewares map[string]map[string]any `mapstructure:"-" key:"middlewares"` diff --git a/go.mod b/go.mod index e9533f42ce..59e7e61716 100644 --- a/go.mod +++ b/go.mod @@ -77,6 +77,7 @@ require ( ) require ( + github.com/creasty/defaults v1.7.0 // indirect github.com/go-jose/go-jose/v3 v3.0.0 // indirect github.com/hashicorp/go-msgpack/v2 v2.1.0 // indirect ) diff --git a/go.sum b/go.sum index bac70c99ab..5fa1a06c16 100644 --- a/go.sum +++ b/go.sum @@ -304,6 +304,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsr github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creasty/defaults v1.7.0 h1:eNdqZvc5B509z18lD8yc212CAqJNvfT1Jq6L8WowdBA= +github.com/creasty/defaults v1.7.0/go.mod h1:iGzKe6pbEHnpMPtfDXZEr0NVxWnPTjb1bbDy08fPzYM= github.com/cs3org/cato v0.0.0-20200828125504-e418fc54dd5e h1:tqSPWQeueWTKnJVMJffz4pz0o1WuQxJ28+5x5JgaHD8= github.com/cs3org/cato v0.0.0-20200828125504-e418fc54dd5e/go.mod h1:XJEZ3/EQuI3BXTp/6DUzFr850vlxq11I6satRtz0YQ4= github.com/cs3org/go-cs3apis v0.0.0-20230508132523-e0d062e63b3b h1:UCO7Rnf5bvIvRtETguV8IaTx73cImLlFWxrApCB0QsQ= From 84846c2722318bd09c3ae92947a3184d023eaa29 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Tue, 27 Jun 2023 13:50:21 +0200 Subject: [PATCH 46/76] log errors --- cmd/revad/main.go | 68 ++++++++++++++++++++++++++--- cmd/revad/pkg/config/common.go | 3 ++ cmd/revad/pkg/config/config.go | 2 +- cmd/revad/pkg/grace/grace.go | 7 ++- cmd/revad/runtime/log.go | 79 ---------------------------------- cmd/revad/runtime/option.go | 5 ++- cmd/revad/runtime/runtime.go | 18 ++++---- 7 files changed, 86 insertions(+), 96 deletions(-) delete mode 100644 cmd/revad/runtime/log.go diff --git a/cmd/revad/main.go b/cmd/revad/main.go index 566de3e973..c8b1e7a551 100644 --- a/cmd/revad/main.go +++ b/cmd/revad/main.go @@ -21,6 +21,7 @@ package main import ( "flag" "fmt" + "io" "io/fs" "os" "path" @@ -31,8 +32,11 @@ import ( "github.com/cs3org/reva/cmd/revad/pkg/config" "github.com/cs3org/reva/cmd/revad/pkg/grace" "github.com/cs3org/reva/cmd/revad/runtime" + "github.com/cs3org/reva/pkg/logger" "github.com/cs3org/reva/pkg/sysinfo" "github.com/google/uuid" + "github.com/pkg/errors" + "github.com/rs/zerolog" ) var ( @@ -222,18 +226,24 @@ func registerReva(r *runtime.Reva) { } func runSingle(conf *config.Config, pidfile string) { - reva, err := runtime.New(conf, runtime.WithPidFile(pidfile)) + log := initLogger(conf.Log) + reva, err := runtime.New(conf, + runtime.WithPidFile(pidfile), + runtime.WithLogger(log), + ) if err != nil { - fmt.Fprint(os.Stderr, err.Error()) - os.Exit(1) + abort(log, "error creating reva runtime: %v", err) } registerReva(reva) if err := reva.Start(); err != nil { - fmt.Fprint(os.Stderr, err.Error()) - os.Exit(1) + abort(log, "error starting reva: %v", err) } } +func abort(log *zerolog.Logger, format string, a ...any) { + log.Fatal().Msgf(format, a...) +} + func runMultiple(confs []*config.Config) { var wg sync.WaitGroup pidfile := getPidfile() @@ -255,3 +265,51 @@ func getPidfile() string { return path.Join(os.TempDir(), name) } + +func initLogger(conf *config.Log) *zerolog.Logger { + log, err := newLogger(conf) + if err != nil { + fmt.Fprintf(os.Stderr, "error creating logger: %v", err) + os.Exit(1) + } + return log +} + +func newLogger(conf *config.Log) (*zerolog.Logger, error) { + // TODO(labkode): use debug level rather than info as default until reaching a stable version. + // Helps having smaller development files. + if conf.Level == "" { + conf.Level = zerolog.DebugLevel.String() + } + + var opts []logger.Option + opts = append(opts, logger.WithLevel(conf.Level)) + + w, err := getWriter(conf.Output) + if err != nil { + return nil, err + } + + opts = append(opts, logger.WithWriter(w, logger.Mode(conf.Mode))) + + l := logger.New(opts...) + sub := l.With().Int("pid", os.Getpid()).Logger() + return &sub, nil +} + +func getWriter(out string) (io.Writer, error) { + if out == "stderr" || out == "" { + return os.Stderr, nil + } + + if out == "stdout" { + return os.Stdout, nil + } + + fd, err := os.OpenFile(out, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + return nil, errors.Wrap(err, "error creating log file: "+out) + } + + return fd, nil +} diff --git a/cmd/revad/pkg/config/common.go b/cmd/revad/pkg/config/common.go index 9444bddddc..58e6a7100f 100644 --- a/cmd/revad/pkg/config/common.go +++ b/cmd/revad/pkg/config/common.go @@ -134,6 +134,9 @@ type InterceptorFunc func(*Interceptor) // ForEachService iterates to each service/driver calling the function f. func (i iterableImpl) ForEachService(f ServiceFunc) { + if i.i == nil { + return + } for name, c := range i.i.services() { for i, cfg := range c { f(&Service{ diff --git a/cmd/revad/pkg/config/config.go b/cmd/revad/pkg/config/config.go index 7cac11536d..43f4adaaad 100644 --- a/cmd/revad/pkg/config/config.go +++ b/cmd/revad/pkg/config/config.go @@ -44,7 +44,7 @@ type Config struct { type Log struct { Output string `key:"output" mapstructure:"output" default:"stdout"` Mode string `key:"mode" mapstructure:"mode" default:"console"` - Level string `key:"level" mapstructure:"level" default:"info"` + Level string `key:"level" mapstructure:"level" default:"trace"` } // Shared holds the shared configuration. diff --git a/cmd/revad/pkg/grace/grace.go b/cmd/revad/pkg/grace/grace.go index e46608301f..d650423655 100644 --- a/cmd/revad/pkg/grace/grace.go +++ b/cmd/revad/pkg/grace/grace.go @@ -84,13 +84,18 @@ func NewWatcher(opts ...Option) *Watcher { // Exit exits the current process cleaning up // existing pid files. func (w *Watcher) Exit(errc int) { + w.Clean() + os.Exit(errc) +} + +// Clean cleans up existing pid files. +func (w *Watcher) Clean() { err := w.clean() if err != nil { w.log.Warn().Err(err).Msg("error removing pid file") } else { w.log.Info().Msgf("pid file %q got removed", w.pidFile) } - os.Exit(errc) } func (w *Watcher) clean() error { diff --git a/cmd/revad/runtime/log.go b/cmd/revad/runtime/log.go deleted file mode 100644 index daa08dfb1e..0000000000 --- a/cmd/revad/runtime/log.go +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2018-2023 CERN -// -// 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. -// -// In applying this license, CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -package runtime - -import ( - "io" - "os" - - "github.com/cs3org/reva/cmd/revad/pkg/config" - "github.com/cs3org/reva/pkg/logger" - "github.com/pkg/errors" - "github.com/rs/zerolog" -) - -func initLogger(conf *config.Log, opts Options) *zerolog.Logger { - if opts.Logger != nil { - return opts.Logger - } - log, err := newLogger(conf) - if err != nil { - abort("error creating logger: %v", err) - } - return log -} - -func newLogger(conf *config.Log) (*zerolog.Logger, error) { - // TODO(labkode): use debug level rather than info as default until reaching a stable version. - // Helps having smaller development files. - if conf.Level == "" { - conf.Level = zerolog.DebugLevel.String() - } - - var opts []logger.Option - opts = append(opts, logger.WithLevel(conf.Level)) - - w, err := getWriter(conf.Output) - if err != nil { - return nil, err - } - - opts = append(opts, logger.WithWriter(w, logger.Mode(conf.Mode))) - - l := logger.New(opts...) - sub := l.With().Int("pid", os.Getpid()).Logger() - return &sub, nil -} - -func getWriter(out string) (io.Writer, error) { - if out == "stderr" || out == "" { - return os.Stderr, nil - } - - if out == "stdout" { - return os.Stdout, nil - } - - fd, err := os.OpenFile(out, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) - if err != nil { - return nil, errors.Wrap(err, "error creating log file: "+out) - } - - return fd, nil -} diff --git a/cmd/revad/runtime/option.go b/cmd/revad/runtime/option.go index 9c500b2e1e..dd49966424 100644 --- a/cmd/revad/runtime/option.go +++ b/cmd/revad/runtime/option.go @@ -35,7 +35,10 @@ type Options struct { // newOptions initializes the available default options. func newOptions(opts ...Option) Options { - opt := Options{} + l := zerolog.Nop() + opt := Options{ + Logger: &l, + } for _, o := range opts { o(&opt) diff --git a/cmd/revad/runtime/runtime.go b/cmd/revad/runtime/runtime.go index 231be2507f..25bd29dfe6 100644 --- a/cmd/revad/runtime/runtime.go +++ b/cmd/revad/runtime/runtime.go @@ -65,8 +65,8 @@ func (s *Server) Start() error { func New(config *config.Config, opt ...Option) (*Reva, error) { opts := newOptions(opt...) + log := opts.Logger - log := initLogger(config.Log, opts) initSharedConf(config) if err := initCPUCount(config.Core, log); err != nil { @@ -87,26 +87,26 @@ func New(config *config.Config, opt ...Option) (*Reva, error) { listeners, err := watcher.GetListeners(maps.Merge(addrGRPC, addrHTTP)) if err != nil { - watcher.Exit(1) + watcher.Clean() return nil, err } setRandomAddresses(config, listeners, log) if err := applyTemplates(config); err != nil { - watcher.Exit(1) + watcher.Clean() return nil, err } servers, err := newServers(grpc, http, log) if err != nil { - watcher.Exit(1) + watcher.Clean() return nil, err } serverless, err := newServerless(config, log) if err != nil { - watcher.Exit(1) + watcher.Clean() return nil, err } @@ -272,10 +272,10 @@ func abort(msg string, args ...any) { } func handlePIDFlag(l *zerolog.Logger, pidFile string) (*grace.Watcher, error) { - var opts []grace.Option - opts = append(opts, grace.WithPIDFile(pidFile)) - opts = append(opts, grace.WithLogger(l.With().Str("pkg", "grace").Logger())) - w := grace.NewWatcher(opts...) + w := grace.NewWatcher( + grace.WithPIDFile(pidFile), + grace.WithLogger(l.With().Str("pkg", "grace").Logger()), + ) err := w.WritePID() if err != nil { return nil, err From 28958b180161272e6a482d277aa49ac0cb9dcf3a Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Tue, 27 Jun 2023 14:09:12 +0200 Subject: [PATCH 47/76] fix default config for http and grpc --- cmd/revad/pkg/config/grpc.go | 16 +++++++--------- cmd/revad/pkg/config/http.go | 16 +++++++--------- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/cmd/revad/pkg/config/grpc.go b/cmd/revad/pkg/config/grpc.go index 85786b8288..30a25b6404 100644 --- a/cmd/revad/pkg/config/grpc.go +++ b/cmd/revad/pkg/config/grpc.go @@ -44,8 +44,7 @@ func (c *Config) parseGRPC(raw map[string]any) error { if !ok { return nil } - var grpc GRPC - if err := mapstructure.Decode(cfg, &grpc); err != nil { + if err := mapstructure.Decode(cfg, c.GRPC); err != nil { return errors.Wrap(err, "config: error decoding grpc config") } @@ -64,14 +63,13 @@ func (c *Config) parseGRPC(raw map[string]any) error { return err } - grpc.Services = services - grpc.Interceptors = interceptors - grpc.iterableImpl = iterableImpl{&grpc} - c.GRPC = &grpc + c.GRPC.Services = services + c.GRPC.Interceptors = interceptors + c.GRPC.iterableImpl = iterableImpl{c.GRPC} - for _, c := range grpc.Services { - for _, cfg := range c { - cfg.Address = addressForService(grpc.Address, cfg.Config) + for _, svc := range c.GRPC.Services { + for _, cfg := range svc { + cfg.Address = addressForService(c.GRPC.Address, cfg.Config) } } return nil diff --git a/cmd/revad/pkg/config/http.go b/cmd/revad/pkg/config/http.go index f265bc2c41..ffb97fadc7 100644 --- a/cmd/revad/pkg/config/http.go +++ b/cmd/revad/pkg/config/http.go @@ -44,8 +44,7 @@ func (c *Config) parseHTTP(raw map[string]any) error { if !ok { return nil } - var http HTTP - if err := mapstructure.Decode(cfg, &http); err != nil { + if err := mapstructure.Decode(cfg, c.HTTP); err != nil { return errors.Wrap(err, "config: error decoding http config") } @@ -64,14 +63,13 @@ func (c *Config) parseHTTP(raw map[string]any) error { return err } - http.Services = services - http.Middlewares = middlewares - http.iterableImpl = iterableImpl{&http} - c.HTTP = &http + c.HTTP.Services = services + c.HTTP.Middlewares = middlewares + c.HTTP.iterableImpl = iterableImpl{c.HTTP} - for _, c := range http.Services { - for _, cfg := range c { - cfg.Address = addressForService(http.Address, cfg.Config) + for _, svc := range c.HTTP.Services { + for _, cfg := range svc { + cfg.Address = addressForService(c.HTTP.Address, cfg.Config) } } return nil From f83e10ee4074b0a98f7d76a640f4c916764e42d5 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Tue, 27 Jun 2023 19:02:58 +0200 Subject: [PATCH 48/76] bug fixes --- cmd/revad/pkg/config/common.go | 7 ++++++ cmd/revad/pkg/config/grpc.go | 3 ++- cmd/revad/pkg/config/http.go | 3 ++- cmd/revad/pkg/grace/grace.go | 21 ++++++++++++++--- cmd/revad/runtime/runtime.go | 42 ++++++++++++++++++++++------------ 5 files changed, 57 insertions(+), 19 deletions(-) diff --git a/cmd/revad/pkg/config/common.go b/cmd/revad/pkg/config/common.go index 58e6a7100f..15d3e151c7 100644 --- a/cmd/revad/pkg/config/common.go +++ b/cmd/revad/pkg/config/common.go @@ -174,3 +174,10 @@ func addressForService(global string, cfg map[string]any) string { } return global } + +func networkForService(global string, cfg map[string]any) string { + if network, ok := cfg["network"].(string); ok { + return network + } + return global +} diff --git a/cmd/revad/pkg/config/grpc.go b/cmd/revad/pkg/config/grpc.go index 30a25b6404..65f84592df 100644 --- a/cmd/revad/pkg/config/grpc.go +++ b/cmd/revad/pkg/config/grpc.go @@ -25,7 +25,7 @@ import ( // GRPC holds the configuration for the GRPC services. type GRPC struct { - Address string `mapstructure:"address" key:"address" default:"0.0.0.0:19000"` + Address string `mapstructure:"address" key:"address"` Network string `mapstructure:"network" key:"network" default:"tcp"` ShutdownDeadline int `mapstructure:"shutdown_deadline" key:"shutdown_deadline"` EnableReflection bool `mapstructure:"enable_reflection" key:"enable_reflection"` @@ -70,6 +70,7 @@ func (c *Config) parseGRPC(raw map[string]any) error { for _, svc := range c.GRPC.Services { for _, cfg := range svc { cfg.Address = addressForService(c.GRPC.Address, cfg.Config) + cfg.Network = networkForService(c.HTTP.Network, cfg.Config) } } return nil diff --git a/cmd/revad/pkg/config/http.go b/cmd/revad/pkg/config/http.go index ffb97fadc7..b70f46b38b 100644 --- a/cmd/revad/pkg/config/http.go +++ b/cmd/revad/pkg/config/http.go @@ -26,7 +26,7 @@ import ( // HTTP holds the configuration for the HTTP services. type HTTP struct { Network string `mapstructure:"network" key:"network" default:"tcp"` - Address string `mapstructure:"address" key:"address" default:"0.0.0.0:19001"` + Address string `mapstructure:"address" key:"address"` CertFile string `mapstructure:"certfile" key:"certfile"` KeyFile string `mapstructure:"keyfile" key:"keyfile"` @@ -70,6 +70,7 @@ func (c *Config) parseHTTP(raw map[string]any) error { for _, svc := range c.HTTP.Services { for _, cfg := range svc { cfg.Address = addressForService(c.HTTP.Address, cfg.Config) + cfg.Network = networkForService(c.HTTP.Network, cfg.Config) } } return nil diff --git a/cmd/revad/pkg/grace/grace.go b/cmd/revad/pkg/grace/grace.go index d650423655..807914e52e 100644 --- a/cmd/revad/pkg/grace/grace.go +++ b/cmd/revad/pkg/grace/grace.go @@ -40,7 +40,7 @@ type Watcher struct { graceful bool ppid int lns map[string]net.Listener - ss map[string]Server + ss []Server SL Serverless pidFile string childPIDs []int @@ -71,7 +71,7 @@ func NewWatcher(opts ...Option) *Watcher { log: zerolog.Nop(), graceful: os.Getenv("GRACEFUL") == "true", ppid: os.Getppid(), - ss: make(map[string]Server), + ss: make([]Server, 0), } for _, opt := range opts { @@ -324,6 +324,12 @@ func (w *Watcher) GetListeners(servers map[string]Addressable) (map[string]net.L // no graceful for svc, s := range servers { network, addr := s.Network(), getAddress(s.Address()) + // multiple services may have the same listener + ln, ok := get(lns, addr, network) + if ok { + lns[svc] = ln + continue + } ln, err := newListener(network, addr) if err != nil { return nil, err @@ -334,6 +340,15 @@ func (w *Watcher) GetListeners(servers map[string]Addressable) (map[string]net.L return lns, nil } +func get(lns map[string]net.Listener, address, network string) (net.Listener, bool) { + for _, ln := range lns { + if ln.Addr().Network() == network && ln.Addr().String() == address { + return ln, true + } + } + return nil, false +} + type Addressable interface { Network() string Address() string @@ -353,7 +368,7 @@ type Serverless interface { GracefulStop() error } -func (w *Watcher) SetServers(s map[string]Server) { w.ss = s } +func (w *Watcher) SetServers(s []Server) { w.ss = s } // TrapSignals captures the OS signal. func (w *Watcher) TrapSignals() { diff --git a/cmd/revad/runtime/runtime.go b/cmd/revad/runtime/runtime.go index 25bd29dfe6..9ec78da945 100644 --- a/cmd/revad/runtime/runtime.go +++ b/cmd/revad/runtime/runtime.go @@ -21,7 +21,6 @@ package runtime import ( "fmt" "net" - "os" "runtime" "strconv" "strings" @@ -35,6 +34,7 @@ import ( "github.com/cs3org/reva/pkg/rhttp/global" "github.com/cs3org/reva/pkg/rserverless" "github.com/cs3org/reva/pkg/sharedconf" + "github.com/cs3org/reva/pkg/utils/list" "github.com/cs3org/reva/pkg/utils/maps" "github.com/rs/zerolog" "golang.org/x/sync/errgroup" @@ -98,11 +98,12 @@ func New(config *config.Config, opt ...Option) (*Reva, error) { return nil, err } - servers, err := newServers(grpc, http, log) + servers, err := newServers(grpc, http, listeners, log) if err != nil { watcher.Clean() return nil, err } + watcher.SetServers(list.Map(servers, func(s *Server) grace.Server { return s.server })) serverless, err := newServerless(config, log) if err != nil { @@ -192,9 +193,7 @@ func groupGRPCByAddress(cfg *config.Config) (map[string]*config.GRPC, map[string Services: make(map[string]config.ServicesConfig), Interceptors: cfg.GRPC.Interceptors, } - if s.Address == "" { - a[s.Label] = &addr{address: s.Address, network: s.Network} - } + a[s.Label] = &addr{address: s.Address, network: s.Network} } g[s.Address].Services[s.Name] = config.ServicesConfig{ {Config: s.Config}, @@ -216,9 +215,7 @@ func groupHTTPByAddress(cfg *config.Config) (map[string]*config.HTTP, map[string Services: make(map[string]config.ServicesConfig), Middlewares: cfg.HTTP.Middlewares, } - if s.Address == "" { - a[s.Label] = &addr{address: s.Address, network: s.Network} - } + a[s.Label] = &addr{address: s.Address, network: s.Network} } g[s.Address].Services[s.Name] = config.ServicesConfig{ {Config: s.Config}, @@ -266,11 +263,6 @@ func initCPUCount(conf *config.Core, log *zerolog.Logger) error { return nil } -func abort(msg string, args ...any) { - fmt.Fprintf(os.Stderr, msg, args...) - os.Exit(1) -} - func handlePIDFlag(l *zerolog.Logger, pidFile string) (*grace.Watcher, error) { w := grace.NewWatcher( grace.WithPIDFile(pidFile), @@ -324,7 +316,27 @@ func adjustCPU(cpu string) (int, error) { return numCPU, nil } -func newServers(grpc map[string]*config.GRPC, http map[string]*config.HTTP, log *zerolog.Logger) ([]*Server, error) { +func firstKey[K comparable, V any](m map[K]V) (K, bool) { + for k := range m { + return k, true + } + var k K + return k, false +} + +func listenerFromServices[V any](lns map[string]net.Listener, svcs map[string]V) net.Listener { + svc, ok := firstKey(svcs) + if !ok { + panic("services map should be not empty") + } + ln, ok := lns[svc] + if !ok { + panic("listener not assigned for service " + svc) + } + return ln +} + +func newServers(grpc map[string]*config.GRPC, http map[string]*config.HTTP, lns map[string]net.Listener, log *zerolog.Logger) ([]*Server, error) { servers := make([]*Server, 0, len(grpc)+len(http)) for _, cfg := range grpc { services, err := rgrpc.InitServices(cfg.Services) @@ -348,6 +360,7 @@ func newServers(grpc map[string]*config.GRPC, http map[string]*config.HTTP, log } server := &Server{ server: s, + listener: listenerFromServices(lns, services), services: maps.MapValues(services, func(s rgrpc.Service) any { return s }), } servers = append(servers, server) @@ -372,6 +385,7 @@ func newServers(grpc map[string]*config.GRPC, http map[string]*config.HTTP, log } server := &Server{ server: s, + listener: listenerFromServices(lns, services), services: maps.MapValues(services, func(s global.Service) any { return s }), } servers = append(servers, server) From 07914204f78ab4f1c7c5653db147a4cc0d442c16 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Wed, 28 Jun 2023 09:21:08 +0200 Subject: [PATCH 49/76] cleanup serverless --- cmd/revad/pkg/grace/grace.go | 3 ++- cmd/revad/runtime/runtime.go | 9 ++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cmd/revad/pkg/grace/grace.go b/cmd/revad/pkg/grace/grace.go index 807914e52e..ae49a76fe9 100644 --- a/cmd/revad/pkg/grace/grace.go +++ b/cmd/revad/pkg/grace/grace.go @@ -368,7 +368,8 @@ type Serverless interface { GracefulStop() error } -func (w *Watcher) SetServers(s []Server) { w.ss = s } +func (w *Watcher) SetServers(s []Server) { w.ss = s } +func (w *Watcher) SetServerless(s Serverless) { w.SL = s } // TrapSignals captures the OS signal. func (w *Watcher) TrapSignals() { diff --git a/cmd/revad/runtime/runtime.go b/cmd/revad/runtime/runtime.go index 9ec78da945..675afef32b 100644 --- a/cmd/revad/runtime/runtime.go +++ b/cmd/revad/runtime/runtime.go @@ -87,27 +87,22 @@ func New(config *config.Config, opt ...Option) (*Reva, error) { listeners, err := watcher.GetListeners(maps.Merge(addrGRPC, addrHTTP)) if err != nil { - watcher.Clean() return nil, err } setRandomAddresses(config, listeners, log) if err := applyTemplates(config); err != nil { - watcher.Clean() return nil, err } servers, err := newServers(grpc, http, listeners, log) if err != nil { - watcher.Clean() return nil, err } - watcher.SetServers(list.Map(servers, func(s *Server) grace.Server { return s.server })) serverless, err := newServerless(config, log) if err != nil { - watcher.Clean() return nil, err } @@ -225,6 +220,10 @@ func groupHTTPByAddress(cfg *config.Config) (map[string]*config.HTTP, map[string } func (r *Reva) Start() error { + defer r.watcher.Clean() + r.watcher.SetServers(list.Map(r.servers, func(s *Server) grace.Server { return s.server })) + r.watcher.SetServerless(r.serverless) + var g errgroup.Group for _, server := range r.servers { server := server From 8fc6e0840606a3c807c82258f13882ed04ac1868 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Wed, 28 Jun 2023 10:20:35 +0200 Subject: [PATCH 50/76] fixes --- cmd/revad/runtime/runtime.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/revad/runtime/runtime.go b/cmd/revad/runtime/runtime.go index 675afef32b..274df78579 100644 --- a/cmd/revad/runtime/runtime.go +++ b/cmd/revad/runtime/runtime.go @@ -188,8 +188,8 @@ func groupGRPCByAddress(cfg *config.Config) (map[string]*config.GRPC, map[string Services: make(map[string]config.ServicesConfig), Interceptors: cfg.GRPC.Interceptors, } - a[s.Label] = &addr{address: s.Address, network: s.Network} } + a[s.Label] = &addr{address: s.Address, network: s.Network} g[s.Address].Services[s.Name] = config.ServicesConfig{ {Config: s.Config}, } @@ -210,8 +210,8 @@ func groupHTTPByAddress(cfg *config.Config) (map[string]*config.HTTP, map[string Services: make(map[string]config.ServicesConfig), Middlewares: cfg.HTTP.Middlewares, } - a[s.Label] = &addr{address: s.Address, network: s.Network} } + a[s.Label] = &addr{address: s.Address, network: s.Network} g[s.Address].Services[s.Name] = config.ServicesConfig{ {Config: s.Config}, } From 42bd7c656ee750349594629bc79a07d4c2efda6b Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Wed, 28 Jun 2023 10:21:51 +0200 Subject: [PATCH 51/76] init shared config after applying templates --- cmd/revad/runtime/runtime.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cmd/revad/runtime/runtime.go b/cmd/revad/runtime/runtime.go index 274df78579..edbc092692 100644 --- a/cmd/revad/runtime/runtime.go +++ b/cmd/revad/runtime/runtime.go @@ -67,8 +67,6 @@ func New(config *config.Config, opt ...Option) (*Reva, error) { opts := newOptions(opt...) log := opts.Logger - initSharedConf(config) - if err := initCPUCount(config.Core, log); err != nil { return nil, err } @@ -95,6 +93,7 @@ func New(config *config.Config, opt ...Option) (*Reva, error) { if err := applyTemplates(config); err != nil { return nil, err } + initSharedConf(config) servers, err := newServers(grpc, http, listeners, log) if err != nil { From aaa17ed9619bf9c8d4ea4674fc0e42d9c12736a6 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Wed, 28 Jun 2023 11:12:56 +0200 Subject: [PATCH 52/76] fix port assignement when multiple services listen to the same port --- cmd/revad/pkg/grace/grace.go | 32 +++++++++++++++++++++++++++++++- cmd/revad/runtime/runtime.go | 4 ++-- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/cmd/revad/pkg/grace/grace.go b/cmd/revad/pkg/grace/grace.go index ae49a76fe9..b53b3d2e63 100644 --- a/cmd/revad/pkg/grace/grace.go +++ b/cmd/revad/pkg/grace/grace.go @@ -342,13 +342,43 @@ func (w *Watcher) GetListeners(servers map[string]Addressable) (map[string]net.L func get(lns map[string]net.Listener, address, network string) (net.Listener, bool) { for _, ln := range lns { - if ln.Addr().Network() == network && ln.Addr().String() == address { + if addressEqual(ln.Addr(), network, address) { return ln, true } } return nil, false } +func addressEqual(a net.Addr, network, address string) bool { + if a.Network() != network { + return false + } + + switch network { + case "tcp": + t, err := net.ResolveTCPAddr(network, address) + if err != nil { + return false + } + return tcpAddressEqual(a.(*net.TCPAddr), t) + case "unix": + t, err := net.ResolveUnixAddr(network, address) + if err != nil { + return false + } + return unixAddressEqual(a.(*net.UnixAddr), t) + } + return false +} + +func tcpAddressEqual(a1, a2 *net.TCPAddr) bool { + return a1.Port == a2.Port +} + +func unixAddressEqual(a1, a2 *net.UnixAddr) bool { + return a1.Name == a2.Name && a1.Net == a2.Net +} + type Addressable interface { Network() string Address() string diff --git a/cmd/revad/runtime/runtime.go b/cmd/revad/runtime/runtime.go index edbc092692..42fa93326e 100644 --- a/cmd/revad/runtime/runtime.go +++ b/cmd/revad/runtime/runtime.go @@ -190,7 +190,7 @@ func groupGRPCByAddress(cfg *config.Config) (map[string]*config.GRPC, map[string } a[s.Label] = &addr{address: s.Address, network: s.Network} g[s.Address].Services[s.Name] = config.ServicesConfig{ - {Config: s.Config}, + {Config: s.Config, Address: s.Address, Network: s.Network}, } }) return g, a @@ -212,7 +212,7 @@ func groupHTTPByAddress(cfg *config.Config) (map[string]*config.HTTP, map[string } a[s.Label] = &addr{address: s.Address, network: s.Network} g[s.Address].Services[s.Name] = config.ServicesConfig{ - {Config: s.Config}, + {Config: s.Config, Address: s.Address, Network: s.Network}, } }) return g, a From db6a0291aa8c0faaf6da03b8993dee019c3bb5d3 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Wed, 28 Jun 2023 11:29:54 +0200 Subject: [PATCH 53/76] do not bail out for gotemplates in config --- cmd/revad/pkg/config/config.go | 22 ++++++++++++++++++++++ cmd/revad/pkg/config/lookup.go | 15 +++++++++++---- cmd/revad/pkg/config/templates.go | 3 +++ 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/cmd/revad/pkg/config/config.go b/cmd/revad/pkg/config/config.go index 43f4adaaad..dc082ff90c 100644 --- a/cmd/revad/pkg/config/config.go +++ b/cmd/revad/pkg/config/config.go @@ -22,6 +22,7 @@ import ( "fmt" "io" "reflect" + "strings" "github.com/BurntSushi/toml" "github.com/creasty/defaults" @@ -123,5 +124,26 @@ func (c *Config) Dump() map[string]any { } func (c *Config) Lookup(key string) (any, error) { + // check thet key is valid, meaning it starts with one of + // the fields of the config struct + if !c.isValidKey(key) { + return nil, nil + } return lookupByType(key, reflect.ValueOf(c)) } + +func (c *Config) isValidKey(key string) bool { + e := reflect.TypeOf(c).Elem() + k := strings.TrimPrefix(key, ".") + for i := 0; i < e.NumField(); i++ { + f := e.Field(i) + prefix := f.Tag.Get("key") + if prefix == "" || prefix == "-" { + continue + } + if strings.HasPrefix(k, prefix) { + return true + } + } + return false +} diff --git a/cmd/revad/pkg/config/lookup.go b/cmd/revad/pkg/config/lookup.go index 29af8e477d..a0684fe086 100644 --- a/cmd/revad/pkg/config/lookup.go +++ b/cmd/revad/pkg/config/lookup.go @@ -27,7 +27,13 @@ import ( // ErrKeyNotFound is the error returned when a key does not exist // in the configuration. -var ErrKeyNotFound = errors.New("key not found") +type ErrKeyNotFound struct { + Key string +} + +func (e ErrKeyNotFound) Error() string { + return "key '" + e.Key + "' not found in the configuration" +} func lookupStruct(key string, v reflect.Value) (any, error) { if v.Kind() != reflect.Struct { @@ -78,7 +84,8 @@ func lookupStruct(key string, v reflect.Value) (any, error) { default: panic("squash not allowed on non map/struct types") } - if errors.Is(err, ErrKeyNotFound) { + var e ErrKeyNotFound + if errors.As(err, &e) { continue } if err != nil { @@ -93,7 +100,7 @@ func lookupStruct(key string, v reflect.Value) (any, error) { return lookupByType(next, val) } - return nil, ErrKeyNotFound + return nil, ErrKeyNotFound{Key: key} } func lookupByType(key string, v reflect.Value) (any, error) { @@ -135,7 +142,7 @@ func lookupMap(key string, v reflect.Value) (any, error) { // lookup elemen in the map el := v.MapIndex(reflect.ValueOf(c.Key)) if !el.IsValid() { - return nil, ErrKeyNotFound + return nil, ErrKeyNotFound{Key: key} } return lookupByType(next, el) diff --git a/cmd/revad/pkg/config/templates.go b/cmd/revad/pkg/config/templates.go index 49738b74c8..38014d3f11 100644 --- a/cmd/revad/pkg/config/templates.go +++ b/cmd/revad/pkg/config/templates.go @@ -144,6 +144,9 @@ func applyTemplateString(l Lookuper, p setter, v reflect.Value) error { if err != nil { return err } + if val == nil { + return nil + } str, ok := val.(string) if ok { From 6f1404299c4b8ac39ba138621533d001ba640946 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Wed, 28 Jun 2023 11:56:33 +0200 Subject: [PATCH 54/76] allow shared config to have templates --- cmd/revad/pkg/config/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/revad/pkg/config/config.go b/cmd/revad/pkg/config/config.go index dc082ff90c..3a82aaae08 100644 --- a/cmd/revad/pkg/config/config.go +++ b/cmd/revad/pkg/config/config.go @@ -35,7 +35,7 @@ type Config struct { GRPC *GRPC `key:"grpc" mapstructure:"-" default:"{}"` HTTP *HTTP `key:"http" mapstructure:"-" default:"{}"` Serverless *Serverless `key:"serverless" mapstructure:"-" default:"{}"` - Shared *Shared `key:"shared" mapstructure:"shared" default:"{}" template:"-"` + Shared *Shared `key:"shared" mapstructure:"shared" default:"{}"` Log *Log `key:"log" mapstructure:"log" default:"{}" template:"-"` Core *Core `key:"core" mapstructure:"core" default:"{}" template:"-"` Vars Vars `key:"vars" mapstructure:"vars" default:"{}" template:"-"` From 4234cbd9b2619d33c94c75d6e7641cbd431d2d6e Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Wed, 28 Jun 2023 12:18:51 +0200 Subject: [PATCH 55/76] fix pid file for dev-dir option --- cmd/revad/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/revad/main.go b/cmd/revad/main.go index c8b1e7a551..778da99a73 100644 --- a/cmd/revad/main.go +++ b/cmd/revad/main.go @@ -246,10 +246,10 @@ func abort(log *zerolog.Logger, format string, a ...any) { func runMultiple(confs []*config.Config) { var wg sync.WaitGroup - pidfile := getPidfile() for _, conf := range confs { wg.Add(1) + pidfile := getPidfile() go func(wg *sync.WaitGroup, conf *config.Config) { defer wg.Done() runSingle(conf, pidfile) From 212e3a5a0e96bbbb164b2691625cdfea60fbbdf6 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Wed, 28 Jun 2023 14:57:05 +0200 Subject: [PATCH 56/76] add useful logs --- cmd/revad/pkg/grace/grace.go | 15 ++++++++------- cmd/revad/runtime/runtime.go | 12 ++++++++++-- pkg/utils/maps/maps.go | 8 ++++++++ 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/cmd/revad/pkg/grace/grace.go b/cmd/revad/pkg/grace/grace.go index b53b3d2e63..ef0f188518 100644 --- a/cmd/revad/pkg/grace/grace.go +++ b/cmd/revad/pkg/grace/grace.go @@ -321,19 +321,20 @@ func (w *Watcher) GetListeners(servers map[string]Addressable) (map[string]net.L return lns, nil } + var err error // no graceful for svc, s := range servers { network, addr := s.Network(), getAddress(s.Address()) // multiple services may have the same listener ln, ok := get(lns, addr, network) - if ok { - lns[svc] = ln - continue - } - ln, err := newListener(network, addr) - if err != nil { - return nil, err + if !ok { + ln, err = newListener(network, addr) + if err != nil { + return nil, err + } } + w.log.Debug(). + Msgf("listener for %s assigned to %s:%s", svc, ln.Addr().Network(), ln.Addr().String()) lns[svc] = ln } w.lns = lns diff --git a/cmd/revad/runtime/runtime.go b/cmd/revad/runtime/runtime.go index 42fa93326e..62b4ebcf25 100644 --- a/cmd/revad/runtime/runtime.go +++ b/cmd/revad/runtime/runtime.go @@ -356,11 +356,15 @@ func newServers(grpc map[string]*config.GRPC, http map[string]*config.HTTP, lns if err != nil { return nil, err } + ln := listenerFromServices(lns, services) server := &Server{ server: s, - listener: listenerFromServices(lns, services), + listener: ln, services: maps.MapValues(services, func(s rgrpc.Service) any { return s }), } + log.Debug(). + Interface("services", maps.Keys(cfg.Services)). + Msgf("spawned grpc server for services listening at %s:%s", ln.Addr().Network(), ln.Addr().String()) servers = append(servers, server) } for _, cfg := range http { @@ -381,11 +385,15 @@ func newServers(grpc map[string]*config.GRPC, http map[string]*config.HTTP, lns if err != nil { return nil, err } + ln := listenerFromServices(lns, services) server := &Server{ server: s, - listener: listenerFromServices(lns, services), + listener: ln, services: maps.MapValues(services, func(s global.Service) any { return s }), } + log.Debug(). + Interface("services", maps.Keys(cfg.Services)). + Msgf("spawned http server for services listening at %s:%s", ln.Addr().Network(), ln.Addr().String()) servers = append(servers, server) } return servers, nil diff --git a/pkg/utils/maps/maps.go b/pkg/utils/maps/maps.go index 7567be8236..0b04f84025 100644 --- a/pkg/utils/maps/maps.go +++ b/pkg/utils/maps/maps.go @@ -40,3 +40,11 @@ func MapValues[K comparable, T, V any](m map[K]T, f func(T) V) map[K]V { } return r } + +func Keys[K comparable, V any](m map[K]V) []K { + l := make([]K, 0, len(m)) + for k := range m { + l = append(l, k) + } + return l +} From 5df025dc68e7160f698173751f3cddd3e634599e Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Wed, 28 Jun 2023 16:17:05 +0200 Subject: [PATCH 57/76] fixes with multiple drivers of same service --- cmd/revad/pkg/config/common.go | 36 +++++++++++------ cmd/revad/pkg/grace/grace.go | 33 +-------------- cmd/revad/runtime/runtime.go | 74 ++++++++++++++++++---------------- pkg/utils/net/net.go | 33 +++++++++++++++ 4 files changed, 99 insertions(+), 77 deletions(-) create mode 100644 pkg/utils/net/net.go diff --git a/cmd/revad/pkg/config/common.go b/cmd/revad/pkg/config/common.go index 15d3e151c7..62ef681ac7 100644 --- a/cmd/revad/pkg/config/common.go +++ b/cmd/revad/pkg/config/common.go @@ -43,18 +43,33 @@ type DriverConfig struct { Config map[string]any `key:",squash"` Address string `key:"address"` Network string `key:"network"` + Label string `key:"-"` } -func newSvcConfigFromList(l []map[string]any) (ServicesConfig, error) { +func (s *ServicesConfig) Add(svc string, c *DriverConfig) { + l := len(*s) + if l == 0 { + // the label is simply the service name + c.Label = svc + } else { + c.Label = label(svc, l) + if l == 1 { + (*s)[0].Label = label(svc, 0) + } + } + *s = append(*s, c) +} + +func newSvcConfigFromList(name string, l []map[string]any) (ServicesConfig, error) { cfg := make(ServicesConfig, 0, len(l)) for _, c := range l { - cfg = append(cfg, &DriverConfig{Config: c}) + cfg.Add(name, &DriverConfig{Config: c}) } return cfg, nil } -func newSvcConfigFromMap(m map[string]any) ServicesConfig { - s, _ := newSvcConfigFromList([]map[string]any{m}) +func newSvcConfigFromMap(name string, m map[string]any) ServicesConfig { + s, _ := newSvcConfigFromList(name, []map[string]any{m}) return s } @@ -70,7 +85,7 @@ func parseServices(cfg map[string]any) (map[string]ServicesConfig, error) { // cfg can be a list or a map cfgLst, ok := cfg.([]map[string]any) if ok { - s, err := newSvcConfigFromList(cfgLst) + s, err := newSvcConfigFromList(name, cfgLst) if err != nil { return nil, err } @@ -81,7 +96,7 @@ func parseServices(cfg map[string]any) (map[string]ServicesConfig, error) { if !ok { return nil, fmt.Errorf("grpc.services.%s must be a list or a map. got %T", name, cfg) } - services[name] = newSvcConfigFromMap(cfgMap) + services[name] = newSvcConfigFromMap(name, cfgMap) } return services, nil @@ -138,12 +153,12 @@ func (i iterableImpl) ForEachService(f ServiceFunc) { return } for name, c := range i.i.services() { - for i, cfg := range c { + for _, cfg := range c { f(&Service{ raw: cfg, Address: cfg.Address, Network: cfg.Network, - Label: label(name, i, len(c)), + Label: cfg.Label, Name: name, Config: cfg.Config, }) @@ -151,10 +166,7 @@ func (i iterableImpl) ForEachService(f ServiceFunc) { } } -func label(name string, i, tot int) string { - if tot == 1 { - return name - } +func label(name string, i int) string { return fmt.Sprintf("%s_%d", name, i) } diff --git a/cmd/revad/pkg/grace/grace.go b/cmd/revad/pkg/grace/grace.go index ef0f188518..ca6097d871 100644 --- a/cmd/revad/pkg/grace/grace.go +++ b/cmd/revad/pkg/grace/grace.go @@ -29,6 +29,7 @@ import ( "syscall" "time" + netutil "github.com/cs3org/reva/pkg/utils/net" "github.com/pkg/errors" "github.com/rs/zerolog" ) @@ -343,43 +344,13 @@ func (w *Watcher) GetListeners(servers map[string]Addressable) (map[string]net.L func get(lns map[string]net.Listener, address, network string) (net.Listener, bool) { for _, ln := range lns { - if addressEqual(ln.Addr(), network, address) { + if netutil.AddressEqual(ln.Addr(), network, address) { return ln, true } } return nil, false } -func addressEqual(a net.Addr, network, address string) bool { - if a.Network() != network { - return false - } - - switch network { - case "tcp": - t, err := net.ResolveTCPAddr(network, address) - if err != nil { - return false - } - return tcpAddressEqual(a.(*net.TCPAddr), t) - case "unix": - t, err := net.ResolveUnixAddr(network, address) - if err != nil { - return false - } - return unixAddressEqual(a.(*net.UnixAddr), t) - } - return false -} - -func tcpAddressEqual(a1, a2 *net.TCPAddr) bool { - return a1.Port == a2.Port -} - -func unixAddressEqual(a1, a2 *net.UnixAddr) bool { - return a1.Name == a2.Name && a1.Net == a2.Net -} - type Addressable interface { Network() string Address() string diff --git a/cmd/revad/runtime/runtime.go b/cmd/revad/runtime/runtime.go index 62b4ebcf25..93432f4da6 100644 --- a/cmd/revad/runtime/runtime.go +++ b/cmd/revad/runtime/runtime.go @@ -36,6 +36,7 @@ import ( "github.com/cs3org/reva/pkg/sharedconf" "github.com/cs3org/reva/pkg/utils/list" "github.com/cs3org/reva/pkg/utils/maps" + netutil "github.com/cs3org/reva/pkg/utils/net" "github.com/rs/zerolog" "golang.org/x/sync/errgroup" ) @@ -71,9 +72,6 @@ func New(config *config.Config, opt ...Option) (*Reva, error) { return nil, err } - grpc, addrGRPC := groupGRPCByAddress(config) - http, addrHTTP := groupHTTPByAddress(config) - if opts.PidFile == "" { return nil, errors.New("pid file not provided") } @@ -83,7 +81,7 @@ func New(config *config.Config, opt ...Option) (*Reva, error) { return nil, err } - listeners, err := watcher.GetListeners(maps.Merge(addrGRPC, addrHTTP)) + listeners, err := watcher.GetListeners(servicesAddresses(config)) if err != nil { return nil, err } @@ -95,6 +93,8 @@ func New(config *config.Config, opt ...Option) (*Reva, error) { } initSharedConf(config) + grpc := groupGRPCByAddress(config) + http := groupHTTPByAddress(config) servers, err := newServers(grpc, http, listeners, log) if err != nil { return nil, err @@ -116,6 +116,17 @@ func New(config *config.Config, opt ...Option) (*Reva, error) { }, nil } +func servicesAddresses(cfg *config.Config) map[string]grace.Addressable { + a := make(map[string]grace.Addressable) + cfg.GRPC.ForEachService(func(s *config.Service) { + a[s.Label] = &addr{address: s.Address, network: s.Network} + }) + cfg.HTTP.ForEachService(func(s *config.Service) { + a[s.Label] = &addr{address: s.Address, network: s.Network} + }) + return a +} + func newServerless(config *config.Config, log *zerolog.Logger) (*rserverless.Serverless, error) { sl := make(map[string]rserverless.Service) logger := log.With().Str("pkg", "serverless").Logger() @@ -155,6 +166,8 @@ func setRandomAddresses(c *config.Config, lns map[string]net.Listener, log *zero log.Fatal().Msg("port not assigned for service " + s.Label) } s.SetAddress(ln.Addr().String()) + log.Debug(). + Msgf("set random address %s:%s to service %s", ln.Addr().Network(), ln.Addr().String(), s.Label) } c.GRPC.ForEachService(f) c.HTTP.ForEachService(f) @@ -173,10 +186,9 @@ func (a *addr) Network() string { return a.network } -func groupGRPCByAddress(cfg *config.Config) (map[string]*config.GRPC, map[string]grace.Addressable) { +func groupGRPCByAddress(cfg *config.Config) []*config.GRPC { // TODO: same address cannot be used in different configurations g := map[string]*config.GRPC{} - a := map[string]grace.Addressable{} cfg.GRPC.ForEachService(func(s *config.Service) { if _, ok := g[s.Address]; !ok { g[s.Address] = &config.GRPC{ @@ -188,17 +200,19 @@ func groupGRPCByAddress(cfg *config.Config) (map[string]*config.GRPC, map[string Interceptors: cfg.GRPC.Interceptors, } } - a[s.Label] = &addr{address: s.Address, network: s.Network} g[s.Address].Services[s.Name] = config.ServicesConfig{ - {Config: s.Config, Address: s.Address, Network: s.Network}, + {Config: s.Config, Address: s.Address, Network: s.Network, Label: s.Label}, } }) - return g, a + l := make([]*config.GRPC, 0, len(g)) + for _, c := range g { + l = append(l, c) + } + return l } -func groupHTTPByAddress(cfg *config.Config) (map[string]*config.HTTP, map[string]grace.Addressable) { +func groupHTTPByAddress(cfg *config.Config) []*config.HTTP { g := map[string]*config.HTTP{} - a := map[string]grace.Addressable{} cfg.HTTP.ForEachService(func(s *config.Service) { if _, ok := g[s.Address]; !ok { g[s.Address] = &config.HTTP{ @@ -210,12 +224,15 @@ func groupHTTPByAddress(cfg *config.Config) (map[string]*config.HTTP, map[string Middlewares: cfg.HTTP.Middlewares, } } - a[s.Label] = &addr{address: s.Address, network: s.Network} g[s.Address].Services[s.Name] = config.ServicesConfig{ - {Config: s.Config, Address: s.Address, Network: s.Network}, + {Config: s.Config, Address: s.Address, Network: s.Network, Label: s.Label}, } }) - return g, a + l := make([]*config.HTTP, 0, len(g)) + for _, c := range g { + l = append(l, c) + } + return l } func (r *Reva) Start() error { @@ -314,27 +331,16 @@ func adjustCPU(cpu string) (int, error) { return numCPU, nil } -func firstKey[K comparable, V any](m map[K]V) (K, bool) { - for k := range m { - return k, true - } - var k K - return k, false -} - -func listenerFromServices[V any](lns map[string]net.Listener, svcs map[string]V) net.Listener { - svc, ok := firstKey(svcs) - if !ok { - panic("services map should be not empty") - } - ln, ok := lns[svc] - if !ok { - panic("listener not assigned for service " + svc) +func listenerFromAddress(lns map[string]net.Listener, network, address string) net.Listener { + for _, ln := range lns { + if netutil.AddressEqual(ln.Addr(), network, address) { + return ln + } } - return ln + panic(fmt.Sprintf("listener not found for address %s:%s", network, address)) } -func newServers(grpc map[string]*config.GRPC, http map[string]*config.HTTP, lns map[string]net.Listener, log *zerolog.Logger) ([]*Server, error) { +func newServers(grpc []*config.GRPC, http []*config.HTTP, lns map[string]net.Listener, log *zerolog.Logger) ([]*Server, error) { servers := make([]*Server, 0, len(grpc)+len(http)) for _, cfg := range grpc { services, err := rgrpc.InitServices(cfg.Services) @@ -356,7 +362,7 @@ func newServers(grpc map[string]*config.GRPC, http map[string]*config.HTTP, lns if err != nil { return nil, err } - ln := listenerFromServices(lns, services) + ln := listenerFromAddress(lns, cfg.Network, cfg.Address) server := &Server{ server: s, listener: ln, @@ -385,7 +391,7 @@ func newServers(grpc map[string]*config.GRPC, http map[string]*config.HTTP, lns if err != nil { return nil, err } - ln := listenerFromServices(lns, services) + ln := listenerFromAddress(lns, cfg.Network, cfg.Address) server := &Server{ server: s, listener: ln, diff --git a/pkg/utils/net/net.go b/pkg/utils/net/net.go new file mode 100644 index 0000000000..46646fe6af --- /dev/null +++ b/pkg/utils/net/net.go @@ -0,0 +1,33 @@ +package net + +import "net" + +func AddressEqual(a net.Addr, network, address string) bool { + if a.Network() != network { + return false + } + + switch network { + case "tcp": + t, err := net.ResolveTCPAddr(network, address) + if err != nil { + return false + } + return tcpAddressEqual(a.(*net.TCPAddr), t) + case "unix": + t, err := net.ResolveUnixAddr(network, address) + if err != nil { + return false + } + return unixAddressEqual(a.(*net.UnixAddr), t) + } + return false +} + +func tcpAddressEqual(a1, a2 *net.TCPAddr) bool { + return a1.Port == a2.Port +} + +func unixAddressEqual(a1, a2 *net.UnixAddr) bool { + return a1.Name == a2.Name && a1.Net == a2.Net +} From e59b69020ed542eb473818120f4105f481bcc695 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Wed, 28 Jun 2023 16:18:51 +0200 Subject: [PATCH 58/76] clean always pid file on error --- cmd/revad/runtime/runtime.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cmd/revad/runtime/runtime.go b/cmd/revad/runtime/runtime.go index 93432f4da6..fb38211a70 100644 --- a/cmd/revad/runtime/runtime.go +++ b/cmd/revad/runtime/runtime.go @@ -83,12 +83,14 @@ func New(config *config.Config, opt ...Option) (*Reva, error) { listeners, err := watcher.GetListeners(servicesAddresses(config)) if err != nil { + watcher.Clean() return nil, err } setRandomAddresses(config, listeners, log) if err := applyTemplates(config); err != nil { + watcher.Clean() return nil, err } initSharedConf(config) @@ -97,11 +99,13 @@ func New(config *config.Config, opt ...Option) (*Reva, error) { http := groupHTTPByAddress(config) servers, err := newServers(grpc, http, listeners, log) if err != nil { + watcher.Clean() return nil, err } serverless, err := newServerless(config, log) if err != nil { + watcher.Clean() return nil, err } From e438caa96df68d3dbe210828faa318364aa62762 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Wed, 28 Jun 2023 16:54:29 +0200 Subject: [PATCH 59/76] support grpc and http services with same name --- cmd/revad/pkg/config/common.go | 31 +++++++++++++++---------------- cmd/revad/pkg/config/grpc.go | 2 +- cmd/revad/pkg/config/http.go | 2 +- 3 files changed, 17 insertions(+), 18 deletions(-) diff --git a/cmd/revad/pkg/config/common.go b/cmd/revad/pkg/config/common.go index 62ef681ac7..ece0d8d9c7 100644 --- a/cmd/revad/pkg/config/common.go +++ b/cmd/revad/pkg/config/common.go @@ -22,7 +22,6 @@ import ( "fmt" "github.com/mitchellh/mapstructure" - "github.com/pkg/errors" ) type iterable interface { @@ -46,38 +45,38 @@ type DriverConfig struct { Label string `key:"-"` } -func (s *ServicesConfig) Add(svc string, c *DriverConfig) { +func (s *ServicesConfig) Add(domain, svc string, c *DriverConfig) { l := len(*s) if l == 0 { // the label is simply the service name - c.Label = svc + c.Label = domain + "_" + svc } else { - c.Label = label(svc, l) + c.Label = label(domain, svc, l) if l == 1 { - (*s)[0].Label = label(svc, 0) + (*s)[0].Label = label(domain, svc, 0) } } *s = append(*s, c) } -func newSvcConfigFromList(name string, l []map[string]any) (ServicesConfig, error) { +func newSvcConfigFromList(domain, name string, l []map[string]any) (ServicesConfig, error) { cfg := make(ServicesConfig, 0, len(l)) for _, c := range l { - cfg.Add(name, &DriverConfig{Config: c}) + cfg.Add(domain, name, &DriverConfig{Config: c}) } return cfg, nil } -func newSvcConfigFromMap(name string, m map[string]any) ServicesConfig { - s, _ := newSvcConfigFromList(name, []map[string]any{m}) +func newSvcConfigFromMap(domain, name string, m map[string]any) ServicesConfig { + s, _ := newSvcConfigFromList(domain, name, []map[string]any{m}) return s } -func parseServices(cfg map[string]any) (map[string]ServicesConfig, error) { +func parseServices(domain string, cfg map[string]any) (map[string]ServicesConfig, error) { // parse services svcCfg, ok := cfg["services"].(map[string]any) if !ok { - return nil, errors.New("grpc.services must be a map") + return nil, fmt.Errorf("%s.services must be a map", domain) } services := make(map[string]ServicesConfig) @@ -85,7 +84,7 @@ func parseServices(cfg map[string]any) (map[string]ServicesConfig, error) { // cfg can be a list or a map cfgLst, ok := cfg.([]map[string]any) if ok { - s, err := newSvcConfigFromList(name, cfgLst) + s, err := newSvcConfigFromList(domain, name, cfgLst) if err != nil { return nil, err } @@ -94,9 +93,9 @@ func parseServices(cfg map[string]any) (map[string]ServicesConfig, error) { } cfgMap, ok := cfg.(map[string]any) if !ok { - return nil, fmt.Errorf("grpc.services.%s must be a list or a map. got %T", name, cfg) + return nil, fmt.Errorf("%s.services.%s must be a list or a map. got %T", domain, name, cfg) } - services[name] = newSvcConfigFromMap(name, cfgMap) + services[name] = newSvcConfigFromMap(domain, name, cfgMap) } return services, nil @@ -166,8 +165,8 @@ func (i iterableImpl) ForEachService(f ServiceFunc) { } } -func label(name string, i int) string { - return fmt.Sprintf("%s_%d", name, i) +func label(domain, name string, i int) string { + return fmt.Sprintf("%s_%s_%d", domain, name, i) } // ForEachInterceptor iterates to each middleware calling the function f. diff --git a/cmd/revad/pkg/config/grpc.go b/cmd/revad/pkg/config/grpc.go index 65f84592df..835ecc3e24 100644 --- a/cmd/revad/pkg/config/grpc.go +++ b/cmd/revad/pkg/config/grpc.go @@ -53,7 +53,7 @@ func (c *Config) parseGRPC(raw map[string]any) error { return errors.New("grpc must be a map") } - services, err := parseServices(cfgGRPC) + services, err := parseServices("grpc", cfgGRPC) if err != nil { return err } diff --git a/cmd/revad/pkg/config/http.go b/cmd/revad/pkg/config/http.go index b70f46b38b..339480c7ff 100644 --- a/cmd/revad/pkg/config/http.go +++ b/cmd/revad/pkg/config/http.go @@ -53,7 +53,7 @@ func (c *Config) parseHTTP(raw map[string]any) error { return errors.New("http must be a map") } - services, err := parseServices(cfgHTTP) + services, err := parseServices("http", cfgHTTP) if err != nil { return err } From 2c02e04102548b2c4b8240a4c941156e719f817b Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Wed, 28 Jun 2023 17:23:38 +0200 Subject: [PATCH 60/76] fix unit tests --- cmd/revad/pkg/config/config_test.go | 60 +++++++++++++++++++++-------- cmd/revad/pkg/config/dump.go | 7 +++- 2 files changed, 50 insertions(+), 17 deletions(-) diff --git a/cmd/revad/pkg/config/config_test.go b/cmd/revad/pkg/config/config_test.go index 91a11de5b7..36ef8c0dac 100644 --- a/cmd/revad/pkg/config/config_test.go +++ b/cmd/revad/pkg/config/config_test.go @@ -67,6 +67,8 @@ something = "test"` }, "address": "localhost:9000", }, + Network: "tcp", + Label: "grpc_authprovider_0", }, { Address: "localhost:9001", @@ -79,6 +81,8 @@ something = "test"` }, }, }, + Network: "tcp", + Label: "grpc_authprovider_1", }, }, "gateway": []*DriverConfig{ @@ -87,6 +91,8 @@ something = "test"` Config: map[string]any{ "something": "test", }, + Network: "tcp", + Label: "grpc_gateway", }, }, } @@ -132,6 +138,8 @@ something = "test"` }, "address": "localhost:9000", }, + Network: "tcp", + Label: "grpc_authprovider_0", }, { Address: "localhost:9001", @@ -144,6 +152,8 @@ something = "test"` }, }, }, + Network: "tcp", + Label: "grpc_authprovider_1", }, }, "gateway": []*DriverConfig{ @@ -151,6 +161,8 @@ something = "test"` Config: map[string]any{ "something": "test", }, + Network: "tcp", + Label: "grpc_gateway", }, }, } @@ -219,30 +231,33 @@ nats_token = "secret-token-example"` c2, err := Load(strings.NewReader(config)) assert.ErrorIs(t, err, nil) - assert.Equal(t, c2.Shared, &Shared{ - GatewaySVC: "localhost:9142", - JWTSecret: "secret", - }) + assert.Equal(t, &Shared{ + GatewaySVC: "localhost:9142", + JWTSecret: "secret", + DataGateway: "http://0.0.0.0:19001/datagateway", + BlockedUsers: []string{}, + }, c2.Shared) - assert.Equal(t, c2.Log, &Log{ + assert.Equal(t, &Log{ Output: "/var/log/revad/revad-gateway.log", Mode: "json", Level: "trace", - }) + }, c2.Log) - assert.Equal(t, c2.Core, &Core{ + assert.Equal(t, &Core{ MaxCPUs: "1", TracingEnabled: true, - }) + }, c2.Core) - assert.Equal(t, c2.Vars, Vars{ + assert.Equal(t, Vars{ "db_username": "root", "db_password": "secretpassword", - }) + }, c2.Vars) - assertGRPCEqual(t, c2.GRPC, &GRPC{ + assertGRPCEqual(t, &GRPC{ ShutdownDeadline: 10, EnableReflection: true, + Network: "tcp", Interceptors: make(map[string]map[string]any), Services: map[string]ServicesConfig{ "gateway": { @@ -250,6 +265,8 @@ nats_token = "secret-token-example"` Config: map[string]any{ "authregistrysvc": "{{ grpc.services.authregistry.address }}", }, + Label: "grpc_gateway", + Network: "tcp", }, }, "authregistry": { @@ -265,6 +282,8 @@ nats_token = "secret-token-example"` }, }, }, + Label: "grpc_authregistry", + Network: "tcp", }, }, "authprovider": { @@ -279,6 +298,8 @@ nats_token = "secret-token-example"` }, }, }, + Label: "grpc_authprovider_0", + Network: "tcp", }, { Address: "localhost:19001", @@ -291,13 +312,16 @@ nats_token = "secret-token-example"` }, }, }, + Label: "grpc_authprovider_1", + Network: "tcp", }, }, }, - }) + }, c2.GRPC) - assertHTTPEqual(t, c2.HTTP, &HTTP{ + assertHTTPEqual(t, &HTTP{ Address: "localhost:19002", + Network: "tcp", Middlewares: make(map[string]map[string]any), Services: map[string]ServicesConfig{ "dataprovider": { @@ -306,25 +330,29 @@ nats_token = "secret-token-example"` Config: map[string]any{ "driver": "localhome", }, + Network: "tcp", + Label: "http_dataprovider", }, }, "sysinfo": { { Address: "localhost:19002", Config: map[string]any{}, + Network: "tcp", + Label: "http_sysinfo", }, }, }, - }) + }, c2.HTTP) - assert.Equal(t, c2.Serverless, &Serverless{ + assert.Equal(t, &Serverless{ Services: map[string]map[string]any{ "notifications": { "nats_address": "nats-server-01.example.com", "nats_token": "secret-token-example", }, }, - }) + }, c2.Serverless) } func assertGRPCEqual(t *testing.T, g1, g2 *GRPC) { diff --git a/cmd/revad/pkg/config/dump.go b/cmd/revad/pkg/config/dump.go index a87896bb71..e819e5eeb4 100644 --- a/cmd/revad/pkg/config/dump.go +++ b/cmd/revad/pkg/config/dump.go @@ -57,7 +57,12 @@ func dumpStruct(v reflect.Value) map[string]any { continue } - m[fieldName(f)] = dumpByType(e) + n := fieldName(f) + if n == "-" { + continue + } + + m[n] = dumpByType(e) } return m } From eafcb2c8c36a5fac8216497329cf2c86c4e0eb8f Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Wed, 28 Jun 2023 20:06:34 +0200 Subject: [PATCH 61/76] replace template as substring --- cmd/revad/pkg/config/templates.go | 92 +++++++++++++++----------- cmd/revad/pkg/config/templates_test.go | 44 ++++++++++-- 2 files changed, 91 insertions(+), 45 deletions(-) diff --git a/cmd/revad/pkg/config/templates.go b/cmd/revad/pkg/config/templates.go index 38014d3f11..38e06e0cec 100644 --- a/cmd/revad/pkg/config/templates.go +++ b/cmd/revad/pkg/config/templates.go @@ -19,10 +19,11 @@ package config import ( + "fmt" "reflect" + "regexp" + "strconv" "strings" - - "github.com/pkg/errors" ) func applyTemplateStruct(l Lookuper, p setter, v reflect.Value) error { @@ -53,8 +54,6 @@ func applyTemplateStruct(l Lookuper, p setter, v reflect.Value) error { func applyTemplateByType(l Lookuper, p setter, v reflect.Value) error { switch v.Kind() { - case reflect.String: - return applyTemplateString(l, p, v) case reflect.Array, reflect.Slice: return applyTemplateList(l, p, v) case reflect.Struct: @@ -109,57 +108,74 @@ func applyTemplateInterface(l Lookuper, p setter, v reflect.Value) error { return applyTemplateByType(l, p, v.Elem()) } - if !isTemplate(s) { + tmpl, is := isTemplate(s) + if !is { // nothing to do return nil } - key := keyFromTemplate(s) + key := keyFromTemplate(tmpl) val, err := l.Lookup(key) if err != nil { return err } - p.SetValue(val) - return nil -} - -func applyTemplateString(l Lookuper, p setter, v reflect.Value) error { - if v.Kind() != reflect.String { - panic("called applyTemplateString on non string type") - } - - s := v.Interface().(string) - if !isTemplate(s) { - // nothing to do - return nil - } - - if !v.CanSet() { - panic("value is not addressable") - } - - key := keyFromTemplate(s) - val, err := l.Lookup(key) + new, err := replaceTemplate(s, tmpl, val) if err != nil { return err } - if val == nil { - return nil - } + p.SetValue(new) + return nil +} - str, ok := val.(string) - if ok { - p.SetValue(str) - return nil +func replaceTemplate(original, tmpl string, val any) (any, error) { + if strings.TrimSpace(original) == tmpl { + // the value was directly a template, i.e. "{{ grpc.services.gateway.address }}" + return val, nil } + // the value is of something like "something {{ template }} something else" + // in this case we need to replace the template string with the value, converted + // as string in the original val + s, ok := convertToString(val) + if !ok { + return nil, fmt.Errorf("value %v cannot be converted as string in the template %s", val, original) + } + return strings.Replace(original, tmpl, s, 1), nil +} - return errors.New("value cannot be set on a non string type") +func convertToString(val any) (string, bool) { + switch v := val.(type) { + case string: + return v, true + case int: + return strconv.FormatInt(int64(v), 10), true + case int8: + return strconv.FormatInt(int64(v), 10), true + case int16: + return strconv.FormatInt(int64(v), 10), true + case int32: + return strconv.FormatInt(int64(v), 10), true + case uint: + return strconv.FormatUint(uint64(v), 10), true + case uint8: + return strconv.FormatUint(uint64(v), 10), true + case uint16: + return strconv.FormatUint(uint64(v), 10), true + case uint32: + return strconv.FormatUint(uint64(v), 10), true + case uint64: + return strconv.FormatUint(uint64(v), 10), true + case bool: + return strconv.FormatBool(v), true + } + return "", false } -func isTemplate(s string) bool { - s = strings.TrimSpace(s) - return strings.HasPrefix(s, "{{") && strings.HasSuffix(s, "}}") +var templateRegex = regexp.MustCompile("{{.{1,}}}") + +func isTemplate(s string) (string, bool) { + m := templateRegex.FindString(s) + return m, m != "" } func keyFromTemplate(s string) string { diff --git a/cmd/revad/pkg/config/templates_test.go b/cmd/revad/pkg/config/templates_test.go index a4b0b34ba7..b0f9b3f46f 100644 --- a/cmd/revad/pkg/config/templates_test.go +++ b/cmd/revad/pkg/config/templates_test.go @@ -45,17 +45,31 @@ func TestApplyTemplate(t *testing.T) { }, }, }, + "other": { + { + Address: "localhost:1902", + Config: map[string]any{ + "drivers": map[string]any{ + "static": map[string]any{ + "demo": "https://{{ grpc.services.authprovider.address }}/data", + }, + }, + }, + }, + }, }, }, } err := cfg1.ApplyTemplates(cfg1) assert.ErrorIs(t, err, nil) - assert.Equal(t, cfg1.GRPC.Services["authregistry"][0].Config["drivers"].(map[string]any)["static"].(map[string]any)["demo"], "localhost:1900") + assert.Equal(t, "localhost:1900", cfg1.GRPC.Services["authregistry"][0].Config["drivers"].(map[string]any)["static"].(map[string]any)["demo"]) + assert.Equal(t, "https://localhost:1900/data", cfg1.GRPC.Services["other"][0].Config["drivers"].(map[string]any)["static"].(map[string]any)["demo"]) cfg2 := &Config{ Vars: Vars{ "db_username": "root", "db_password": "secretpassword", + "integer": 10, }, GRPC: &GRPC{ Services: map[string]ServicesConfig{ @@ -68,6 +82,19 @@ func TestApplyTemplate(t *testing.T) { "db_username": "{{ vars.db_username }}", "db_password": "{{ vars.db_password }}", "key": "value", + "int": "{{ vars.integer }}", + }, + }, + }, + }, + }, + "other": { + { + Address: "localhost:1902", + Config: map[string]any{ + "drivers": map[string]any{ + "sql": map[string]any{ + "db_host": "http://localhost:{{ vars.integer }}", }, }, }, @@ -79,10 +106,13 @@ func TestApplyTemplate(t *testing.T) { err = cfg2.ApplyTemplates(cfg2) assert.ErrorIs(t, err, nil) - assert.Equal(t, cfg2.GRPC.Services["authregistry"][0].Config["drivers"].(map[string]any)["sql"], - map[string]any{ - "db_username": "root", - "db_password": "secretpassword", - "key": "value", - }) + assert.Equal(t, map[string]any{ + "db_username": "root", + "db_password": "secretpassword", + "key": "value", + "int": 10, + }, cfg2.GRPC.Services["authregistry"][0].Config["drivers"].(map[string]any)["sql"]) + assert.Equal(t, map[string]any{ + "db_host": "http://localhost:10", + }, cfg2.GRPC.Services["other"][0].Config["drivers"].(map[string]any)["sql"]) } From fd04c3d937c3a389d86639db7098a08d08fdb872 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Thu, 29 Jun 2023 10:23:41 +0200 Subject: [PATCH 62/76] get ports from addresses in the template config --- cmd/revad/pkg/config/common.go | 33 ++++++++++++++++++++++---- cmd/revad/pkg/config/config_test.go | 20 ++++++++-------- cmd/revad/pkg/config/grpc.go | 8 +++---- cmd/revad/pkg/config/http.go | 8 +++---- cmd/revad/pkg/config/lookup.go | 31 ++++++++++++++++++++++++ cmd/revad/pkg/config/lookup_test.go | 11 +++++++++ cmd/revad/pkg/config/templates.go | 2 ++ cmd/revad/pkg/config/templates_test.go | 15 +++++++++++- cmd/revad/runtime/runtime.go | 24 +++++++++---------- 9 files changed, 116 insertions(+), 36 deletions(-) diff --git a/cmd/revad/pkg/config/common.go b/cmd/revad/pkg/config/common.go index ece0d8d9c7..6ddf572619 100644 --- a/cmd/revad/pkg/config/common.go +++ b/cmd/revad/pkg/config/common.go @@ -20,6 +20,7 @@ package config import ( "fmt" + "net" "github.com/mitchellh/mapstructure" ) @@ -40,7 +41,7 @@ func (c ServicesConfig) DriversNumber() int { return len(c) } // DriverConfig holds the configuration for a driver. type DriverConfig struct { Config map[string]any `key:",squash"` - Address string `key:"address"` + Address Address `key:"address"` Network string `key:"network"` Label string `key:"-"` } @@ -117,7 +118,7 @@ func parseMiddlwares(cfg map[string]any, key string) (map[string]map[string]any, // Service contains the configuration for a service. type Service struct { - Address string + Address Address Network string Name string Label string @@ -127,7 +128,7 @@ type Service struct { } // SetAddress sets the address for the service in the configuration. -func (s *Service) SetAddress(address string) { +func (s *Service) SetAddress(address Address) { s.Address = address s.raw.Address = address } @@ -179,9 +180,9 @@ func (i iterableImpl) ForEachInterceptor(f InterceptorFunc) { } } -func addressForService(global string, cfg map[string]any) string { +func addressForService(global Address, cfg map[string]any) Address { if address, ok := cfg["address"].(string); ok { - return address + return Address(address) } return global } @@ -192,3 +193,25 @@ func networkForService(global string, cfg map[string]any) string { } return global } + +type Address string + +func (a Address) String() string { return string(a) } + +func (a Address) Get(k string) (any, error) { + switch k { + case "port": + t, err := net.ResolveTCPAddr("tcp", a.String()) + if err != nil { + return nil, err + } + return t.Port, nil + case "ip": + t, err := net.ResolveTCPAddr("tcp", a.String()) + if err != nil { + return nil, err + } + return t.IP.String(), nil + } + return nil, ErrKeyNotFound{Key: k} +} diff --git a/cmd/revad/pkg/config/config_test.go b/cmd/revad/pkg/config/config_test.go index 36ef8c0dac..4490d1ef90 100644 --- a/cmd/revad/pkg/config/config_test.go +++ b/cmd/revad/pkg/config/config_test.go @@ -52,7 +52,7 @@ something = "test"` t.Fatalf("not expected error: %v", err) } - assert.Equal(t, "localhost:9142", c.GRPC.Address) + assert.Equal(t, Address("localhost:9142"), c.GRPC.Address) exp := map[string]ServicesConfig{ "authprovider": []*DriverConfig{ @@ -123,7 +123,7 @@ something = "test"` t.Fatalf("not expected error: %v", err) } - assert.Equal(t, "", c.GRPC.Address) + assert.Equal(t, Address(""), c.GRPC.Address) exp := map[string]ServicesConfig{ "authprovider": []*DriverConfig{ @@ -505,7 +505,7 @@ func TestDump(t *testing.T) { "db_password": "secretpassword", }, "grpc": map[string]any{ - "address": "", + "address": Address(""), "network": "", "shutdown_deadline": 10, "enable_reflection": true, @@ -513,14 +513,14 @@ func TestDump(t *testing.T) { "services": map[string]any{ "gateway": []any{ map[string]any{ - "address": "", + "address": Address(""), "network": "", "authregistrysvc": "localhost:19000", }, }, "authregistry": []any{ map[string]any{ - "address": "localhost:19000", + "address": Address("localhost:19000"), "network": "", "driver": "static", "drivers": map[string]any{ @@ -535,7 +535,7 @@ func TestDump(t *testing.T) { }, "authprovider": []any{ map[string]any{ - "address": "localhost:19001", + "address": Address("localhost:19001"), "network": "", "driver": "ldap", "drivers": map[string]any{ @@ -545,7 +545,7 @@ func TestDump(t *testing.T) { }, }, map[string]any{ - "address": "localhost:19002", + "address": Address("localhost:19002"), "network": "", "driver": "machine", "drivers": map[string]any{ @@ -559,21 +559,21 @@ func TestDump(t *testing.T) { }, "http": map[string]any{ "network": "", - "address": "localhost:19003", + "address": Address("localhost:19003"), "certfile": "", "keyfile": "", "middlewares": map[string]any{}, "services": map[string]any{ "dataprovider": []any{ map[string]any{ - "address": "localhost:19003", + "address": Address("localhost:19003"), "network": "", "driver": "localhome", }, }, "sysinfo": []any{ map[string]any{ - "address": "localhost:19003", + "address": Address("localhost:19003"), "network": "", }, }, diff --git a/cmd/revad/pkg/config/grpc.go b/cmd/revad/pkg/config/grpc.go index 835ecc3e24..26c67bea3d 100644 --- a/cmd/revad/pkg/config/grpc.go +++ b/cmd/revad/pkg/config/grpc.go @@ -25,10 +25,10 @@ import ( // GRPC holds the configuration for the GRPC services. type GRPC struct { - Address string `mapstructure:"address" key:"address"` - Network string `mapstructure:"network" key:"network" default:"tcp"` - ShutdownDeadline int `mapstructure:"shutdown_deadline" key:"shutdown_deadline"` - EnableReflection bool `mapstructure:"enable_reflection" key:"enable_reflection"` + Address Address `mapstructure:"address" key:"address"` + Network string `mapstructure:"network" key:"network" default:"tcp"` + ShutdownDeadline int `mapstructure:"shutdown_deadline" key:"shutdown_deadline"` + EnableReflection bool `mapstructure:"enable_reflection" key:"enable_reflection"` Services map[string]ServicesConfig `mapstructure:"-" key:"services"` Interceptors map[string]map[string]any `mapstructure:"-" key:"interceptors"` diff --git a/cmd/revad/pkg/config/http.go b/cmd/revad/pkg/config/http.go index 339480c7ff..e29d02e089 100644 --- a/cmd/revad/pkg/config/http.go +++ b/cmd/revad/pkg/config/http.go @@ -25,10 +25,10 @@ import ( // HTTP holds the configuration for the HTTP services. type HTTP struct { - Network string `mapstructure:"network" key:"network" default:"tcp"` - Address string `mapstructure:"address" key:"address"` - CertFile string `mapstructure:"certfile" key:"certfile"` - KeyFile string `mapstructure:"keyfile" key:"keyfile"` + Network string `mapstructure:"network" key:"network" default:"tcp"` + Address Address `mapstructure:"address" key:"address"` + CertFile string `mapstructure:"certfile" key:"certfile"` + KeyFile string `mapstructure:"keyfile" key:"keyfile"` Services map[string]ServicesConfig `mapstructure:"-" key:"services"` Middlewares map[string]map[string]any `mapstructure:"-" key:"middlewares"` diff --git a/cmd/revad/pkg/config/lookup.go b/cmd/revad/pkg/config/lookup.go index a0684fe086..727a0ec974 100644 --- a/cmd/revad/pkg/config/lookup.go +++ b/cmd/revad/pkg/config/lookup.go @@ -35,6 +35,10 @@ func (e ErrKeyNotFound) Error() string { return "key '" + e.Key + "' not found in the configuration" } +type Getter interface { + Get(k string) (any, error) +} + func lookupStruct(key string, v reflect.Value) (any, error) { if v.Kind() != reflect.Struct { panic("called lookupStruct on non struct type") @@ -103,7 +107,12 @@ func lookupStruct(key string, v reflect.Value) (any, error) { return nil, ErrKeyNotFound{Key: key} } +var typeGetter = reflect.TypeOf((*Getter)(nil)).Elem() + func lookupByType(key string, v reflect.Value) (any, error) { + if v.Type().Implements(typeGetter) { + return lookupGetter(key, v) + } switch v.Kind() { case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, @@ -121,6 +130,28 @@ func lookupByType(key string, v reflect.Value) (any, error) { panic("type not supported: " + v.Kind().String()) } +func lookupGetter(key string, v reflect.Value) (any, error) { + g, ok := v.Interface().(Getter) + if !ok { + panic("called lookupGetter on type not implementing Getter interface") + } + + cmd, _, err := parseNext(key) + if errors.Is(err, io.EOF) { + return v.Interface(), nil + } + if err != nil { + return nil, err + } + + c, ok := cmd.(FieldByKey) + if !ok { + return nil, errors.New("call of index on getter type") + } + + return g.Get(c.Key) +} + func lookupMap(key string, v reflect.Value) (any, error) { if v.Kind() != reflect.Map { panic("called lookupMap on non map type") diff --git a/cmd/revad/pkg/config/lookup_test.go b/cmd/revad/pkg/config/lookup_test.go index c831f183eb..dcd4e1b9bb 100644 --- a/cmd/revad/pkg/config/lookup_test.go +++ b/cmd/revad/pkg/config/lookup_test.go @@ -53,6 +53,10 @@ type SquashedMap struct { Simple SimpleStruct `key:"simple"` } +type StructWithAddress struct { + Address Address `key:"address"` +} + func TestLookupStruct(t *testing.T) { tests := []struct { in any @@ -175,6 +179,13 @@ func TestLookupStruct(t *testing.T) { key: ".keya", val: "val_a[1]", }, + { + in: StructWithAddress{ + Address: "188.184.37.219:9142", + }, + key: ".address.port", + val: 9142, + }, } for _, tt := range tests { diff --git a/cmd/revad/pkg/config/templates.go b/cmd/revad/pkg/config/templates.go index 38e06e0cec..1449058cd4 100644 --- a/cmd/revad/pkg/config/templates.go +++ b/cmd/revad/pkg/config/templates.go @@ -147,6 +147,8 @@ func convertToString(val any) (string, bool) { switch v := val.(type) { case string: return v, true + case fmt.Stringer: + return v.String(), true case int: return strconv.FormatInt(int64(v), 10), true case int8: diff --git a/cmd/revad/pkg/config/templates_test.go b/cmd/revad/pkg/config/templates_test.go index b0f9b3f46f..45dd50a11f 100644 --- a/cmd/revad/pkg/config/templates_test.go +++ b/cmd/revad/pkg/config/templates_test.go @@ -57,13 +57,26 @@ func TestApplyTemplate(t *testing.T) { }, }, }, + "port": { + { + + Config: map[string]any{ + "drivers": map[string]any{ + "static": map[string]any{ + "demo": "https://cern.ch:{{ grpc.services.authprovider.address.port }}/data", + }, + }, + }, + }, + }, }, }, } err := cfg1.ApplyTemplates(cfg1) assert.ErrorIs(t, err, nil) - assert.Equal(t, "localhost:1900", cfg1.GRPC.Services["authregistry"][0].Config["drivers"].(map[string]any)["static"].(map[string]any)["demo"]) + assert.Equal(t, Address("localhost:1900"), cfg1.GRPC.Services["authregistry"][0].Config["drivers"].(map[string]any)["static"].(map[string]any)["demo"]) assert.Equal(t, "https://localhost:1900/data", cfg1.GRPC.Services["other"][0].Config["drivers"].(map[string]any)["static"].(map[string]any)["demo"]) + assert.Equal(t, "https://cern.ch:1900/data", cfg1.GRPC.Services["port"][0].Config["drivers"].(map[string]any)["static"].(map[string]any)["demo"]) cfg2 := &Config{ Vars: Vars{ diff --git a/cmd/revad/runtime/runtime.go b/cmd/revad/runtime/runtime.go index fb38211a70..f61396fd5c 100644 --- a/cmd/revad/runtime/runtime.go +++ b/cmd/revad/runtime/runtime.go @@ -123,10 +123,10 @@ func New(config *config.Config, opt ...Option) (*Reva, error) { func servicesAddresses(cfg *config.Config) map[string]grace.Addressable { a := make(map[string]grace.Addressable) cfg.GRPC.ForEachService(func(s *config.Service) { - a[s.Label] = &addr{address: s.Address, network: s.Network} + a[s.Label] = &addr{address: s.Address.String(), network: s.Network} }) cfg.HTTP.ForEachService(func(s *config.Service) { - a[s.Label] = &addr{address: s.Address, network: s.Network} + a[s.Label] = &addr{address: s.Address.String(), network: s.Network} }) return a } @@ -169,7 +169,7 @@ func setRandomAddresses(c *config.Config, lns map[string]net.Listener, log *zero if !ok { log.Fatal().Msg("port not assigned for service " + s.Label) } - s.SetAddress(ln.Addr().String()) + s.SetAddress(config.Address(ln.Addr().String())) log.Debug(). Msgf("set random address %s:%s to service %s", ln.Addr().Network(), ln.Addr().String(), s.Label) } @@ -194,9 +194,9 @@ func groupGRPCByAddress(cfg *config.Config) []*config.GRPC { // TODO: same address cannot be used in different configurations g := map[string]*config.GRPC{} cfg.GRPC.ForEachService(func(s *config.Service) { - if _, ok := g[s.Address]; !ok { - g[s.Address] = &config.GRPC{ - Address: s.Address, + if _, ok := g[s.Address.String()]; !ok { + g[s.Address.String()] = &config.GRPC{ + Address: config.Address(s.Address), Network: s.Network, ShutdownDeadline: cfg.GRPC.ShutdownDeadline, EnableReflection: cfg.GRPC.EnableReflection, @@ -204,7 +204,7 @@ func groupGRPCByAddress(cfg *config.Config) []*config.GRPC { Interceptors: cfg.GRPC.Interceptors, } } - g[s.Address].Services[s.Name] = config.ServicesConfig{ + g[s.Address.String()].Services[s.Name] = config.ServicesConfig{ {Config: s.Config, Address: s.Address, Network: s.Network, Label: s.Label}, } }) @@ -218,8 +218,8 @@ func groupGRPCByAddress(cfg *config.Config) []*config.GRPC { func groupHTTPByAddress(cfg *config.Config) []*config.HTTP { g := map[string]*config.HTTP{} cfg.HTTP.ForEachService(func(s *config.Service) { - if _, ok := g[s.Address]; !ok { - g[s.Address] = &config.HTTP{ + if _, ok := g[s.Address.String()]; !ok { + g[s.Address.String()] = &config.HTTP{ Address: s.Address, Network: s.Network, CertFile: cfg.HTTP.CertFile, @@ -228,7 +228,7 @@ func groupHTTPByAddress(cfg *config.Config) []*config.HTTP { Middlewares: cfg.HTTP.Middlewares, } } - g[s.Address].Services[s.Name] = config.ServicesConfig{ + g[s.Address.String()].Services[s.Name] = config.ServicesConfig{ {Config: s.Config, Address: s.Address, Network: s.Network, Label: s.Label}, } }) @@ -335,9 +335,9 @@ func adjustCPU(cpu string) (int, error) { return numCPU, nil } -func listenerFromAddress(lns map[string]net.Listener, network, address string) net.Listener { +func listenerFromAddress(lns map[string]net.Listener, network string, address config.Address) net.Listener { for _, ln := range lns { - if netutil.AddressEqual(ln.Addr(), network, address) { + if netutil.AddressEqual(ln.Addr(), network, address.String()) { return ln } } From 3cc82f69f6d16ae3ba071cc2ee05ad7d12676def Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Thu, 29 Jun 2023 10:56:18 +0200 Subject: [PATCH 63/76] clearer error on lookup --- cmd/revad/pkg/config/config.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cmd/revad/pkg/config/config.go b/cmd/revad/pkg/config/config.go index 3a82aaae08..f9d71e8728 100644 --- a/cmd/revad/pkg/config/config.go +++ b/cmd/revad/pkg/config/config.go @@ -129,7 +129,11 @@ func (c *Config) Lookup(key string) (any, error) { if !c.isValidKey(key) { return nil, nil } - return lookupByType(key, reflect.ValueOf(c)) + val, err := lookupByType(key, reflect.ValueOf(c)) + if err != nil { + return nil, errors.Wrapf(err, "lookup: error on key '%s'", key) + } + return val, nil } func (c *Config) isValidKey(key string) bool { From beb8f4237399eea3c26592c6a61addc667e333b5 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Thu, 29 Jun 2023 11:08:51 +0200 Subject: [PATCH 64/76] fix port addressing from address in driver config --- cmd/revad/pkg/config/common.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/revad/pkg/config/common.go b/cmd/revad/pkg/config/common.go index 6ddf572619..bca984b24c 100644 --- a/cmd/revad/pkg/config/common.go +++ b/cmd/revad/pkg/config/common.go @@ -40,10 +40,10 @@ func (c ServicesConfig) DriversNumber() int { return len(c) } // DriverConfig holds the configuration for a driver. type DriverConfig struct { - Config map[string]any `key:",squash"` Address Address `key:"address"` Network string `key:"network"` Label string `key:"-"` + Config map[string]any `key:",squash"` // this must be at the bottom! } func (s *ServicesConfig) Add(domain, svc string, c *DriverConfig) { From 494d995c97df1055e43eddc0ea13e72c9926f69b Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Thu, 29 Jun 2023 15:40:51 +0200 Subject: [PATCH 65/76] fixes when template is in shared config --- cmd/revad/pkg/config/config_test.go | 6 ++--- cmd/revad/pkg/config/templates.go | 32 ++++++++++++++++++++++++++ cmd/revad/pkg/config/templates_test.go | 4 ++++ 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/cmd/revad/pkg/config/config_test.go b/cmd/revad/pkg/config/config_test.go index 4490d1ef90..aa2395b02e 100644 --- a/cmd/revad/pkg/config/config_test.go +++ b/cmd/revad/pkg/config/config_test.go @@ -320,7 +320,7 @@ nats_token = "secret-token-example"` }, c2.GRPC) assertHTTPEqual(t, &HTTP{ - Address: "localhost:19002", + Address: Address("localhost:19002"), Network: "tcp", Middlewares: make(map[string]map[string]any), Services: map[string]ServicesConfig{ @@ -535,7 +535,7 @@ func TestDump(t *testing.T) { }, "authprovider": []any{ map[string]any{ - "address": Address("localhost:19001"), + "address": "localhost:19001", "network": "", "driver": "ldap", "drivers": map[string]any{ @@ -545,7 +545,7 @@ func TestDump(t *testing.T) { }, }, map[string]any{ - "address": Address("localhost:19002"), + "address": "localhost:19002", "network": "", "driver": "machine", "drivers": map[string]any{ diff --git a/cmd/revad/pkg/config/templates.go b/cmd/revad/pkg/config/templates.go index 1449058cd4..1f9a47dc89 100644 --- a/cmd/revad/pkg/config/templates.go +++ b/cmd/revad/pkg/config/templates.go @@ -62,6 +62,8 @@ func applyTemplateByType(l Lookuper, p setter, v reflect.Value) error { return applyTemplateMap(l, p, v) case reflect.Interface: return applyTemplateInterface(l, p, v) + case reflect.String: + return applyTemplateString(l, p, v) case reflect.Pointer: return applyTemplateByType(l, p, v.Elem()) } @@ -98,6 +100,36 @@ func applyTemplateMap(l Lookuper, p setter, v reflect.Value) error { return nil } +func applyTemplateString(l Lookuper, p setter, v reflect.Value) error { + if v.Kind() != reflect.String { + panic("called applyTemplateString on non string type") + } + s := v.String() + tmpl, is := isTemplate(s) + if !is { + // nothing to do + return nil + } + + key := keyFromTemplate(tmpl) + val, err := l.Lookup(key) + if err != nil { + return err + } + + new, err := replaceTemplate(s, tmpl, val) + if err != nil { + return err + } + str, ok := convertToString(new) + if !ok { + return fmt.Errorf("value %v cannot be converted as string in the template %s", val, new) + } + + p.SetValue(str) + return nil +} + func applyTemplateInterface(l Lookuper, p setter, v reflect.Value) error { if v.Kind() != reflect.Interface { panic("called applyTemplateInterface on non interface value") diff --git a/cmd/revad/pkg/config/templates_test.go b/cmd/revad/pkg/config/templates_test.go index 45dd50a11f..9db0167bec 100644 --- a/cmd/revad/pkg/config/templates_test.go +++ b/cmd/revad/pkg/config/templates_test.go @@ -79,6 +79,9 @@ func TestApplyTemplate(t *testing.T) { assert.Equal(t, "https://cern.ch:1900/data", cfg1.GRPC.Services["port"][0].Config["drivers"].(map[string]any)["static"].(map[string]any)["demo"]) cfg2 := &Config{ + Shared: &Shared{ + GatewaySVC: "{{ grpc.services.authregistry.address }}", + }, Vars: Vars{ "db_username": "root", "db_password": "secretpassword", @@ -119,6 +122,7 @@ func TestApplyTemplate(t *testing.T) { err = cfg2.ApplyTemplates(cfg2) assert.ErrorIs(t, err, nil) + assert.Equal(t, "localhost:1901", cfg2.Shared.GatewaySVC) assert.Equal(t, map[string]any{ "db_username": "root", "db_password": "secretpassword", From be4a00dcd64152f160f1bee04e7cb8374def72c7 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Thu, 29 Jun 2023 17:11:45 +0200 Subject: [PATCH 66/76] fix instantiation of middlewares --- cmd/revad/runtime/grpc.go | 5 ++--- cmd/revad/runtime/http.go | 3 +-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/cmd/revad/runtime/grpc.go b/cmd/revad/runtime/grpc.go index 8ff7c10158..5cd1b4b195 100644 --- a/cmd/revad/runtime/grpc.go +++ b/cmd/revad/runtime/grpc.go @@ -19,7 +19,6 @@ package runtime import ( - "fmt" "sort" "github.com/cs3org/reva/internal/grpc/interceptors/appctx" @@ -53,7 +52,7 @@ func initGRPCInterceptors(conf map[string]map[string]any, unprotected []string, for name, c := range conf { new, ok := rgrpc.UnaryInterceptors[name] if !ok { - return nil, nil, fmt.Errorf("unary interceptor %s not found", name) + continue } inter, prio, err := new(c) if err != nil { @@ -100,7 +99,7 @@ func initGRPCInterceptors(conf map[string]map[string]any, unprotected []string, for name, c := range conf { new, ok := rgrpc.StreamInterceptors[name] if !ok { - return nil, nil, fmt.Errorf("stream interceptor %s not found", name) + continue } inter, prio, err := new(c) if err != nil { diff --git a/cmd/revad/runtime/http.go b/cmd/revad/runtime/http.go index c110d3ddaa..7568c174ea 100644 --- a/cmd/revad/runtime/http.go +++ b/cmd/revad/runtime/http.go @@ -19,7 +19,6 @@ package runtime import ( - "fmt" "path" "sort" @@ -44,7 +43,7 @@ func initHTTPMiddlewares(conf map[string]map[string]any, unprotected []string, l for name, c := range conf { new, ok := global.NewMiddlewares[name] if !ok { - return nil, fmt.Errorf("http middleware %s not found", name) + continue } m, prio, err := new(c) if err != nil { From 5c7cad15b5d916f32c065a0b043190b1dda12605 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Thu, 29 Jun 2023 17:51:58 +0200 Subject: [PATCH 67/76] add comments --- cmd/revad/pkg/config/common.go | 11 +++++++- cmd/revad/pkg/config/config.go | 9 +++++++ cmd/revad/pkg/config/lookup.go | 43 +++++++++++++++++++++++-------- cmd/revad/pkg/config/parser.go | 5 +++- cmd/revad/pkg/config/templates.go | 21 +++++++++++++++ cmd/revad/pkg/grace/grace.go | 6 ++++- cmd/revad/runtime/runtime.go | 5 ++++ 7 files changed, 86 insertions(+), 14 deletions(-) diff --git a/cmd/revad/pkg/config/common.go b/cmd/revad/pkg/config/common.go index bca984b24c..af584f0df6 100644 --- a/cmd/revad/pkg/config/common.go +++ b/cmd/revad/pkg/config/common.go @@ -46,6 +46,7 @@ type DriverConfig struct { Config map[string]any `key:",squash"` // this must be at the bottom! } +// Add appends the driver configuration to the given list of services. func (s *ServicesConfig) Add(domain, svc string, c *DriverConfig) { l := len(*s) if l == 0 { @@ -194,11 +195,19 @@ func networkForService(global string, cfg map[string]any) string { return global } +// Address is the data structure holding an address. type Address string +// ensure Address implements the Lookuper interface. +var _ Lookuper = (*Address)(nil) + +// String return the string representation of the address. func (a Address) String() string { return string(a) } -func (a Address) Get(k string) (any, error) { +// Get returns the value associated to the given key. +// The key available for an Address type are "port" and "ip", +// allowing respectively to get the port and the ip from the address. +func (a Address) Lookup(k string) (any, error) { switch k { case "port": t, err := net.ResolveTCPAddr("tcp", a.String()) diff --git a/cmd/revad/pkg/config/config.go b/cmd/revad/pkg/config/config.go index f9d71e8728..29b376843e 100644 --- a/cmd/revad/pkg/config/config.go +++ b/cmd/revad/pkg/config/config.go @@ -71,7 +71,11 @@ type Core struct { // can be references by other parts of the configuration. type Vars map[string]any +// Lookuper is the interface for getting the value +// associated with a given key. type Lookuper interface { + // Lookup get the value associated to thye given key. + // It returns ErrKeyNotFound if the key does not exists. Lookup(key string) (any, error) } @@ -123,6 +127,11 @@ func (c *Config) Dump() map[string]any { return dump } +// Lookup gets the value associated to the given key in the config. +// The key is in the form .[], allowing accessing +// recursively the config on subfields, in case of maps or structs or +// types implementing the Getter interface, or elements in a list by the +// given index. func (c *Config) Lookup(key string) (any, error) { // check thet key is valid, meaning it starts with one of // the fields of the config struct diff --git a/cmd/revad/pkg/config/lookup.go b/cmd/revad/pkg/config/lookup.go index 727a0ec974..7f1f3aff53 100644 --- a/cmd/revad/pkg/config/lookup.go +++ b/cmd/revad/pkg/config/lookup.go @@ -31,14 +31,19 @@ type ErrKeyNotFound struct { Key string } +// Error returns a string representation of the ErrKeyNotFound error. func (e ErrKeyNotFound) Error() string { return "key '" + e.Key + "' not found in the configuration" } -type Getter interface { - Get(k string) (any, error) -} - +// lookupStruct recursively looks up the key in the struct v. +// It panics if the value in v is not a struct. +// Only fields are allowed to be accessed. It bails out if +// an user wants to access by index. +// The struct is traversed considering the field tags. If the tag +// "key" is not specified for a field, the field is skipped in +// the lookup. If the tag specifies "squash", the field is treated +// as squashed. func lookupStruct(key string, v reflect.Value) (any, error) { if v.Kind() != reflect.Struct { panic("called lookupStruct on non struct type") @@ -107,11 +112,14 @@ func lookupStruct(key string, v reflect.Value) (any, error) { return nil, ErrKeyNotFound{Key: key} } -var typeGetter = reflect.TypeOf((*Getter)(nil)).Elem() +var typeLookuper = reflect.TypeOf((*Lookuper)(nil)).Elem() +// lookupByType recursively looks up the given key in v. func lookupByType(key string, v reflect.Value) (any, error) { - if v.Type().Implements(typeGetter) { - return lookupGetter(key, v) + if v.Type().Implements(typeLookuper) { + if v, err := lookupFromLookuper(key, v); err == nil { + return v, nil + } } switch v.Kind() { case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, @@ -130,10 +138,11 @@ func lookupByType(key string, v reflect.Value) (any, error) { panic("type not supported: " + v.Kind().String()) } -func lookupGetter(key string, v reflect.Value) (any, error) { - g, ok := v.Interface().(Getter) +// lookupFromLookuper looks up the key in a Lookup value. +func lookupFromLookuper(key string, v reflect.Value) (any, error) { + g, ok := v.Interface().(Lookuper) if !ok { - panic("called lookupGetter on type not implementing Getter interface") + panic("called lookupFromLookuper on type not implementing Lookup interface") } cmd, _, err := parseNext(key) @@ -149,9 +158,12 @@ func lookupGetter(key string, v reflect.Value) (any, error) { return nil, errors.New("call of index on getter type") } - return g.Get(c.Key) + return g.Lookup(c.Key) } +// lookupMap recursively looks up the given key in the map v. +// It panics if the value in v is not a map. +// Works similarly to lookupStruct. func lookupMap(key string, v reflect.Value) (any, error) { if v.Kind() != reflect.Map { panic("called lookupMap on non map type") @@ -179,6 +191,12 @@ func lookupMap(key string, v reflect.Value) (any, error) { return lookupByType(next, el) } +// lookupList recursively looks up the given key in the list v, +// in all the elements contained in the list. +// It panics if the value v is not a list. +// The elements can be addressed in general by index, but +// access by key is only allowed if the list contains exactly +// one element. func lookupList(key string, v reflect.Value) (any, error) { if v.Kind() != reflect.Slice && v.Kind() != reflect.Array { panic("called lookupList on non array/slice type") @@ -215,6 +233,9 @@ func lookupList(key string, v reflect.Value) (any, error) { return lookupByType(next, el) } +// lookupPrimitive gets the value from v. +// If the key tries to access by field or by index the value, +// an error is returned. func lookupPrimitive(key string, v reflect.Value) (any, error) { if v.Kind() != reflect.Bool && v.Kind() != reflect.Int && v.Kind() != reflect.Int8 && v.Kind() != reflect.Int16 && v.Kind() != reflect.Int32 && v.Kind() != reflect.Int64 && diff --git a/cmd/revad/pkg/config/parser.go b/cmd/revad/pkg/config/parser.go index 25976b804b..aa25de6e70 100644 --- a/cmd/revad/pkg/config/parser.go +++ b/cmd/revad/pkg/config/parser.go @@ -39,6 +39,9 @@ type FieldByIndex struct{ Index int } func (FieldByIndex) isCommand() {} +// parseNext reads the next token from the key and +// assings a command. +// If the key is empty io.EOF is returned. func parseNext(key string) (Command, string, error) { // key = ".grpc.services.authprovider[1].address" @@ -63,7 +66,7 @@ func parseNext(key string) (Command, string, error) { return FieldByIndex{Index: int(index)}, next, nil } - return nil, "", errors.New("parsing error: operator not recognised") + return nil, "", errors.New("parsing error: operator not recognised in key " + key) } func split(key string) (token string, next string) { diff --git a/cmd/revad/pkg/config/templates.go b/cmd/revad/pkg/config/templates.go index 1f9a47dc89..a61c66074a 100644 --- a/cmd/revad/pkg/config/templates.go +++ b/cmd/revad/pkg/config/templates.go @@ -26,6 +26,11 @@ import ( "strings" ) +// applyTemplateStruct applies recursively to all its fields all the template +// strings to the struct v. +// It panics if the value is not a struct. +// A field in the struct is skipped for applying all the templates +// if a tag "template" has teh value "-". func applyTemplateStruct(l Lookuper, p setter, v reflect.Value) error { if v.Kind() != reflect.Struct { panic("called applyTemplateStruct on non struct type") @@ -52,6 +57,7 @@ func applyTemplateStruct(l Lookuper, p setter, v reflect.Value) error { return nil } +// applyTemplateByType applies the template string to a generic type. func applyTemplateByType(l Lookuper, p setter, v reflect.Value) error { switch v.Kind() { case reflect.Array, reflect.Slice: @@ -70,6 +76,9 @@ func applyTemplateByType(l Lookuper, p setter, v reflect.Value) error { return nil } +// applyTemplateList recursively applies in all the elements of the list +// the template strings. +// It panics if the given value is not a list. func applyTemplateList(l Lookuper, p setter, v reflect.Value) error { if v.Kind() != reflect.Array && v.Kind() != reflect.Slice { panic("called applyTemplateList on non array/slice type") @@ -84,6 +93,9 @@ func applyTemplateList(l Lookuper, p setter, v reflect.Value) error { return nil } +// applyTemplateMap recursively applies in all the elements of the map +// the template strings. +// It panics if the given value is not a map. func applyTemplateMap(l Lookuper, p setter, v reflect.Value) error { if v.Kind() != reflect.Map { panic("called applyTemplateMap on non map type") @@ -100,6 +112,8 @@ func applyTemplateMap(l Lookuper, p setter, v reflect.Value) error { return nil } +// applyTemplateString applies to the string the template string, if any. +// It panics if the given value is not a string. func applyTemplateString(l Lookuper, p setter, v reflect.Value) error { if v.Kind() != reflect.String { panic("called applyTemplateString on non string type") @@ -130,6 +144,8 @@ func applyTemplateString(l Lookuper, p setter, v reflect.Value) error { return nil } +// applyTemplateInterface applies to the interface the template string, if any. +// It panics if the given value is not an interface. func applyTemplateInterface(l Lookuper, p setter, v reflect.Value) error { if v.Kind() != reflect.Interface { panic("called applyTemplateInterface on non interface value") @@ -220,6 +236,7 @@ func keyFromTemplate(s string) string { } type setter interface { + // SetValue sets the value v in a container. SetValue(v any) } @@ -238,15 +255,19 @@ type setterStruct struct { Field int } +// SetValue sets the value v in the element +// of the list. func (s setterList) SetValue(v any) { el := s.List.Index(s.Index) el.Set(reflect.ValueOf(v)) } +// SetValue sets the value v to the element of the map. func (s setterMap) SetValue(v any) { s.Map.SetMapIndex(reflect.ValueOf(s.Key), reflect.ValueOf(v)) } +// SetValue sets the value v to the field in the struct. func (s setterStruct) SetValue(v any) { s.Struct.Field(s.Field).Set(reflect.ValueOf(v)) } diff --git a/cmd/revad/pkg/grace/grace.go b/cmd/revad/pkg/grace/grace.go index ca6097d871..a4d5f08706 100644 --- a/cmd/revad/pkg/grace/grace.go +++ b/cmd/revad/pkg/grace/grace.go @@ -351,6 +351,7 @@ func get(lns map[string]net.Listener, address, network string) (net.Listener, bo return nil, false } +// Addressable is the interface for exposing address info. type Addressable interface { Network() string Address() string @@ -370,7 +371,10 @@ type Serverless interface { GracefulStop() error } -func (w *Watcher) SetServers(s []Server) { w.ss = s } +// SetServers sets the list of servers that have to be watched. +func (w *Watcher) SetServers(s []Server) { w.ss = s } + +// SetServerless sets the serverless that has to be watched. func (w *Watcher) SetServerless(s Serverless) { w.SL = s } // TrapSignals captures the OS signal. diff --git a/cmd/revad/runtime/runtime.go b/cmd/revad/runtime/runtime.go index f61396fd5c..12fc8e1bc3 100644 --- a/cmd/revad/runtime/runtime.go +++ b/cmd/revad/runtime/runtime.go @@ -41,6 +41,7 @@ import ( "golang.org/x/sync/errgroup" ) +// Reva represents a full instance of reva. type Reva struct { config *config.Config @@ -53,6 +54,7 @@ type Reva struct { log *zerolog.Logger } +// Server represents a reva server (grpc or http). type Server struct { server grace.Server listener net.Listener @@ -60,10 +62,12 @@ type Server struct { services map[string]any } +// Start starts the server listening on the assigned listener. func (s *Server) Start() error { return s.server.Start(s.listener) } +// New creates a new reva instance. func New(config *config.Config, opt ...Option) (*Reva, error) { opts := newOptions(opt...) log := opts.Logger @@ -239,6 +243,7 @@ func groupHTTPByAddress(cfg *config.Config) []*config.HTTP { return l } +// Start starts all the reva services and waits for a signal. func (r *Reva) Start() error { defer r.watcher.Clean() r.watcher.SetServers(list.Map(r.servers, func(s *Server) grace.Server { return s.server })) From 896d393828fc5a2af041cf0ce9d5eba38989a6b8 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Fri, 30 Jun 2023 09:28:41 +0200 Subject: [PATCH 68/76] fix invalid templates --- cmd/revad/pkg/config/config.go | 13 ++++++++++--- cmd/revad/pkg/config/lookup.go | 2 +- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/cmd/revad/pkg/config/config.go b/cmd/revad/pkg/config/config.go index 29b376843e..eb979c201d 100644 --- a/cmd/revad/pkg/config/config.go +++ b/cmd/revad/pkg/config/config.go @@ -22,7 +22,6 @@ import ( "fmt" "io" "reflect" - "strings" "github.com/BurntSushi/toml" "github.com/creasty/defaults" @@ -146,15 +145,23 @@ func (c *Config) Lookup(key string) (any, error) { } func (c *Config) isValidKey(key string) bool { + cmd, _, err := parseNext(key) + if err != nil { + return false + } + f, ok := cmd.(FieldByKey) + if !ok { + return false + } + k := f.Key e := reflect.TypeOf(c).Elem() - k := strings.TrimPrefix(key, ".") for i := 0; i < e.NumField(); i++ { f := e.Field(i) prefix := f.Tag.Get("key") if prefix == "" || prefix == "-" { continue } - if strings.HasPrefix(k, prefix) { + if k == prefix { return true } } diff --git a/cmd/revad/pkg/config/lookup.go b/cmd/revad/pkg/config/lookup.go index 7f1f3aff53..5edb2a98d6 100644 --- a/cmd/revad/pkg/config/lookup.go +++ b/cmd/revad/pkg/config/lookup.go @@ -117,7 +117,7 @@ var typeLookuper = reflect.TypeOf((*Lookuper)(nil)).Elem() // lookupByType recursively looks up the given key in v. func lookupByType(key string, v reflect.Value) (any, error) { if v.Type().Implements(typeLookuper) { - if v, err := lookupFromLookuper(key, v); err == nil { + if v, err := lookupFromLookuper(key, v); err == nil && v != nil { return v, nil } } From bbb694ec672b78c7ce6edf7b588fc82af3d77c26 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Fri, 30 Jun 2023 09:34:48 +0200 Subject: [PATCH 69/76] fix linter --- cmd/revad/pkg/config/common.go | 2 +- cmd/revad/pkg/config/templates.go | 4 ++-- cmd/revad/runtime/runtime.go | 2 +- pkg/utils/net/net.go | 21 +++++++++++++++++++++ 4 files changed, 25 insertions(+), 4 deletions(-) diff --git a/cmd/revad/pkg/config/common.go b/cmd/revad/pkg/config/common.go index af584f0df6..fa4ce3148b 100644 --- a/cmd/revad/pkg/config/common.go +++ b/cmd/revad/pkg/config/common.go @@ -36,7 +36,7 @@ type iterableImpl struct{ i iterable } type ServicesConfig []*DriverConfig // DriversNumber return the number of driver configured for the service. -func (c ServicesConfig) DriversNumber() int { return len(c) } +func (s ServicesConfig) DriversNumber() int { return len(s) } // DriverConfig holds the configuration for a driver. type DriverConfig struct { diff --git a/cmd/revad/pkg/config/templates.go b/cmd/revad/pkg/config/templates.go index a61c66074a..e12cdaaa13 100644 --- a/cmd/revad/pkg/config/templates.go +++ b/cmd/revad/pkg/config/templates.go @@ -30,7 +30,7 @@ import ( // strings to the struct v. // It panics if the value is not a struct. // A field in the struct is skipped for applying all the templates -// if a tag "template" has teh value "-". +// if a tag "template" has the value "-". func applyTemplateStruct(l Lookuper, p setter, v reflect.Value) error { if v.Kind() != reflect.Struct { panic("called applyTemplateStruct on non struct type") @@ -214,7 +214,7 @@ func convertToString(val any) (string, bool) { case uint32: return strconv.FormatUint(uint64(v), 10), true case uint64: - return strconv.FormatUint(uint64(v), 10), true + return strconv.FormatUint(v, 10), true case bool: return strconv.FormatBool(v), true } diff --git a/cmd/revad/runtime/runtime.go b/cmd/revad/runtime/runtime.go index 12fc8e1bc3..1116638afe 100644 --- a/cmd/revad/runtime/runtime.go +++ b/cmd/revad/runtime/runtime.go @@ -200,7 +200,7 @@ func groupGRPCByAddress(cfg *config.Config) []*config.GRPC { cfg.GRPC.ForEachService(func(s *config.Service) { if _, ok := g[s.Address.String()]; !ok { g[s.Address.String()] = &config.GRPC{ - Address: config.Address(s.Address), + Address: s.Address, Network: s.Network, ShutdownDeadline: cfg.GRPC.ShutdownDeadline, EnableReflection: cfg.GRPC.EnableReflection, diff --git a/pkg/utils/net/net.go b/pkg/utils/net/net.go index 46646fe6af..354367a289 100644 --- a/pkg/utils/net/net.go +++ b/pkg/utils/net/net.go @@ -1,7 +1,28 @@ +// Copyright 2018-2023 CERN +// +// 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. +// +// In applying this license, CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + package net import "net" +// AddressEqual return true if the addresses are equal. +// For tpc addressess only the port is compared, for unix +// the name and net are compared. func AddressEqual(a net.Addr, network, address string) bool { if a.Network() != network { return false From 38fdb586c95ff791a417a4b5580eebaba2ed3d79 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Fri, 30 Jun 2023 09:38:55 +0200 Subject: [PATCH 70/76] add changelog --- changelog/unreleased/new-config.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 changelog/unreleased/new-config.md diff --git a/changelog/unreleased/new-config.md b/changelog/unreleased/new-config.md new file mode 100644 index 0000000000..9f9f1f0124 --- /dev/null +++ b/changelog/unreleased/new-config.md @@ -0,0 +1,11 @@ +Enhancement: New configuration + +Allow multiple driverts of the same service to be in the +same toml config. Add a `vars` section to contain common +parameters addressable using templates in the configuration +of the different drivers. Support templating to reference +values of other parameters in the configuration. +Assign random ports to services where the address is not +specified. + +https://github.com/cs3org/reva/pull/4015 From 1a3869cff7e2b5aafa6cb42ad22668870870e220 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Fri, 30 Jun 2023 09:40:38 +0200 Subject: [PATCH 71/76] fix typo --- pkg/utils/net/net.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/utils/net/net.go b/pkg/utils/net/net.go index 354367a289..51b94117f0 100644 --- a/pkg/utils/net/net.go +++ b/pkg/utils/net/net.go @@ -21,7 +21,7 @@ package net import "net" // AddressEqual return true if the addresses are equal. -// For tpc addressess only the port is compared, for unix +// For tpc addresses only the port is compared, for unix // the name and net are compared. func AddressEqual(a net.Addr, network, address string) bool { if a.Network() != network { From 09b0aef64b3529ee3657e0e1310ccad8232a69b3 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Fri, 30 Jun 2023 09:45:01 +0200 Subject: [PATCH 72/76] skip no config templates --- cmd/revad/pkg/config/templates.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cmd/revad/pkg/config/templates.go b/cmd/revad/pkg/config/templates.go index e12cdaaa13..d4d3e4ae52 100644 --- a/cmd/revad/pkg/config/templates.go +++ b/cmd/revad/pkg/config/templates.go @@ -130,6 +130,9 @@ func applyTemplateString(l Lookuper, p setter, v reflect.Value) error { if err != nil { return err } + if val == nil { + return nil + } new, err := replaceTemplate(s, tmpl, val) if err != nil { @@ -167,6 +170,9 @@ func applyTemplateInterface(l Lookuper, p setter, v reflect.Value) error { if err != nil { return err } + if val == nil { + return nil + } new, err := replaceTemplate(s, tmpl, val) if err != nil { From 0bf5c7b1820099f6688eb27c1322c2211f855b4c Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Fri, 30 Jun 2023 10:31:30 +0200 Subject: [PATCH 73/76] improved logging for http services --- cmd/revad/runtime/runtime.go | 7 ++++--- pkg/rhttp/rhttp.go | 6 ++++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/cmd/revad/runtime/runtime.go b/cmd/revad/runtime/runtime.go index 1116638afe..7496c7634f 100644 --- a/cmd/revad/runtime/runtime.go +++ b/cmd/revad/runtime/runtime.go @@ -383,17 +383,18 @@ func newServers(grpc []*config.GRPC, http []*config.HTTP, lns map[string]net.Lis servers = append(servers, server) } for _, cfg := range http { - services, err := rhttp.InitServices(cfg.Services) + log := log.With().Str("pkg", "http").Logger() + services, err := rhttp.InitServices(cfg.Services, &log) if err != nil { return nil, err } - middlewares, err := initHTTPMiddlewares(cfg.Middlewares, httpUnprotected(services), log) + middlewares, err := initHTTPMiddlewares(cfg.Middlewares, httpUnprotected(services), &log) if err != nil { return nil, err } s, err := rhttp.New( rhttp.WithServices(services), - rhttp.WithLogger(log.With().Str("pkg", "http").Logger()), + rhttp.WithLogger(log), rhttp.WithCertAndKeyFiles(cfg.CertFile, cfg.KeyFile), rhttp.WithMiddlewares(middlewares), ) diff --git a/pkg/rhttp/rhttp.go b/pkg/rhttp/rhttp.go index 92984dada9..e5d796f917 100644 --- a/pkg/rhttp/rhttp.go +++ b/pkg/rhttp/rhttp.go @@ -62,7 +62,7 @@ func WithLogger(log zerolog.Logger) Config { } } -func InitServices(services map[string]config.ServicesConfig) (map[string]global.Service, error) { +func InitServices(services map[string]config.ServicesConfig, log *zerolog.Logger) (map[string]global.Service, error) { s := make(map[string]global.Service) for name, cfg := range services { new, ok := global.Services[name] @@ -72,7 +72,7 @@ func InitServices(services map[string]config.ServicesConfig) (map[string]global. if cfg.DriversNumber() > 1 { return nil, fmt.Errorf("service %s cannot have more than one driver in the same server", name) } - log := zerolog.Nop() // TODO: pass correct log + log := log.With().Str("service", name).Logger() svc, err := new(cfg[0].Config, &log) if err != nil { return nil, errors.Wrapf(err, "http service %s could not be started", name) @@ -86,6 +86,7 @@ func InitServices(services map[string]config.ServicesConfig) (map[string]global. func New(c ...Config) (*Server, error) { httpServer := &http.Server{} s := &Server{ + log: zerolog.Nop(), httpServer: httpServer, svcs: map[string]global.Service{}, unprotected: []string{}, @@ -182,6 +183,7 @@ func (s *Server) registerServices() { s.handlers[svc.Prefix()] = h s.svcs[svc.Prefix()] = svc s.unprotected = append(s.unprotected, getUnprotected(svc.Prefix(), svc.Unprotected())...) + s.log.Info().Msgf("http service enabled: %s@/%s", name, svc.Prefix()) } } From aae8e9365a80150c3bf99427b4cdb93663ad5c5c Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Fri, 30 Jun 2023 10:44:34 +0200 Subject: [PATCH 74/76] enable tracing from config --- cmd/revad/pkg/config/config.go | 4 ++-- cmd/revad/runtime/runtime.go | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/cmd/revad/pkg/config/config.go b/cmd/revad/pkg/config/config.go index eb979c201d..846b03374e 100644 --- a/cmd/revad/pkg/config/config.go +++ b/cmd/revad/pkg/config/config.go @@ -59,8 +59,8 @@ type Shared struct { // Core holds the core configuration. type Core struct { MaxCPUs string `key:"max_cpus" mapstructure:"max_cpus"` - TracingEnabled bool `key:"tracing_enabled" mapstructure:"tracing_enabled"` - TracingEndpoint string `key:"tracing_endpoint" mapstructure:"tracing_endpoint"` + TracingEnabled bool `key:"tracing_enabled" mapstructure:"tracing_enabled" default:"true"` + TracingEndpoint string `key:"tracing_endpoint" mapstructure:"tracing_endpoint" default:"localhost:6831"` TracingCollector string `key:"tracing_collector" mapstructure:"tracing_collector"` TracingServiceName string `key:"tracing_service_name" mapstructure:"tracing_service_name"` TracingService string `key:"tracing_service" mapstructure:"tracing_service"` diff --git a/cmd/revad/runtime/runtime.go b/cmd/revad/runtime/runtime.go index 7496c7634f..3a4b601109 100644 --- a/cmd/revad/runtime/runtime.go +++ b/cmd/revad/runtime/runtime.go @@ -34,6 +34,7 @@ import ( "github.com/cs3org/reva/pkg/rhttp/global" "github.com/cs3org/reva/pkg/rserverless" "github.com/cs3org/reva/pkg/sharedconf" + rtrace "github.com/cs3org/reva/pkg/trace" "github.com/cs3org/reva/pkg/utils/list" "github.com/cs3org/reva/pkg/utils/maps" netutil "github.com/cs3org/reva/pkg/utils/net" @@ -75,6 +76,7 @@ func New(config *config.Config, opt ...Option) (*Reva, error) { if err := initCPUCount(config.Core, log); err != nil { return nil, err } + initTracing(config.Core) if opts.PidFile == "" { return nil, errors.New("pid file not provided") @@ -300,6 +302,12 @@ func handlePIDFlag(l *zerolog.Logger, pidFile string) (*grace.Watcher, error) { return w, nil } +func initTracing(conf *config.Core) { + if conf.TracingEnabled { + rtrace.SetTraceProvider(conf.TracingCollector, conf.TracingEndpoint, conf.TracingServiceName) + } +} + // adjustCPU parses string cpu and sets GOMAXPROCS // according to its value. It accepts either // a number (e.g. 3) or a percent (e.g. 50%). From f13ae92d8983afa7324c838618514ccc4d743916 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Fri, 30 Jun 2023 12:14:54 +0200 Subject: [PATCH 75/76] fix unit tests --- cmd/revad/pkg/config/config_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cmd/revad/pkg/config/config_test.go b/cmd/revad/pkg/config/config_test.go index aa2395b02e..7ab22f8a03 100644 --- a/cmd/revad/pkg/config/config_test.go +++ b/cmd/revad/pkg/config/config_test.go @@ -245,8 +245,9 @@ nats_token = "secret-token-example"` }, c2.Log) assert.Equal(t, &Core{ - MaxCPUs: "1", - TracingEnabled: true, + MaxCPUs: "1", + TracingEnabled: true, + TracingEndpoint: "localhost:6831", }, c2.Core) assert.Equal(t, Vars{ From e4fcad31744221ac73a5078278679c03503f88a5 Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte Date: Fri, 30 Jun 2023 14:07:48 +0200 Subject: [PATCH 76/76] fix restart of revad process --- cmd/revad/main.go | 2 +- cmd/revad/pkg/grace/grace.go | 34 +++++++++++++++++++++++++++------- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/cmd/revad/main.go b/cmd/revad/main.go index 778da99a73..991073ace7 100644 --- a/cmd/revad/main.go +++ b/cmd/revad/main.go @@ -133,7 +133,7 @@ func handleSignalFlag() { // kill process with signal if err := process.Signal(signal); err != nil { - fmt.Fprintf(os.Stderr, "error signaling process %d with signal %s\n", process.Pid, signal) + fmt.Fprintf(os.Stderr, "error signaling process %d with signal %s: %v\n", process.Pid, signal, err) os.Exit(1) } diff --git a/cmd/revad/pkg/grace/grace.go b/cmd/revad/pkg/grace/grace.go index a4d5f08706..6640a40484 100644 --- a/cmd/revad/pkg/grace/grace.go +++ b/cmd/revad/pkg/grace/grace.go @@ -227,7 +227,7 @@ func inheritedListeners() map[string]net.Listener { if len(s) != 2 { continue } - svcname := s[0] + svcname := strings.ToLower(s[0]) fd, err := strconv.ParseUint(s[1], 10, 64) if err != nil { continue @@ -263,6 +263,8 @@ func (w *Watcher) GetListeners(servers map[string]Addressable) (map[string]net.L w.log.Info().Msg("graceful restart, inheriting parent listener fds for grpc and http services") inherited := inheritedListeners() + logListeners(inherited, "inherited", &w.log) + for svc, ln := range inherited { addr, ok := servers[svc] if !ok { @@ -270,7 +272,8 @@ func (w *Watcher) GetListeners(servers map[string]Addressable) (map[string]net.L } // for services with random addresses, check and assign if available from inherited // from the assigned addresses, assing the listener if address correspond - if isRandomAddress(addr.Address()) || addr.Address() == ln.Addr().String() { // TODO: check which is the host here + if isRandomAddress(addr.Address()) || + netutil.AddressEqual(ln.Addr(), addr.Network(), addr.Address()) { lns[svc] = ln } } @@ -278,23 +281,32 @@ func (w *Watcher) GetListeners(servers map[string]Addressable) (map[string]net.L // close all the listeners not used from inherited for svc, ln := range inherited { if _, ok := lns[svc]; !ok { + w.log.Debug().Msgf("closing inherited listener %s:%s for service %s", ln.Addr().Network(), ln.Addr().String(), svc) if err := ln.Close(); err != nil { - w.log.Error().Err(err).Msgf("error closing inherited listener %s", ln.Addr().String()) + w.log.Error().Err(err).Msgf("error closing inherited listener %s:%s", ln.Addr().Network(), ln.Addr().String()) return nil, errors.Wrap(err, "error closing inherited listener") } } } + var err error // create assigned/random listeners for the missing services - for svc, addr := range servers { + for svc, a := range servers { _, ok := lns[svc] if ok { continue } - a := getAddress(addr.Address()) - ln, err := newListener(addr.Network(), a) + network, addr := a.Network(), getAddress(a.Address()) + // multiple services may have the same listener + ln, ok := get(lns, addr, network) + if !ok { + ln, err = newListener(network, addr) + if err != nil { + return nil, err + } + } if err != nil { - w.log.Error().Err(err).Msgf("error getting listener on %s", a) + w.log.Error().Err(err).Msgf("error getting listener on %s", addr) return nil, errors.Wrap(err, "error getting listener") } lns[svc] = ln @@ -342,6 +354,14 @@ func (w *Watcher) GetListeners(servers map[string]Addressable) (map[string]net.L return lns, nil } +func logListeners(lns map[string]net.Listener, info string, log *zerolog.Logger) { + r := make(map[string]string, len(lns)) + for n, ln := range lns { + r[n] = fmt.Sprintf("%s:%s", ln.Addr().Network(), ln.Addr().String()) + } + log.Debug().Interface(info, r).Send() +} + func get(lns map[string]net.Listener, address, network string) (net.Listener, bool) { for _, ln := range lns { if netutil.AddressEqual(ln.Addr(), network, address) {