diff --git a/config-parser/generate/config-file.go b/config-parser/generate/config-file.go index 0b9420ac..323f0904 100644 --- a/config-parser/generate/config-file.go +++ b/config-parser/generate/config-file.go @@ -285,7 +285,7 @@ func (c *ConfigFile) StringFiles(baseFolder string) { } usedNiceNames[niceName] = struct{}{} sectionName := " test" - if sectionType == "global" { + if sectionType == "global" || sectionType == "traces" { sectionName = "" } oneTest := "const " + niceName + " = `\n" + sectionType + sectionName + "\n" + " " + line + "\n`" + "\n" diff --git a/config-parser/init.go b/config-parser/init.go index f510a2c6..d378ef94 100644 --- a/config-parser/init.go +++ b/config-parser/init.go @@ -45,6 +45,7 @@ type ConfiguredParsers struct { LogForward *Parsers FCGIApp *Parsers CrtStore *Parsers + Traces *Parsers // spoe parsers SPOEAgent *Parsers SPOEGroup *Parsers @@ -95,4 +96,5 @@ func (p *configParser) initParserMaps() { p.Parsers[LogForward] = map[string]*Parsers{} p.Parsers[FCGIApp] = map[string]*Parsers{} p.Parsers[CrtStore] = map[string]*Parsers{} + p.Parsers[Traces] = map[string]*Parsers{} } diff --git a/config-parser/parser.go b/config-parser/parser.go index 8c0f92c4..5171072b 100644 --- a/config-parser/parser.go +++ b/config-parser/parser.go @@ -47,6 +47,7 @@ const ( LogForward Section = "log-forward" FCGIApp Section = "fcgi-app" CrtStore Section = "crt-store" + Traces Section = "traces" // spoe sections SPOEAgent Section = "spoe-agent" SPOEGroup Section = "spoe-group" @@ -56,6 +57,7 @@ const ( const ( CommentsSectionName = "data" GlobalSectionName = "data" + TracesSectionName = "" ) var DefaultSectionName = "" //nolint:gochecknoglobals diff --git a/config-parser/parsers/trace.go b/config-parser/parsers/trace.go new file mode 100644 index 00000000..268d47bd --- /dev/null +++ b/config-parser/parsers/trace.go @@ -0,0 +1,58 @@ +/* +Copyright 2024 HAProxy Technologies + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package parsers + +import ( + "github.com/haproxytech/client-native/v6/config-parser/common" + "github.com/haproxytech/client-native/v6/config-parser/errors" + "github.com/haproxytech/client-native/v6/config-parser/types" +) + +type Trace struct { + data []types.Trace + preComments []string // comments that appear before the actual line +} + +func (t *Trace) parse(line string, parts []string, comment string) (*types.Trace, error) { + if parts[0] != t.GetParserName() { + return nil, &errors.ParseError{Parser: t.GetParserName(), Line: line} + } + if len(parts) < 2 { + return nil, &errors.ParseError{Parser: t.GetParserName(), Line: line, Message: "Parse error: not enough arguments"} + } + + return &types.Trace{ + Params: parts[1:], + Comment: comment, + }, nil +} + +func (t *Trace) Result() ([]common.ReturnResultLine, error) { + if len(t.data) == 0 { + return nil, errors.ErrFetch + } + + result := make([]common.ReturnResultLine, len(t.data)) + + for i, trace := range t.data { + result[i] = common.ReturnResultLine{ + Data: t.GetParserName() + " " + common.SmartJoin(trace.Params...), + Comment: trace.Comment, + } + } + return result, nil +} diff --git a/config-parser/parsers/trace_generated.go b/config-parser/parsers/trace_generated.go new file mode 100644 index 00000000..860abe09 --- /dev/null +++ b/config-parser/parsers/trace_generated.go @@ -0,0 +1,157 @@ +// Code generated by go generate; DO NOT EDIT. +/* +Copyright 2019 HAProxy Technologies + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package parsers + +import ( + "github.com/haproxytech/client-native/v6/config-parser/common" + "github.com/haproxytech/client-native/v6/config-parser/errors" + "github.com/haproxytech/client-native/v6/config-parser/types" +) + +func (p *Trace) Init() { + p.data = []types.Trace{} + p.preComments = []string{} +} + +func (p *Trace) GetParserName() string { + return "trace" +} + +func (p *Trace) Get(createIfNotExist bool) (common.ParserData, error) { + if len(p.data) == 0 && !createIfNotExist { + return nil, errors.ErrFetch + } + return p.data, nil +} + +func (p *Trace) GetPreComments() ([]string, error) { + return p.preComments, nil +} + +func (p *Trace) SetPreComments(preComments []string) { + p.preComments = preComments +} + +func (p *Trace) GetOne(index int) (common.ParserData, error) { + if index < 0 || index >= len(p.data) { + return nil, errors.ErrFetch + } + return p.data[index], nil +} + +func (p *Trace) Delete(index int) error { + if index < 0 || index >= len(p.data) { + return errors.ErrFetch + } + copy(p.data[index:], p.data[index+1:]) + p.data[len(p.data)-1] = types.Trace{} + p.data = p.data[:len(p.data)-1] + return nil +} + +func (p *Trace) Insert(data common.ParserData, index int) error { + if data == nil { + return errors.ErrInvalidData + } + switch newValue := data.(type) { + case []types.Trace: + p.data = newValue + case *types.Trace: + if index > -1 { + if index > len(p.data) { + return errors.ErrIndexOutOfRange + } + p.data = append(p.data, types.Trace{}) + copy(p.data[index+1:], p.data[index:]) + p.data[index] = *newValue + } else { + p.data = append(p.data, *newValue) + } + case types.Trace: + if index > -1 { + if index > len(p.data) { + return errors.ErrIndexOutOfRange + } + p.data = append(p.data, types.Trace{}) + copy(p.data[index+1:], p.data[index:]) + p.data[index] = newValue + } else { + p.data = append(p.data, newValue) + } + default: + return errors.ErrInvalidData + } + return nil +} + +func (p *Trace) Set(data common.ParserData, index int) error { + if data == nil { + p.Init() + return nil + } + switch newValue := data.(type) { + case []types.Trace: + p.data = newValue + case *types.Trace: + if index > -1 && index < len(p.data) { + p.data[index] = *newValue + } else if index == -1 { + p.data = append(p.data, *newValue) + } else { + return errors.ErrIndexOutOfRange + } + case types.Trace: + if index > -1 && index < len(p.data) { + p.data[index] = newValue + } else if index == -1 { + p.data = append(p.data, newValue) + } else { + return errors.ErrIndexOutOfRange + } + default: + return errors.ErrInvalidData + } + return nil +} + +func (p *Trace) PreParse(line string, parts []string, preComments []string, comment string) (string, error) { + changeState, err := p.Parse(line, parts, comment) + if err == nil && preComments != nil { + p.preComments = append(p.preComments, preComments...) + } + return changeState, err +} + +func (p *Trace) Parse(line string, parts []string, comment string) (string, error) { + if parts[0] == "trace" { + data, err := p.parse(line, parts, comment) + if err != nil { + if _, ok := err.(*errors.ParseError); ok { + return "", err + } + return "", &errors.ParseError{Parser: "Trace", Line: line} + } + p.data = append(p.data, *data) + return "", nil + } + return "", &errors.ParseError{Parser: "Trace", Line: line} +} + +func (p *Trace) ResultAll() ([]common.ReturnResultLine, []string, error) { + res, err := p.Result() + return res, p.preComments, err +} diff --git a/config-parser/reader.go b/config-parser/reader.go index f6d93f4e..d5c3346f 100644 --- a/config-parser/reader.go +++ b/config-parser/reader.go @@ -352,6 +352,15 @@ func (p *configParser) ProcessLine(line string, parts []string, comment string, if p.Options.Log { p.Options.Logger.Tracef("%scrt-store section %s active", p.Options.LogPrefix, data.Name) } + case "traces": + if config.Traces == nil { + config.Traces = p.getTracesParser() + p.Parsers[Traces][TracesSectionName] = config.Traces + if p.Options.Log { + p.Options.Logger.Tracef("%straces section active", p.Options.LogPrefix) + } + } + config.Active = config.Traces case "snippet_beg": config.Previous = config.Active config.Active = &Parsers{ diff --git a/config-parser/section-parsers.go b/config-parser/section-parsers.go index 413429cc..c9a44760 100644 --- a/config-parser/section-parsers.go +++ b/config-parser/section-parsers.go @@ -50,6 +50,7 @@ func (p *configParser) createParsers(parser map[string]ParserInterface, sequence addParser(parser, &sequence, &extra.Section{Name: "log-forward"}) addParser(parser, &sequence, &extra.Section{Name: "fcgi-app"}) addParser(parser, &sequence, &extra.Section{Name: "crt-store"}) + addParser(parser, &sequence, &extra.Section{Name: "traces"}) if !p.Options.DisableUnProcessed { addParser(parser, &sequence, &extra.UnProcessed{}) } @@ -978,3 +979,10 @@ func (p *configParser) getCrtStoreParser() *Parsers { addParser(parser, &sequence, &parsers.LoadCert{}) return p.createParsers(parser, sequence) } + +func (p *configParser) getTracesParser() *Parsers { + parser := map[string]ParserInterface{} + sequence := []Section{} + addParser(parser, &sequence, &parsers.Trace{}) + return p.createParsers(parser, sequence) +} diff --git a/config-parser/tests/configs/haproxy.cfg.go b/config-parser/tests/configs/haproxy.cfg.go index c624090c..9eb6d294 100644 --- a/config-parser/tests/configs/haproxy.cfg.go +++ b/config-parser/tests/configs/haproxy.cfg.go @@ -181,6 +181,10 @@ cache foobar total-max-size 4 max-age 240 +traces + trace h1 sink buf1 level developer verbosity complete start now + trace h2 sink buf2 level developer verbosity complete start now + crt-store tpm2 crt-base /c key-base /k diff --git a/config-parser/tests/configs/haproxy_generated.cfg.go b/config-parser/tests/configs/haproxy_generated.cfg.go index d6bd5d97..5ba15e68 100644 --- a/config-parser/tests/configs/haproxy_generated.cfg.go +++ b/config-parser/tests/configs/haproxy_generated.cfg.go @@ -2915,6 +2915,9 @@ resolvers test nameserver dns1 10.0.0.1:53 nameserver dns1 10.0.0.1:53 # comment +traces test + trace h1 sink buf1 level developer verbosity complete start now + userlist test group G1 users tiger,scott group G1 @@ -4091,6 +4094,8 @@ var configTests = []configTest{{` command spoa-mirror --runtime 0 --mirror-url {` load crt foo.pem alias foo.com key foo.priv.key ocsp foo.ocsp.der issuer foo.issuer.pem sctl foo.sctl ocsp-update on `, 1}, {` load crt foo.pem alias foo.com key foo.priv.key ocsp foo.ocsp.der issuer foo.issuer.pem sctl foo.sctl ocsp-update off +`, 1}, + {` trace h1 sink buf1 level developer verbosity complete start now `, 1}, {` http-request set-map(map.lst) %[src] %[req.hdr(X-Value)] if value `, 3}, diff --git a/config-parser/tests/integration/traces_data_test.go b/config-parser/tests/integration/traces_data_test.go new file mode 100644 index 00000000..a67ac81c --- /dev/null +++ b/config-parser/tests/integration/traces_data_test.go @@ -0,0 +1,23 @@ +// Code generated by go generate; DO NOT EDIT. +/* +Copyright 2019 HAProxy Technologies + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package integration_test + +const traces_traceh1sinkbuf1leveldeveloperver = ` +traces + trace h1 sink buf1 level developer verbosity complete start now +` diff --git a/config-parser/tests/integration/traces_test.go b/config-parser/tests/integration/traces_test.go new file mode 100644 index 00000000..bf1ee784 --- /dev/null +++ b/config-parser/tests/integration/traces_test.go @@ -0,0 +1,56 @@ +// Code generated by go generate; DO NOT EDIT. +/* +Copyright 2019 HAProxy Technologies + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package integration_test + +import ( + "bytes" + "testing" + + parser "github.com/haproxytech/client-native/v6/config-parser" + "github.com/haproxytech/client-native/v6/config-parser/options" +) + +func TestWholeConfigsSectionsTraces(t *testing.T) { + t.Parallel() + tests := []struct { + Name, Config string + }{ + {"traces_traceh1sinkbuf1leveldeveloperver", traces_traceh1sinkbuf1leveldeveloperver}, + } + for _, config := range tests { + t.Run(config.Name, func(t *testing.T) { + t.Parallel() + var buffer bytes.Buffer + buffer.WriteString(config.Config) + p, err := parser.New(options.Reader(&buffer)) + if err != nil { + t.Fatalf(err.Error()) + } + result := p.String() + if result != config.Config { + compare(t, config.Config, result) + t.Error("======== ORIGINAL =========") + t.Error(config.Config) + t.Error("======== RESULT ===========") + t.Error(result) + t.Error("===========================") + t.Fatalf("configurations does not match") + } + }) + } +} diff --git a/config-parser/tests/trace_generated_test.go b/config-parser/tests/trace_generated_test.go new file mode 100644 index 00000000..85b08686 --- /dev/null +++ b/config-parser/tests/trace_generated_test.go @@ -0,0 +1,82 @@ +// Code generated by go generate; DO NOT EDIT. +/* +Copyright 2019 HAProxy Technologies + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tests + +import ( + "fmt" + "strings" + "testing" + + "github.com/haproxytech/client-native/v6/config-parser/parsers" +) + +func TestTrace(t *testing.T) { + tests := map[string]bool{ + "trace h1 sink buf1 level developer verbosity complete start now": true, + "trace": false, + "---": false, + "--- ---": false, + } + parser := &parsers.Trace{} + for command, shouldPass := range tests { + t.Run(command, func(t *testing.T) { + line := strings.TrimSpace(command) + lines := strings.SplitN(line, "\n", -1) + var err error + parser.Init() + if len(lines) > 1 { + for _, line = range lines { + line = strings.TrimSpace(line) + if err = ProcessLine(line, parser); err != nil { + break + } + } + } else { + err = ProcessLine(line, parser) + } + if shouldPass { + if err != nil { + t.Errorf(err.Error()) + return + } + result, err := parser.Result() + if err != nil { + t.Errorf(err.Error()) + return + } + var returnLine string + if result[0].Comment == "" { + returnLine = result[0].Data + } else { + returnLine = fmt.Sprintf("%s # %s", result[0].Data, result[0].Comment) + } + if command != returnLine { + t.Errorf(fmt.Sprintf("error: has [%s] expects [%s]", returnLine, command)) + } + } else { + if err == nil { + t.Errorf(fmt.Sprintf("error: did not throw error for line [%s]", line)) + } + _, parseErr := parser.Result() + if parseErr == nil { + t.Errorf(fmt.Sprintf("error: did not throw error on result for line [%s]", line)) + } + } + }) + } +} diff --git a/config-parser/types/types.go b/config-parser/types/types.go index 1af96424..15b6ab3c 100644 --- a/config-parser/types/types.go +++ b/config-parser/types/types.go @@ -1656,3 +1656,13 @@ type LoadCert struct { Comment string OcspUpdate *bool } + +//sections:traces +//name:trace +//is:multiple +//test:ok:trace h1 sink buf1 level developer verbosity complete start now +//test:fail:trace +type Trace struct { + Params []string + Comment string +} diff --git a/config-parser/writer.go b/config-parser/writer.go index 8bcba760..68507021 100644 --- a/config-parser/writer.go +++ b/config-parser/writer.go @@ -41,7 +41,7 @@ func (p *configParser) String() string { p.writeParsers("", p.Parsers[Comments][CommentsSectionName], &result, false) p.writeParsers("global", p.Parsers[Global][GlobalSectionName], &result, true) - sections := []Section{Defaults, UserList, Peers, Mailers, Resolvers, Cache, Ring, LogForward, HTTPErrors, CrtStore, Frontends, Backends, Listen, Program, FCGIApp} + sections := []Section{Defaults, UserList, Peers, Mailers, Resolvers, Cache, Ring, Traces, LogForward, HTTPErrors, CrtStore, Frontends, Backends, Listen, Program, FCGIApp} for _, section := range sections { var sortedSections []string diff --git a/configuration/configuration.go b/configuration/configuration.go index 421448da..03a8faf2 100644 --- a/configuration/configuration.go +++ b/configuration/configuration.go @@ -48,6 +48,7 @@ const ( FCGIAppParentName = "fcgi-app" ResolverParentName = "resolvers" CrtStoreParentName = "crt-store" + TracesParentName = "traces" ) // ClientParams is just a placeholder for all client options diff --git a/configuration/interface.go b/configuration/interface.go index 4991c03a..eb1d03e5 100644 --- a/configuration/interface.go +++ b/configuration/interface.go @@ -71,6 +71,7 @@ type Configuration interface { TCPCheck TCPRequestRule TCPResponseRule + Traces Transactions QUICInitialRule TransactionHandling diff --git a/configuration/structured.go b/configuration/structured.go index 0ddd8e1e..612a1d84 100644 --- a/configuration/structured.go +++ b/configuration/structured.go @@ -32,6 +32,7 @@ type Structured interface { StructuredLogForward StructuredFCGIApp StructuredMailersSection + StructuredTraces } type StructuredToParserArgs struct { diff --git a/configuration/structured_traces.go b/configuration/structured_traces.go new file mode 100644 index 00000000..f8ee0941 --- /dev/null +++ b/configuration/structured_traces.go @@ -0,0 +1,84 @@ +// Copyright 2024 HAProxy Technologies +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package configuration + +import ( + "github.com/go-openapi/strfmt" + parser "github.com/haproxytech/client-native/v6/config-parser" + "github.com/haproxytech/client-native/v6/models" +) + +type StructuredTraces interface { + GetStructuredTraces(transactionID string) (int64, *models.Traces, error) + PushStructuredTraces(data *models.Traces, transactionID string, version int64) error +} + +func (c *client) GetStructuredTraces(transactionID string) (int64, *models.Traces, error) { + p, err := c.GetParser(transactionID) + if err != nil { + return 0, nil, err + } + + v, err := c.GetVersion(transactionID) + if err != nil { + return 0, nil, err + } + + traces, err := ParseTraces(p) + + return v, traces, err +} + +func (c *client) PushStructuredTraces(data *models.Traces, transactionID string, version int64) error { + if c.UseModelsValidation { + validationErr := data.Validate(strfmt.Default) + if validationErr != nil { + return NewConfError(ErrValidationError, validationErr.Error()) + } + } + + p, t, err := c.loadDataForChange(transactionID, version) + if err != nil { + return err + } + + // Delete the existing section. + if c.checkSectionExists(parser.Traces, parser.TracesSectionName, p) { + if err = p.SectionsDelete(parser.Traces, parser.TracesSectionName); err != nil { + return c.HandleError(TracesParentName, "", "", t, transactionID == "", err) + } + } + + if err = p.SectionsCreate(parser.Traces, parser.TracesSectionName); err != nil { + return c.HandleError(TracesParentName, "", "", t, transactionID == "", err) + } + + if err = serializeStructuredTraces(StructuredToParserArgs{ + TID: transactionID, + Parser: &p, + Options: &c.ConfigurationOptions, + HandleError: c.HandleError, + CheckSectionExists: c.checkSectionExists, + }, data); err != nil { + return err + } + + return c.SaveData(p, t, transactionID == "") +} + +func serializeStructuredTraces(a StructuredToParserArgs, traces *models.Traces) error { + return SerializeTraces(*a.Parser, traces) +} diff --git a/configuration/traces.go b/configuration/traces.go new file mode 100644 index 00000000..3deb8823 --- /dev/null +++ b/configuration/traces.go @@ -0,0 +1,244 @@ +// Copyright 2024 HAProxy Technologies +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package configuration + +import ( + "errors" + "fmt" + "strings" + + strfmt "github.com/go-openapi/strfmt" + parser "github.com/haproxytech/client-native/v6/config-parser" + "github.com/haproxytech/client-native/v6/config-parser/common" + parser_errors "github.com/haproxytech/client-native/v6/config-parser/errors" + "github.com/haproxytech/client-native/v6/config-parser/types" + "github.com/haproxytech/client-native/v6/misc" + "github.com/haproxytech/client-native/v6/models" +) + +type Traces interface { + GetTraces(transactionID string) (int64, *models.Traces, error) + CreateTraces(data *models.Traces, transactionID string, version int64) error + EditTraces(data *models.Traces, transactionID string, version int64) error + DeleteTraces(transactionID string, version int64) error + CreateTraceEntry(data *models.TraceEntry, transactionID string, version int64) error + DeleteTraceEntry(data *models.TraceEntry, transactionID string, version int64) error +} + +func (c *client) GetTraces(transactionID string) (int64, *models.Traces, error) { + p, err := c.GetParser(transactionID) + if err != nil { + return 0, nil, err + } + + v, err := c.GetVersion(transactionID) + if err != nil { + return 0, nil, err + } + + traces, err := ParseTraces(p) + if err != nil { + return v, nil, c.HandleError(TracesParentName, "", "", transactionID, transactionID == "", err) + } + + return v, traces, err +} + +func (c *client) CreateTraces(data *models.Traces, transactionID string, version int64) error { + if c.UseModelsValidation { + validationErr := data.Validate(strfmt.Default) + if validationErr != nil { + return NewConfError(ErrValidationError, validationErr.Error()) + } + } + + p, t, err := c.loadDataForChange(transactionID, version) + if err != nil { + return c.HandleError(TracesParentName, "", "", t, transactionID == "", err) + } + + if c.checkSectionExists(parser.Traces, parser.TracesSectionName, p) { + e := NewConfError(ErrObjectAlreadyExists, fmt.Sprintf("%s section already exists", parser.Traces)) + return c.HandleError(TracesParentName, "", "", t, transactionID == "", e) + } + + if err = p.SectionsCreate(parser.Traces, parser.TracesSectionName); err != nil { + return c.HandleError(TracesParentName, "", "", t, transactionID == "", err) + } + + if err = SerializeTraces(p, data); err != nil { + return c.HandleError(TracesParentName, "", "", t, transactionID == "", err) + } + + return c.SaveData(p, t, transactionID == "") +} + +func (c *client) EditTraces(data *models.Traces, transactionID string, version int64) error { + if c.UseModelsValidation { + validationErr := data.Validate(strfmt.Default) + if validationErr != nil { + return NewConfError(ErrValidationError, validationErr.Error()) + } + } + p, t, err := c.loadDataForChange(transactionID, version) + if err != nil { + return err + } + + // Delete the existing section. + if c.checkSectionExists(parser.Traces, parser.TracesSectionName, p) { + if err = p.SectionsDelete(parser.Traces, parser.TracesSectionName); err != nil { + return c.HandleError(TracesParentName, "", "", t, transactionID == "", err) + } + } + + if err = p.SectionsCreate(parser.Traces, parser.TracesSectionName); err != nil { + return c.HandleError(TracesParentName, "", "", t, transactionID == "", err) + } + + if err = SerializeTraces(p, data); err != nil { + return err + } + + return c.SaveData(p, t, transactionID == "") +} + +func (c *client) DeleteTraces(transactionID string, version int64) error { + return c.deleteSection(parser.Traces, parser.TracesSectionName, transactionID, version) +} + +// Add an entry to the traces section. +func (c *client) CreateTraceEntry(data *models.TraceEntry, transactionID string, version int64) error { + if c.UseModelsValidation { + validationErr := data.Validate(strfmt.Default) + if validationErr != nil { + return NewConfError(ErrValidationError, validationErr.Error()) + } + } + + p, t, err := c.loadDataForChange(transactionID, version) + if err != nil { + return c.HandleError("trace", TracesParentName, "", t, transactionID == "", err) + } + + // Count existing entries. This Get() will create the section if needed. + traceEntries, err := p.Get(parser.Traces, parser.TracesSectionName, "trace", true) + if err != nil { + if !errors.Is(err, parser_errors.ErrFetch) { + return err + } + } + entries, ok := traceEntries.([]types.Trace) + if !ok { + return misc.CreateTypeAssertError("trace entries") + } + i := len(entries) + + err = p.Insert(parser.Traces, parser.TracesSectionName, "trace", convertTraceEntry(data), i) + if err != nil { + return c.HandleError("trace", TracesParentName, "", t, transactionID == "", err) + } + + return c.SaveData(p, t, transactionID == "") +} + +// Delete a trace entry from the traces section. +func (c *client) DeleteTraceEntry(data *models.TraceEntry, transactionID string, version int64) error { + if c.UseModelsValidation { + validationErr := data.Validate(strfmt.Default) + if validationErr != nil { + return NewConfError(ErrValidationError, validationErr.Error()) + } + } + p, t, err := c.loadDataForChange(transactionID, version) + if err != nil { + return err + } + + if !c.checkSectionExists(parser.Traces, parser.TracesSectionName, p) { + e := NewConfError(ErrObjectDoesNotExist, fmt.Sprintf("%s section does not exists", parser.Traces)) + return c.HandleError(TracesParentName, "", "", t, transactionID == "", e) + } + + // Look for the trace entry to remove + traces, err := ParseTraces(p) + if err != nil { + return c.HandleError(TracesParentName, "", "", t, transactionID == "", err) + } + for i, ent := range traces.Entries { + if ent.Trace == data.Trace { + err = p.Delete(parser.Traces, parser.TracesSectionName, "trace", i) + if err != nil { + return c.HandleError(TracesParentName, "", "", t, transactionID == "", err) + } + return c.SaveData(p, t, transactionID == "") + } + } + + // Entry not found. + e := NewConfError(ErrObjectDoesNotExist, fmt.Sprintf("trace entry does not exists: '%s'", data.Trace)) + return c.HandleError(TracesParentName, "", "", t, transactionID == "", e) +} + +func ParseTraces(p parser.Parser) (*models.Traces, error) { + traces := new(models.Traces) + + traceEntries, err := p.Get(parser.Traces, parser.TracesSectionName, "trace", false) + if err != nil { + if errors.Is(err, parser_errors.ErrFetch) { + return traces, nil + } + return nil, err + } + + entries, ok := traceEntries.([]types.Trace) + if !ok { + return nil, misc.CreateTypeAssertError("trace entries") + } + if len(entries) == 0 { + return traces, nil + } + + traces.Entries = make(models.TraceEntries, len(entries)) + for i, t := range entries { + traces.Entries[i] = &models.TraceEntry{Trace: strings.Join(t.Params, " ")} + } + + return traces, nil +} + +func SerializeTraces(p parser.Parser, traces *models.Traces) error { + if traces == nil { + return fmt.Errorf("empty %s section", TracesParentName) + } + + for i, t := range traces.Entries { + err := p.Insert(parser.Traces, parser.TracesSectionName, "trace", convertTraceEntry(t), i) + if err != nil { + return err + } + } + + return nil +} + +func convertTraceEntry(entry *models.TraceEntry) types.Trace { + result := types.Trace{} + if entry != nil { + result.Params = common.StringSplitIgnoreEmpty(entry.Trace, ' ', ' ') + } + return result +} diff --git a/models/trace_entries.go b/models/trace_entries.go new file mode 100644 index 00000000..0fa86e14 --- /dev/null +++ b/models/trace_entries.go @@ -0,0 +1,93 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// Copyright 2019 HAProxy Technologies +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "strconv" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// TraceEntries list of entries in a traces section +// +// swagger:model trace_entries +type TraceEntries []*TraceEntry + +// Validate validates this trace entries +func (m TraceEntries) Validate(formats strfmt.Registry) error { + var res []error + + for i := 0; i < len(m); i++ { + if swag.IsZero(m[i]) { // not required + continue + } + + if m[i] != nil { + if err := m[i].Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName(strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName(strconv.Itoa(i)) + } + return err + } + } + + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +// ContextValidate validate this trace entries based on the context it is used +func (m TraceEntries) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + for i := 0; i < len(m); i++ { + + if m[i] != nil { + + if swag.IsZero(m[i]) { // not required + return nil + } + + if err := m[i].ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName(strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName(strconv.Itoa(i)) + } + return err + } + } + + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/models/trace_entries_compare.go b/models/trace_entries_compare.go new file mode 100644 index 00000000..c321d60b --- /dev/null +++ b/models/trace_entries_compare.go @@ -0,0 +1,98 @@ +// Code generated with struct_equal_generator; DO NOT EDIT. + +// Copyright 2019 HAProxy Technologies +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package models + +import ( + "fmt" +) + +// Equal checks if two structs of type TraceEntries are equal +// +// By default empty maps and slices are equal to nil: +// +// var a, b TraceEntries +// equal := a.Equal(b) +// +// For more advanced use case you can configure these options (default values are shown): +// +// var a, b TraceEntries +// equal := a.Equal(b,Options{ +// NilSameAsEmpty: true, +// }) +func (s TraceEntries) Equal(t TraceEntries, opts ...Options) bool { + opt := getOptions(opts...) + + if !opt.NilSameAsEmpty { + if s == nil && t != nil { + return false + } + if t == nil && s != nil { + return false + } + } + if len(s) != len(t) { + return false + } + for i, v := range s { + if !v.Equal(*t[i], opt) { + return false + } + + } + return true +} + +// Diff checks if two structs of type TraceEntries are equal +// +// By default empty maps and slices are equal to nil: +// +// var a, b TraceEntries +// diff := a.Diff(b) +// +// For more advanced use case you can configure these options (default values are shown): +// +// var a, b TraceEntries +// diff := a.Diff(b,Options{ +// NilSameAsEmpty: true, +// }) +func (s TraceEntries) Diff(t TraceEntries, opts ...Options) map[string][]interface{} { + opt := getOptions(opts...) + + diff := make(map[string][]interface{}) + if !opt.NilSameAsEmpty { + if s == nil && t != nil { + diff["Diff"] = []interface{}{s, t} + return diff + } + if t == nil && s != nil { + diff["Diff"] = []interface{}{s, t} + return diff + } + } + if len(s) != len(t) { + diff["Diff"] = []interface{}{s, t} + return diff + } + for i, v := range s { + if !v.Equal(*t[i], opt) { + diff[fmt.Sprintf("Diff[%d]", i)] = []interface{}{v, t[i]} + } + + } + return diff +} diff --git a/models/trace_entry.go b/models/trace_entry.go new file mode 100644 index 00000000..b39a15f6 --- /dev/null +++ b/models/trace_entry.go @@ -0,0 +1,93 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// Copyright 2019 HAProxy Technologies +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" + "github.com/go-openapi/validate" +) + +// TraceEntry Trace event +// +// # Configure a trace event +// +// swagger:model trace_entry +type TraceEntry struct { + + // Trace parameters + // Required: true + // Min Length: 1 + Trace string `json:"trace"` +} + +// Validate validates this trace entry +func (m *TraceEntry) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateTrace(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *TraceEntry) validateTrace(formats strfmt.Registry) error { + + if err := validate.RequiredString("trace", "body", m.Trace); err != nil { + return err + } + + if err := validate.MinLength("trace", "body", m.Trace, 1); err != nil { + return err + } + + return nil +} + +// ContextValidate validates this trace entry based on context it is used +func (m *TraceEntry) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (m *TraceEntry) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *TraceEntry) UnmarshalBinary(b []byte) error { + var res TraceEntry + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/models/trace_entry_compare.go b/models/trace_entry_compare.go new file mode 100644 index 00000000..15547d9e --- /dev/null +++ b/models/trace_entry_compare.go @@ -0,0 +1,47 @@ +// Code generated with struct_equal_generator; DO NOT EDIT. + +// Copyright 2019 HAProxy Technologies +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package models + +// Equal checks if two structs of type TraceEntry are equal +// +// var a, b TraceEntry +// equal := a.Equal(b) +// +// opts ...Options are ignored in this method +func (s TraceEntry) Equal(t TraceEntry, opts ...Options) bool { + if s.Trace != t.Trace { + return false + } + + return true +} + +// Diff checks if two structs of type TraceEntry are equal +// +// var a, b TraceEntry +// diff := a.Diff(b) +// +// opts ...Options are ignored in this method +func (s TraceEntry) Diff(t TraceEntry, opts ...Options) map[string][]interface{} { + diff := make(map[string][]interface{}) + if s.Trace != t.Trace { + diff["Trace"] = []interface{}{s.Trace, t.Trace} + } + + return diff +} diff --git a/models/trace_entry_compare_test.go b/models/trace_entry_compare_test.go new file mode 100644 index 00000000..897d3ec7 --- /dev/null +++ b/models/trace_entry_compare_test.go @@ -0,0 +1,186 @@ +// Code generated with struct_equal_generator; DO NOT EDIT. + +// Copyright 2019 HAProxy Technologies +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package models + +import ( + "encoding/json" + "testing" + + "github.com/go-faker/faker/v4" + "github.com/go-faker/faker/v4/pkg/options" + + jsoniter "github.com/json-iterator/go" +) + +func TestTraceEntryEqual(t *testing.T) { + samples := []struct { + a, b TraceEntry + }{} + for i := 0; i < 2; i++ { + var sample TraceEntry + var result TraceEntry + err := faker.FakeData(&sample, options.WithIgnoreInterface(true)) + if err != nil { + t.Errorf(err.Error()) + } + byteJSON, err := json.Marshal(sample) + if err != nil { + t.Errorf(err.Error()) + } + err = json.Unmarshal(byteJSON, &result) + if err != nil { + t.Errorf(err.Error()) + } + + samples = append(samples, struct { + a, b TraceEntry + }{sample, result}) + } + + for _, sample := range samples { + result := sample.a.Equal(sample.b) + if !result { + json := jsoniter.ConfigCompatibleWithStandardLibrary + a, err := json.Marshal(&sample.a) + if err != nil { + t.Errorf(err.Error()) + } + b, err := json.Marshal(&sample.b) + if err != nil { + t.Errorf(err.Error()) + } + t.Errorf("Expected TraceEntry to be equal, but it is not %s %s", a, b) + } + } +} + +func TestTraceEntryEqualFalse(t *testing.T) { + samples := []struct { + a, b TraceEntry + }{} + for i := 0; i < 2; i++ { + var sample TraceEntry + var result TraceEntry + err := faker.FakeData(&sample, options.WithIgnoreInterface(true)) + if err != nil { + t.Errorf(err.Error()) + } + err = faker.FakeData(&result, options.WithIgnoreInterface(true)) + if err != nil { + t.Errorf(err.Error()) + } + samples = append(samples, struct { + a, b TraceEntry + }{sample, result}) + } + + for _, sample := range samples { + result := sample.a.Equal(sample.b) + if result { + json := jsoniter.ConfigCompatibleWithStandardLibrary + a, err := json.Marshal(&sample.a) + if err != nil { + t.Errorf(err.Error()) + } + b, err := json.Marshal(&sample.b) + if err != nil { + t.Errorf(err.Error()) + } + t.Errorf("Expected TraceEntry to be different, but it is not %s %s", a, b) + } + } +} + +func TestTraceEntryDiff(t *testing.T) { + samples := []struct { + a, b TraceEntry + }{} + for i := 0; i < 2; i++ { + var sample TraceEntry + var result TraceEntry + err := faker.FakeData(&sample, options.WithIgnoreInterface(true)) + if err != nil { + t.Errorf(err.Error()) + } + byteJSON, err := json.Marshal(sample) + if err != nil { + t.Errorf(err.Error()) + } + err = json.Unmarshal(byteJSON, &result) + if err != nil { + t.Errorf(err.Error()) + } + + samples = append(samples, struct { + a, b TraceEntry + }{sample, result}) + } + + for _, sample := range samples { + result := sample.a.Diff(sample.b) + if len(result) != 0 { + json := jsoniter.ConfigCompatibleWithStandardLibrary + a, err := json.Marshal(&sample.a) + if err != nil { + t.Errorf(err.Error()) + } + b, err := json.Marshal(&sample.b) + if err != nil { + t.Errorf(err.Error()) + } + t.Errorf("Expected TraceEntry to be equal, but it is not %s %s, %v", a, b, result) + } + } +} + +func TestTraceEntryDiffFalse(t *testing.T) { + samples := []struct { + a, b TraceEntry + }{} + for i := 0; i < 2; i++ { + var sample TraceEntry + var result TraceEntry + err := faker.FakeData(&sample, options.WithIgnoreInterface(true)) + if err != nil { + t.Errorf(err.Error()) + } + err = faker.FakeData(&result, options.WithIgnoreInterface(true)) + if err != nil { + t.Errorf(err.Error()) + } + samples = append(samples, struct { + a, b TraceEntry + }{sample, result}) + } + + for _, sample := range samples { + result := sample.a.Diff(sample.b) + if len(result) != 1 { + json := jsoniter.ConfigCompatibleWithStandardLibrary + a, err := json.Marshal(&sample.a) + if err != nil { + t.Errorf(err.Error()) + } + b, err := json.Marshal(&sample.b) + if err != nil { + t.Errorf(err.Error()) + } + t.Errorf("Expected TraceEntry to be different in 1 cases, but it is not (%d) %s %s", len(result), a, b) + } + } +} diff --git a/models/traces.go b/models/traces.go new file mode 100644 index 00000000..0b482902 --- /dev/null +++ b/models/traces.go @@ -0,0 +1,115 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// Copyright 2019 HAProxy Technologies +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// Traces Trace events configuration +// +// swagger:model Traces +type Traces struct { + + // entries + Entries TraceEntries `json:"entries,omitempty"` +} + +// Validate validates this traces +func (m *Traces) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateEntries(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *Traces) validateEntries(formats strfmt.Registry) error { + if swag.IsZero(m.Entries) { // not required + return nil + } + + if err := m.Entries.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("entries") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("entries") + } + return err + } + + return nil +} + +// ContextValidate validate this traces based on the context it is used +func (m *Traces) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + if err := m.contextValidateEntries(ctx, formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *Traces) contextValidateEntries(ctx context.Context, formats strfmt.Registry) error { + + if err := m.Entries.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("entries") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("entries") + } + return err + } + + return nil +} + +// MarshalBinary interface implementation +func (m *Traces) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *Traces) UnmarshalBinary(b []byte) error { + var res Traces + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/models/traces_compare.go b/models/traces_compare.go new file mode 100644 index 00000000..12ea80b5 --- /dev/null +++ b/models/traces_compare.go @@ -0,0 +1,66 @@ +// Code generated with struct_equal_generator; DO NOT EDIT. + +// Copyright 2019 HAProxy Technologies +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package models + +// Equal checks if two structs of type Traces are equal +// +// By default empty maps and slices are equal to nil: +// +// var a, b Traces +// equal := a.Equal(b) +// +// For more advanced use case you can configure these options (default values are shown): +// +// var a, b Traces +// equal := a.Equal(b,Options{ +// NilSameAsEmpty: true, +// }) +func (s Traces) Equal(t Traces, opts ...Options) bool { + opt := getOptions(opts...) + + if !s.Entries.Equal(t.Entries, opt) { + return false + } + + return true +} + +// Diff checks if two structs of type Traces are equal +// +// By default empty maps and slices are equal to nil: +// +// var a, b Traces +// diff := a.Diff(b) +// +// For more advanced use case you can configure these options (default values are shown): +// +// var a, b Traces +// diff := a.Diff(b,Options{ +// NilSameAsEmpty: true, +// }) +func (s Traces) Diff(t Traces, opts ...Options) map[string][]interface{} { + opt := getOptions(opts...) + + diff := make(map[string][]interface{}) + + if !s.Entries.Equal(t.Entries, opt) { + diff["Entries"] = []interface{}{s.Entries, t.Entries} + } + + return diff +} diff --git a/models/traces_compare_test.go b/models/traces_compare_test.go new file mode 100644 index 00000000..88969068 --- /dev/null +++ b/models/traces_compare_test.go @@ -0,0 +1,186 @@ +// Code generated with struct_equal_generator; DO NOT EDIT. + +// Copyright 2019 HAProxy Technologies +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package models + +import ( + "encoding/json" + "testing" + + "github.com/go-faker/faker/v4" + "github.com/go-faker/faker/v4/pkg/options" + + jsoniter "github.com/json-iterator/go" +) + +func TestTracesEqual(t *testing.T) { + samples := []struct { + a, b Traces + }{} + for i := 0; i < 2; i++ { + var sample Traces + var result Traces + err := faker.FakeData(&sample, options.WithIgnoreInterface(true)) + if err != nil { + t.Errorf(err.Error()) + } + byteJSON, err := json.Marshal(sample) + if err != nil { + t.Errorf(err.Error()) + } + err = json.Unmarshal(byteJSON, &result) + if err != nil { + t.Errorf(err.Error()) + } + + samples = append(samples, struct { + a, b Traces + }{sample, result}) + } + + for _, sample := range samples { + result := sample.a.Equal(sample.b) + if !result { + json := jsoniter.ConfigCompatibleWithStandardLibrary + a, err := json.Marshal(&sample.a) + if err != nil { + t.Errorf(err.Error()) + } + b, err := json.Marshal(&sample.b) + if err != nil { + t.Errorf(err.Error()) + } + t.Errorf("Expected Traces to be equal, but it is not %s %s", a, b) + } + } +} + +func TestTracesEqualFalse(t *testing.T) { + samples := []struct { + a, b Traces + }{} + for i := 0; i < 2; i++ { + var sample Traces + var result Traces + err := faker.FakeData(&sample, options.WithIgnoreInterface(true)) + if err != nil { + t.Errorf(err.Error()) + } + err = faker.FakeData(&result, options.WithIgnoreInterface(true)) + if err != nil { + t.Errorf(err.Error()) + } + samples = append(samples, struct { + a, b Traces + }{sample, result}) + } + + for _, sample := range samples { + result := sample.a.Equal(sample.b) + if result { + json := jsoniter.ConfigCompatibleWithStandardLibrary + a, err := json.Marshal(&sample.a) + if err != nil { + t.Errorf(err.Error()) + } + b, err := json.Marshal(&sample.b) + if err != nil { + t.Errorf(err.Error()) + } + t.Errorf("Expected Traces to be different, but it is not %s %s", a, b) + } + } +} + +func TestTracesDiff(t *testing.T) { + samples := []struct { + a, b Traces + }{} + for i := 0; i < 2; i++ { + var sample Traces + var result Traces + err := faker.FakeData(&sample, options.WithIgnoreInterface(true)) + if err != nil { + t.Errorf(err.Error()) + } + byteJSON, err := json.Marshal(sample) + if err != nil { + t.Errorf(err.Error()) + } + err = json.Unmarshal(byteJSON, &result) + if err != nil { + t.Errorf(err.Error()) + } + + samples = append(samples, struct { + a, b Traces + }{sample, result}) + } + + for _, sample := range samples { + result := sample.a.Diff(sample.b) + if len(result) != 0 { + json := jsoniter.ConfigCompatibleWithStandardLibrary + a, err := json.Marshal(&sample.a) + if err != nil { + t.Errorf(err.Error()) + } + b, err := json.Marshal(&sample.b) + if err != nil { + t.Errorf(err.Error()) + } + t.Errorf("Expected Traces to be equal, but it is not %s %s, %v", a, b, result) + } + } +} + +func TestTracesDiffFalse(t *testing.T) { + samples := []struct { + a, b Traces + }{} + for i := 0; i < 2; i++ { + var sample Traces + var result Traces + err := faker.FakeData(&sample, options.WithIgnoreInterface(true)) + if err != nil { + t.Errorf(err.Error()) + } + err = faker.FakeData(&result, options.WithIgnoreInterface(true)) + if err != nil { + t.Errorf(err.Error()) + } + samples = append(samples, struct { + a, b Traces + }{sample, result}) + } + + for _, sample := range samples { + result := sample.a.Diff(sample.b) + if len(result) != 1 { + json := jsoniter.ConfigCompatibleWithStandardLibrary + a, err := json.Marshal(&sample.a) + if err != nil { + t.Errorf(err.Error()) + } + b, err := json.Marshal(&sample.b) + if err != nil { + t.Errorf(err.Error()) + } + t.Errorf("Expected Traces to be different in 1 cases, but it is not (%d) %s %s", len(result), a, b) + } + } +} diff --git a/specification/build/haproxy_spec.yaml b/specification/build/haproxy_spec.yaml index 46c96fc7..5746c8fd 100644 --- a/specification/build/haproxy_spec.yaml +++ b/specification/build/haproxy_spec.yaml @@ -10989,6 +10989,31 @@ definitions: items: $ref: "#/definitions/crt_load" x-omitempty: true + traces: + additionalProperties: false + description: Trace events configuration + properties: + entries: + $ref: '#/definitions/trace_entries' + type: object + x-go-name: Traces + trace_entry: + description: Configure a trace event + properties: + trace: + description: Trace parameters + minLength: 1 + type: string + x-nullable: false + required: + - trace + title: Trace event + type: object + trace_entries: + description: list of entries in a traces section + type: array + items: + $ref: "#/definitions/trace_entry" responses: BadRequest: description: Bad request @@ -11154,6 +11179,7 @@ tags: - name: TCPRequestRule - name: TCPResponseRule - name: Table + - name: Traces - name: Transactions description: | Managing transactions. Configuration changes can be grouped in the transaction. You start the @@ -22867,6 +22893,184 @@ paths: summary: Replace a certificate load entry tags: - CrtLoad + /services/haproxy/configuration/traces: + delete: + description: Delete the whole traces section. + operationId: deleteTraces + parameters: + - $ref: '#/parameters/transaction_id' + - $ref: '#/parameters/version' + - $ref: '#/parameters/force_reload' + responses: + "202": + description: Configuration change accepted and reload requested + headers: + Reload-ID: + description: ID of the requested reload + type: string + "204": + description: Traces section deleted + "404": + $ref: '#/responses/NotFound' + default: + $ref: '#/responses/DefaultError' + summary: Delete the traces section + tags: + - Traces + get: + description: Return the traces section contents. + operationId: getTraces + parameters: + - $ref: '#/parameters/transaction_id' + - $ref: '#/parameters/full_section' + responses: + "200": + description: Successful operation + headers: + Configuration-Version: + description: Configuration file version + type: string + schema: + $ref: '#/definitions/traces' + "404": + $ref: '#/responses/NotFound' + default: + $ref: '#/responses/DefaultError' + summary: Return the traces section contents + tags: + - Traces + post: + description: Create the traces section. + operationId: createTraces + parameters: + - in: body + name: data + required: true + schema: + $ref: '#/definitions/traces' + - $ref: '#/parameters/transaction_id' + - $ref: '#/parameters/version' + - $ref: '#/parameters/force_reload' + responses: + "201": + description: Traces section created + schema: + $ref: '#/definitions/traces' + "202": + description: Configuration change accepted and reload requested + headers: + Reload-ID: + description: ID of the requested reload + type: string + schema: + $ref: '#/definitions/traces' + "400": + $ref: '#/responses/BadRequest' + "409": + $ref: '#/responses/AlreadyExists' + default: + $ref: '#/responses/DefaultError' + summary: Create the traces section + tags: + - Traces + put: + description: Replace the traces section contents + operationId: replaceTraces + parameters: + - in: body + name: data + required: true + schema: + $ref: '#/definitions/traces' + - $ref: '#/parameters/transaction_id' + - $ref: '#/parameters/version' + - $ref: '#/parameters/force_reload' + - $ref: '#/parameters/full_section' + responses: + "200": + description: Traces replaced + schema: + $ref: '#/definitions/traces' + "202": + description: Configuration change accepted and reload requested + headers: + Reload-ID: + description: ID of the requested reload + type: string + schema: + $ref: '#/definitions/traces' + "400": + $ref: '#/responses/BadRequest' + "404": + $ref: '#/responses/NotFound' + default: + $ref: '#/responses/DefaultError' + summary: Replace traces + tags: + - Traces + /services/haproxy/configuration/traces/entries: + delete: + description: Deletes a trace entry from the traces section. + operationId: deleteTraceEntry + parameters: + - in: body + name: data + required: true + schema: + $ref: '#/definitions/trace_entry' + - $ref: '#/parameters/transaction_id' + - $ref: '#/parameters/version' + - $ref: '#/parameters/force_reload' + responses: + "202": + description: Configuration change accepted and reload requested + headers: + Reload-ID: + description: ID of the requested reload + type: string + "204": + description: Trace entry deleted + "404": + $ref: '#/responses/NotFound' + default: + $ref: '#/responses/DefaultError' + summary: Delete a trace entry + tags: + - Traces + post: + description: Adds a new trace entry into the traces section. The traces section will be created if needed. + operationId: createTraceEntry + parameters: + - in: body + name: data + required: true + schema: + $ref: '#/definitions/trace_entry' + - $ref: '#/parameters/transaction_id' + - $ref: '#/parameters/version' + - $ref: '#/parameters/force_reload' + responses: + "201": + description: Trace entry added + schema: + $ref: '#/definitions/trace_entry' + "202": + description: Configuration change accepted and reload requested + headers: + Reload-ID: + description: ID of the requested reload + type: string + schema: + $ref: '#/definitions/trace_entry' + "400": + $ref: '#/responses/BadRequest' + "409": + $ref: '#/responses/AlreadyExists' + default: + $ref: '#/responses/DefaultError' + summary: Add a new trace entry + tags: + - Traces /services/haproxy/runtime: get: description: Returns a list of endpoints to be used for advanced runtime settings of HAProxy objects. diff --git a/specification/haproxy-spec.yaml b/specification/haproxy-spec.yaml index 4adcf6d5..9c352a4d 100644 --- a/specification/haproxy-spec.yaml +++ b/specification/haproxy-spec.yaml @@ -652,6 +652,15 @@ definitions: items: $ref: "#/definitions/crt_load" x-omitempty: true + traces: + $ref: "models/configuration/traces.yaml#/traces" + trace_entry: + $ref: "models/configuration/traces.yaml#/trace_entry" + trace_entries: + description: list of entries in a traces section + type: array + items: + $ref: "#/definitions/trace_entry" responses: BadRequest: description: Bad request @@ -825,6 +834,7 @@ tags: - name: Table - name: CrtStore - name: CrtLoad + - name: Traces security: - basic_auth: [] paths: @@ -1100,6 +1110,10 @@ paths: $ref: "paths/configuration/crt_store.yaml#/crt_loads" /services/haproxy/configuration/crt_loads/{certificate}: $ref: "paths/configuration/crt_store.yaml#/crt_load" + /services/haproxy/configuration/traces: + $ref: "paths/configuration/traces.yaml#/traces" + /services/haproxy/configuration/traces/entries: + $ref: "paths/configuration/traces.yaml#/trace_entries" /services/haproxy/runtime: $ref: "paths/general.yaml#/runtime" /services/haproxy/runtime/acls: diff --git a/specification/models/configuration/traces.yaml b/specification/models/configuration/traces.yaml new file mode 100644 index 00000000..9e183cff --- /dev/null +++ b/specification/models/configuration/traces.yaml @@ -0,0 +1,21 @@ +--- +traces: + type: object + description: Trace events configuration + x-go-name: Traces + properties: + entries: + $ref: "#/definitions/trace_entries" + additionalProperties: false +trace_entry: + title: Trace event + description: Configure a trace event + type: object + required: + - trace + properties: + trace: + type: string + description: Trace parameters + minLength: 1 + x-nullable: false \ No newline at end of file diff --git a/specification/paths/configuration/traces.yaml b/specification/paths/configuration/traces.yaml new file mode 100644 index 00000000..42c6602a --- /dev/null +++ b/specification/paths/configuration/traces.yaml @@ -0,0 +1,180 @@ +--- +traces: + get: + summary: Return the traces section contents + description: Return the traces section contents. + operationId: getTraces + parameters: + - $ref: "#/parameters/transaction_id" + - $ref: "#/parameters/full_section" + tags: + - Traces + responses: + '200': + description: Successful operation + headers: + Configuration-Version: + description: Configuration file version + type: string + schema: + $ref: "#/definitions/traces" + '404': + $ref: "#/responses/NotFound" + 'default': + $ref: '#/responses/DefaultError' + post: + summary: Create the traces section + description: Create the traces section. + operationId: createTraces + tags: + - Traces + parameters: + - name: data + in: body + required: true + schema: + $ref: '#/definitions/traces' + - $ref: "#/parameters/transaction_id" + - $ref: "#/parameters/version" + - $ref: "#/parameters/force_reload" + responses: + '202': + description: Configuration change accepted and reload requested + headers: + Reload-ID: + description: ID of the requested reload + type: string + schema: + $ref: "#/definitions/traces" + '201': + description: Traces section created + schema: + $ref: "#/definitions/traces" + '409': + $ref: '#/responses/AlreadyExists' + '400': + $ref: '#/responses/BadRequest' + 'default': + $ref: '#/responses/DefaultError' + put: + summary: Replace traces + description: Replace the traces section contents + operationId: replaceTraces + parameters: + - name: data + required: true + in: body + schema: + $ref: "#/definitions/traces" + - $ref: "#/parameters/transaction_id" + - $ref: "#/parameters/version" + - $ref: "#/parameters/force_reload" + - $ref: "#/parameters/full_section" + tags: + - Traces + responses: + '202': + description: Configuration change accepted and reload requested + headers: + Reload-ID: + description: ID of the requested reload + type: string + schema: + $ref: "#/definitions/traces" + '200': + description: Traces replaced + schema: + $ref: "#/definitions/traces" + '400': + $ref: '#/responses/BadRequest' + '404': + $ref: '#/responses/NotFound' + 'default': + $ref: '#/responses/DefaultError' + delete: + summary: Delete the traces section + description: Delete the whole traces section. + operationId: deleteTraces + tags: + - Traces + parameters: + - $ref: "#/parameters/transaction_id" + - $ref: "#/parameters/version" + - $ref: "#/parameters/force_reload" + responses: + '202': + description: Configuration change accepted and reload requested + headers: + Reload-ID: + description: ID of the requested reload + type: string + '204': + description: Traces section deleted + '404': + $ref: '#/responses/NotFound' + 'default': + $ref: '#/responses/DefaultError' + +trace_entries: + post: + summary: Add a new trace entry + description: Adds a new trace entry into the traces section. The traces section will be created if needed. + operationId: createTraceEntry + tags: + - Traces + parameters: + - name: data + in: body + required: true + schema: + $ref: '#/definitions/trace_entry' + - $ref: "#/parameters/transaction_id" + - $ref: "#/parameters/version" + - $ref: "#/parameters/force_reload" + responses: + '202': + description: Configuration change accepted and reload requested + headers: + Reload-ID: + description: ID of the requested reload + type: string + schema: + $ref: "#/definitions/trace_entry" + '201': + description: Trace entry added + schema: + $ref: "#/definitions/trace_entry" + '409': + $ref: '#/responses/AlreadyExists' + '400': + $ref: '#/responses/BadRequest' + 'default': + $ref: '#/responses/DefaultError' + delete: + summary: Delete a trace entry + description: Deletes a trace entry from the traces section. + operationId: deleteTraceEntry + tags: + - Traces + parameters: + - name: data + in: body + required: true + schema: + $ref: '#/definitions/trace_entry' + - $ref: "#/parameters/transaction_id" + - $ref: "#/parameters/version" + - $ref: "#/parameters/force_reload" + responses: + '202': + description: Configuration change accepted and reload requested + headers: + Reload-ID: + description: ID of the requested reload + type: string + '204': + description: Trace entry deleted + '404': + $ref: '#/responses/NotFound' + 'default': + $ref: '#/responses/DefaultError' diff --git a/test/configuration_test.go b/test/configuration_test.go index 98bf559a..61a8eda7 100644 --- a/test/configuration_test.go +++ b/test/configuration_test.go @@ -965,6 +965,10 @@ crt-store cert-bunker1 crt-base /secure/certs key-base /secure/keys +traces + trace h1 sink buf1 level developer verbosity complete start now + trace h2 sink buf2 level developer verbosity complete start now + http-errors website-1 errorfile 400 /etc/haproxy/errorfiles/site1/400.http errorfile 404 /etc/haproxy/errorfiles/site1/404.http diff --git a/test/convert.go b/test/convert.go index 733c6096..14eafb0b 100644 --- a/test/convert.go +++ b/test/convert.go @@ -57,7 +57,7 @@ func expectedResources[T any](elementKey string) (map[string]T, error) { var elems map[string]T err = json.Unmarshal(j, &elems) if err != nil { - // Case Defaults, Globals + // Case Defaults, Globals, Traces var elem T err = json.Unmarshal(j, &elem) if err != nil { @@ -276,6 +276,11 @@ func StructuredToGlobalMap() models.Global { return resources[""] } +func StructuredToTracesMap() models.Traces { + resources, _ := expectedResources[models.Traces]("traces") + return resources[""] +} + func StructuredToDefaultsMap() map[string][]*models.Defaults { resources, _ := expectedResources[models.Defaults]("defaults") res := make(map[string][]*models.Defaults) diff --git a/test/expected/structured.json b/test/expected/structured.json index a48d4a2e..fcf88544 100644 --- a/test/expected/structured.json +++ b/test/expected/structured.json @@ -2966,6 +2966,12 @@ "key_base": "/secure/keys" } }, + "traces": { + "entries": [ + {"trace": "h1 sink buf1 level developer verbosity complete start now"}, + {"trace": "h2 sink buf2 level developer verbosity complete start now"} + ] + }, "defaults": { "test_defaults": { "acl_list": [ diff --git a/test/structured_traces_test.go b/test/structured_traces_test.go new file mode 100644 index 00000000..4cd884b9 --- /dev/null +++ b/test/structured_traces_test.go @@ -0,0 +1,72 @@ +// Copyright 2024 HAProxy Technologies +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package test + +import ( + "os" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/haproxytech/client-native/v6/models" + "github.com/stretchr/testify/require" +) + +func TestStructuredGetTest(t *testing.T) { + clientTest, filename, err := getTestClient() + require.NoError(t, err) + defer os.Remove(filename) + version := int64(1) + + v, traces, err := clientTest.GetStructuredTraces("") + require.NoError(t, err) + require.Equal(t, version, v, "Version %v returned, expected %v", v, version) + + checkStructuredTraces(t, traces) +} + +func checkStructuredTraces(t *testing.T, traces *models.Traces) { + want := tracesExpcectation() + require.True(t, traces.Equal(*want), "diff %v", cmp.Diff(*traces, *want)) +} + +func TestPutStructuredTraces(t *testing.T) { + require := require.New(t) + clientTest, filename, err := getTestClient() + require.NoError(err) + defer os.Remove(filename) + version := int64(1) + + traces := &models.Traces{ + Entries: models.TraceEntries{ + &models.TraceEntry{Trace: "test trace 1"}, + &models.TraceEntry{Trace: "test trace 2"}, + }, + } + + err = clientTest.PushStructuredTraces(traces, "", version) + + require.NoError(err) + version++ + + ver, got, err := clientTest.GetStructuredTraces("") + require.NoError(err) + require.True(got.Equal(*traces), "global - diff %v", cmp.Diff(*got, *traces)) + require.Equal(version, ver, "Version %v returned, expected %v", ver, version) + + err = clientTest.PushStructuredTraces(traces, "", 55) + require.Error(err, "Should have returned version conflict.") + +} diff --git a/test/traces_test.go b/test/traces_test.go new file mode 100644 index 00000000..126bb350 --- /dev/null +++ b/test/traces_test.go @@ -0,0 +1,220 @@ +// Copyright 2024 HAProxy Technologies +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package test + +import ( + "reflect" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/haproxytech/client-native/v6/configuration" + "github.com/haproxytech/client-native/v6/models" + "github.com/stretchr/testify/require" +) + +func tracesExpcectation() *models.Traces { + initStructuredExpected() + res := StructuredToTracesMap() + return &res +} + +func TestGetTraces(t *testing.T) { + v, traces, err := clientTest.GetTraces("") + if err != nil { + t.Error(err.Error()) + } + + if v != version { + t.Errorf("Version %v returned, expected %v", v, version) + } + + checkTraces(t, traces) +} + +func checkTraces(t *testing.T, traces *models.Traces) { + want := tracesExpcectation() + require.True(t, traces.Equal(*want), "diff %v", cmp.Diff(traces, want)) +} + +func TestCreateEditDeleteTraces(t *testing.T) { + + // We cannot start by creating a new traces section, since there is + // already one present in $testConf, and this is a unique section. + // So let's delete first. + err := clientTest.DeleteTraces("", version) + if err != nil { + t.Error(err) + } else { + version++ + } + + traces := &models.Traces{ + Entries: models.TraceEntries{ + &models.TraceEntry{Trace: "test trace entry 1"}, + &models.TraceEntry{Trace: "test trace entry 2"}, + &models.TraceEntry{Trace: "test trace entry 3"}, + }, + } + + err = clientTest.CreateTraces(traces, "", version) + if err != nil { + t.Fatal(err) + } else { + version++ + } + + v, created, err := clientTest.GetTraces("") + if err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(created, traces) { + t.Log(cmp.Diff(created, traces)) + t.Fatal("Created traces section not equal to the given one") + } + + if v != version { + t.Errorf("found version %d, expected %d", v, version) + } + + err = clientTest.CreateTraces(traces, "", version) + if err == nil { + t.Fatal("Should throw error traces section already exists") + version++ + } + + // Modify the section. + traces.Entries[1].Trace = "test trace entry 2 bis" + traces.Entries = append(traces.Entries, &models.TraceEntry{Trace: "test trace entry 4"}) + err = clientTest.EditTraces(traces, "", version) + if err != nil { + t.Errorf("EditTraces: %v", err) + } else { + version++ + } + + // Check if the modification was effective. + v, created, err = clientTest.GetTraces("") + if err != nil { + t.Fatal(err) + } + if created == nil { + t.Fatal("got a nil Traces") + } + if v != version { + t.Errorf("found version %d, expected %d", v, version) + } + if !reflect.DeepEqual(created, traces) { + t.Log(cmp.Diff(created, traces)) + t.Fatal("Created traces section not equal to the given one") + } + + // Delete the section. + err = clientTest.DeleteTraces("", version) + if err != nil { + t.Error(err) + } else { + version++ + } + + if v, _ := clientTest.GetVersion(""); v != version { + t.Error("Version not incremented") + } + + err = clientTest.DeleteTraces("", 999999) + if err != nil { + if confErr, ok := err.(*configuration.ConfError); ok { + if !confErr.Is(configuration.ErrVersionMismatch) { + t.Error("Should throw configuration.ErrVersionMismatch error") + } + } else { + t.Error("Should throw configuration.ErrVersionMismatch error") + } + } + + _, _, err = clientTest.GetTraces("") + if err == nil { + t.Error("DeleteTraces failed: traces section still exists") + } + + err = clientTest.DeleteTraces("", version) + if err == nil { + t.Error("Should throw error, non existent Traces") + version++ + } + + // Re-create the traces section for the following tests. + traces = &models.Traces{ + Entries: models.TraceEntries{ + &models.TraceEntry{Trace: "h1 sink buf1 level developer verbosity complete start now"}, + &models.TraceEntry{Trace: "h2 sink buf2 level developer verbosity complete start now"}, + }, + } + err = clientTest.CreateTraces(traces, "", version) + if err != nil { + t.Fatal(err) + } else { + version++ + } +} + +func TestAddRemoveTraceEntries(t *testing.T) { + v, traces, err := clientTest.GetTraces("") + if err != nil { + t.Fatal(err) + } + if len(traces.Entries) == 0 { + t.Fatal("zero log entries found in testConf") + } + + initial := len(traces.Entries) + + entry := &models.TraceEntry{Trace: "h2 oops buf2"} + + if err = clientTest.CreateTraceEntry(entry, "", v); err != nil { + t.Fatal("CreateTraceEntry: ", err) + } + version++ + + v, traces, err = clientTest.GetTraces("") + if err != nil { + t.Fatal(err) + } + + if len(traces.Entries) != initial+1 { + for i, e := range traces.Entries { + t.Logf("trace entry %d: %v", i, e.Trace) + } + t.Fatal("wrong number of entries") + } + + if err = clientTest.DeleteTraceEntry(entry, "", v); err != nil { + t.Fatal("DeleteTraceEntry: ", err) + } + version++ + + v, traces, err = clientTest.GetTraces("") + if err != nil { + t.Fatal(err) + } + + if len(traces.Entries) != initial { + for i, e := range traces.Entries { + t.Logf("trace entry %d: %v", i, e.Trace) + } + t.Fatal("wrong number of entries") + } +}