diff --git a/go.mod b/go.mod index c7293c9..0563ab5 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ toolchain go1.22.2 require ( github.com/AlekSi/pointer v1.2.0 - github.com/beevik/etree v1.3.0 + github.com/beevik/etree v1.4.1 github.com/google/go-cmp v0.6.0 github.com/gorilla/mux v1.8.1 github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 @@ -14,35 +14,35 @@ require ( github.com/jellydator/ttlcache/v3 v3.2.0 github.com/kylelemons/godebug v1.1.0 github.com/olekukonko/tablewriter v0.0.5 - github.com/openconfig/gnmi v0.10.0 - github.com/openconfig/gnmic/pkg/api v0.1.2 + github.com/openconfig/gnmi v0.11.0 + github.com/openconfig/gnmic/pkg/api v0.1.8 github.com/openconfig/gnmic/pkg/target v0.1.4 github.com/openconfig/gnmic/pkg/types v0.1.2 github.com/openconfig/goyang v1.6.0 github.com/openconfig/ygot v0.29.19 - github.com/prometheus/client_golang v1.19.1 - github.com/scrapli/scrapligo v1.2.0 + github.com/prometheus/client_golang v1.20.1 + github.com/scrapli/scrapligo v1.3.2 github.com/sdcio/cache v0.0.34 github.com/sdcio/schema-server v0.0.20 github.com/sdcio/sdc-protos v0.0.27 github.com/sdcio/yang-parser v0.0.5 github.com/sirupsen/logrus v1.9.3 - github.com/spf13/cobra v1.8.0 + github.com/spf13/cobra v1.8.1 github.com/spf13/pflag v1.0.5 go.uber.org/mock v0.4.0 golang.org/x/sync v0.8.0 google.golang.org/grpc v1.65.0 google.golang.org/protobuf v1.34.2 gopkg.in/yaml.v2 v2.4.0 - sigs.k8s.io/controller-runtime v0.18.5 + sigs.k8s.io/controller-runtime v0.19.0 ) require ( - cloud.google.com/go/compute/metadata v0.3.0 // indirect + cloud.google.com/go/compute/metadata v0.5.0 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/bufbuild/protocompile v0.8.0 // indirect + github.com/bufbuild/protocompile v0.14.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/creack/pty v1.1.21 // indirect + github.com/creack/pty v1.1.23 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dgraph-io/badger/v4 v4.2.0 // indirect github.com/dgraph-io/ristretto v0.1.1 // indirect @@ -64,7 +64,7 @@ require ( github.com/google/gofuzz v1.2.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/jhump/protoreflect v1.15.5 // indirect + github.com/jhump/protoreflect v1.16.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.17.9 // indirect @@ -74,8 +74,7 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/openconfig/gnmic/pkg/path v0.34.1 // indirect - github.com/openconfig/gnmic/pkg/utils v0.1.0 // indirect + github.com/openconfig/gnmic/pkg/utils v0.1.1 // indirect github.com/openconfig/grpctunnel v0.1.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/prometheus/client_model v0.6.1 // indirect @@ -94,15 +93,15 @@ require ( golang.org/x/term v0.23.0 // indirect golang.org/x/text v0.17.0 // indirect golang.org/x/time v0.6.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240812133136-8ffd90a71988 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240820151423-278611b39280 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/api v0.31.0 // indirect k8s.io/apimachinery v0.31.0 // indirect k8s.io/client-go v0.31.0 // indirect k8s.io/klog/v2 v2.130.1 // indirect - k8s.io/kube-openapi v0.0.0-20240812233141-91dab695df6f // indirect - k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect + k8s.io/kube-openapi v0.0.0-20240816214639-573285566f34 // indirect + k8s.io/utils v0.0.0-20240821151609-f90d01438635 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect sigs.k8s.io/yaml v1.4.0 // indirect diff --git a/go.sum b/go.sum index 1b30f8b..91599fb 100644 --- a/go.sum +++ b/go.sum @@ -1,19 +1,19 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc= -cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= +cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY= +cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY= github.com/AlekSi/pointer v1.2.0 h1:glcy/gc4h8HnG2Z3ZECSzZ1IX1x2JxRVuDzaJwQE0+w= github.com/AlekSi/pointer v1.2.0/go.mod h1:gZGfd3dpW4vEc/UlyfKKi1roIqcCgwOIvb0tSNSBle0= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/beevik/etree v1.3.0 h1:hQTc+pylzIKDb23yYprodCWWTt+ojFfUZyzU09a/hmU= -github.com/beevik/etree v1.3.0/go.mod h1:aiPf89g/1k3AShMVAzriilpcE4R/Vuor90y83zVZWFc= +github.com/beevik/etree v1.4.1 h1:PmQJDDYahBGNKDcpdX8uPy1xRCwoCGVUiW669MEirVI= +github.com/beevik/etree v1.4.1/go.mod h1:gPNJNaBGVZ9AwsidazFZyygnd+0pAU38N4D+WemwKNs= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bufbuild/protocompile v0.8.0 h1:9Kp1q6OkS9L4nM3FYbr8vlJnEwtbpDPQlQOVXfR+78s= -github.com/bufbuild/protocompile v0.8.0/go.mod h1:+Etjg4guZoAqzVk2czwEQP12yaxLJ8DxuqCJ9qHdH94= +github.com/bufbuild/protocompile v0.14.0 h1:z3DW4IvXE5G/uTOnSQn+qwQQxvhckkTWLS/0No/o7KU= +github.com/bufbuild/protocompile v0.14.0/go.mod h1:N6J1NYzkspJo3ZwyL4Xjvli86XOj1xq4qAasUFxGups= github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= @@ -24,10 +24,10 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= -github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= +github.com/creack/pty v1.1.23 h1:4M6+isWdcStXEf15G/RbrMPOQj1dZ7HPZCGwE4kOeP0= +github.com/creack/pty v1.1.23/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= @@ -49,7 +49,6 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg= github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= @@ -70,7 +69,6 @@ github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDsl github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= @@ -135,8 +133,8 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jellydator/ttlcache/v3 v3.2.0 h1:6lqVJ8X3ZaUwvzENqPAobDsXNExfUJd61u++uW8a3LE= github.com/jellydator/ttlcache/v3 v3.2.0/go.mod h1:hi7MGFdMAwZna5n2tuvh63DvFLzVKySzCVW6+0gA2n4= -github.com/jhump/protoreflect v1.15.5 h1:Y9eEjV3vwdX57VV3uuxtknBRxA2vQ3LOifKOOC2n5ec= -github.com/jhump/protoreflect v1.15.5/go.mod h1:jCHoyYQIJnaabEYnbGwyo9hUqfyUMTbJw/tAut5t97E= +github.com/jhump/protoreflect v1.16.0 h1:54fZg+49widqXYQ0b+usAFHbMkBGR4PpXrsHc8+TBDg= +github.com/jhump/protoreflect v1.16.0/go.mod h1:oYPd7nPvcBw/5wlDfm/AVmU9zH9BgqGCI469pGxfj/8= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= @@ -176,20 +174,17 @@ github.com/onsi/ginkgo/v2 v2.20.0 h1:PE84V2mHqoT1sglvHc8ZdQtPcwmvvt29WLEEO3xmdZw github.com/onsi/ginkgo/v2 v2.20.0/go.mod h1:lG9ey2Z29hR41WMVthyJBGUBcBhGOtoPF2VFMvBXFCI= github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= -github.com/openconfig/gnmi v0.10.0 h1:kQEZ/9ek3Vp2Y5IVuV2L/ba8/77TgjdXg505QXvYmg8= github.com/openconfig/gnmi v0.10.0/go.mod h1:Y9os75GmSkhHw2wX8sMsxfI7qRGAEcDh8NTa5a8vj6E= -github.com/openconfig/gnmic/pkg/api v0.1.2 h1:bbLOVDbn8RKAHSP2gFqbIss/TgS269iJ53qMqvkLDa8= -github.com/openconfig/gnmic/pkg/api v0.1.2/go.mod h1:yPdo5ZIV3WUb9yQJuy9LosO/kTKzD6dTKzb0X8e5AVM= -github.com/openconfig/gnmic/pkg/path v0.34.1 h1:enMPNv4il5cNjXwyPZSmmQlRPONdTlPN7VMh3Y5dfh4= -github.com/openconfig/gnmic/pkg/path v0.34.1/go.mod h1:BtTs36pDCN8hbEpdSSCfmhgSqyVj0Vjokh/sutU1bC0= +github.com/openconfig/gnmi v0.11.0 h1:H7pLIb/o3xObu3+x0Fv9DCK7TH3FUh7mNwbYe+34hFw= +github.com/openconfig/gnmi v0.11.0/go.mod h1:9oJSQPPCpNvfMRj8e4ZoLVAw4wL8HyxXbiDlyuexCGU= +github.com/openconfig/gnmic/pkg/api v0.1.8 h1:3N9oFduU204Ta3g7r3dC4pcYdT/Dr7PrSC13Lyc7Ymc= +github.com/openconfig/gnmic/pkg/api v0.1.8/go.mod h1:WbME6stT7zcYzQnCwQO42a23WgZRqFQVGVsp0C4FHtc= github.com/openconfig/gnmic/pkg/target v0.1.4 h1:zO7gOke9RVnkockM2lZv5MqgNdfysUSqxyp91OnEjOQ= github.com/openconfig/gnmic/pkg/target v0.1.4/go.mod h1:hNmkYvuYFaUtbFWVm0PuvctJ70ku+fotxZxmkr5oeWM= -github.com/openconfig/gnmic/pkg/testutils v0.1.0 h1:Mw6LKGqzXQnrI9fCtaCAp3pFDy2B0+rKQFtjCqQWJwM= -github.com/openconfig/gnmic/pkg/testutils v0.1.0/go.mod h1:/JyhCq6rSbQdmeCbPIQjD8K3ArOqGQZFKUKKUpebonk= github.com/openconfig/gnmic/pkg/types v0.1.2 h1:FRjERZrbcuqhwRKHZ7ux4UXr4fc1DlgXvH37Iysu4Co= github.com/openconfig/gnmic/pkg/types v0.1.2/go.mod h1:Gwc9suBy/s17bP8BaCrp3dE5E+RkcHqVIkFqdXopog4= -github.com/openconfig/gnmic/pkg/utils v0.1.0 h1:MqRhW8oJdPpBb1UprbnpDxciBJVjl/Gw97xuNoBG3Vs= -github.com/openconfig/gnmic/pkg/utils v0.1.0/go.mod h1:DQm/e8cdRwdmUORjODWteDU0HG0CWNYBAhLWqnPQegE= +github.com/openconfig/gnmic/pkg/utils v0.1.1 h1:LgfI/c/O8VpuZg1RS9G6XEeRhAQSlOalE52/8tsdf00= +github.com/openconfig/gnmic/pkg/utils v0.1.1/go.mod h1:DQm/e8cdRwdmUORjODWteDU0HG0CWNYBAhLWqnPQegE= github.com/openconfig/goyang v0.0.0-20200115183954-d0a48929f0ea/go.mod h1:dhXaV0JgHJzdrHi2l+w0fZrwArtXL7jEFoiqLEdmkvU= github.com/openconfig/goyang v1.6.0 h1:JjnPbLY1/y28VyTO67LsEV0TaLWNiZyDcsppGq4F4is= github.com/openconfig/goyang v1.6.0/go.mod h1:sdNZi/wdTZyLNBNfgLzmmbi7kISm7FskMDKKzMY+x1M= @@ -207,8 +202,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= -github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= +github.com/prometheus/client_golang v1.20.1 h1:IMJXHOD6eARkQpxo8KkhgEVFlBNm+nkrFUyGlIu7Na8= +github.com/prometheus/client_golang v1.20.1/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= @@ -226,8 +221,8 @@ github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99 github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/scrapli/scrapligo v1.2.0 h1:jn83HPkKAPDzvth7i9V/70BAPuVgriU+/tHHv3eAtC4= -github.com/scrapli/scrapligo v1.2.0/go.mod h1:rRx/rT2oNPYztiT3/ik0FRR/Ro7AdzN/eR9AtF8A81Y= +github.com/scrapli/scrapligo v1.3.2 h1:9D5TFM/DlqAijqH18uNHygbNos0ReDsJl/vhMRObkhg= +github.com/scrapli/scrapligo v1.3.2/go.mod h1:VnmqW27qUU1NOt6PNllZNJqAXQddn2vqZpk7g5qxebw= github.com/sdcio/cache v0.0.34 h1:mDxIPN9r339yHykCPJZFKX0va8RMoAscErbEkAcql7E= github.com/sdcio/cache v0.0.34/go.mod h1:u/rIbC03e6k+J19VWo8DwJdBWY0vzNZ25viXRmrS+5I= github.com/sdcio/schema-server v0.0.20 h1:TpWSuWZA3oJZGTEhcuaWOsuwVDLkpuK0h1aUcPO41I0= @@ -242,8 +237,8 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= -github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= +github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= +github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -396,8 +391,8 @@ google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20210811021853-ddbe55d93216/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240812133136-8ffd90a71988 h1:V71AcdLZr2p8dC9dbOIMCpqi4EmRl8wUwnJzXXLmbmc= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240812133136-8ffd90a71988/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240820151423-278611b39280 h1:XQMA2e105XNlEZ8NRF0HqnUOZzP14sUSsgL09kpdNnU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240820151423-278611b39280/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= @@ -447,20 +442,20 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= k8s.io/api v0.31.0 h1:b9LiSjR2ym/SzTOlfMHm1tr7/21aD7fSkqgD/CVJBCo= k8s.io/api v0.31.0/go.mod h1:0YiFF+JfFxMM6+1hQei8FY8M7s1Mth+z/q7eF1aJkTE= -k8s.io/apiextensions-apiserver v0.30.1 h1:4fAJZ9985BmpJG6PkoxVRpXv9vmPUOVzl614xarePws= -k8s.io/apiextensions-apiserver v0.30.1/go.mod h1:R4GuSrlhgq43oRY9sF2IToFh7PVlF1JjfWdoG3pixk4= +k8s.io/apiextensions-apiserver v0.31.0 h1:fZgCVhGwsclj3qCw1buVXCV6khjRzKC5eCFt24kyLSk= +k8s.io/apiextensions-apiserver v0.31.0/go.mod h1:b9aMDEYaEe5sdK+1T0KU78ApR/5ZVp4i56VacZYEHxk= k8s.io/apimachinery v0.31.0 h1:m9jOiSr3FoSSL5WO9bjm1n6B9KROYYgNZOb4tyZ1lBc= k8s.io/apimachinery v0.31.0/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= k8s.io/client-go v0.31.0 h1:QqEJzNjbN2Yv1H79SsS+SWnXkBgVu4Pj3CJQgbx0gI8= k8s.io/client-go v0.31.0/go.mod h1:Y9wvC76g4fLjmU0BA+rV+h2cncoadjvjjkkIGoTLcGU= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kube-openapi v0.0.0-20240812233141-91dab695df6f h1:bnWtxXWdAl5bVOCEPoNdvMkyj6cTW3zxHuwKIakuV9w= -k8s.io/kube-openapi v0.0.0-20240812233141-91dab695df6f/go.mod h1:G0W3eI9gG219NHRq3h5uQaRBl4pj4ZpwzRP5ti8y770= -k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= -k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -sigs.k8s.io/controller-runtime v0.18.5 h1:nTHio/W+Q4aBlQMgbnC5hZb4IjIidyrizMai9P6n4Rk= -sigs.k8s.io/controller-runtime v0.18.5/go.mod h1:TVoGrfdpbA9VRFaRnKgk9P5/atA0pMwq+f+msb9M8Sg= +k8s.io/kube-openapi v0.0.0-20240816214639-573285566f34 h1:/amS69DLm09mtbFtN3+LyygSFohnYGMseF8iv+2zulg= +k8s.io/kube-openapi v0.0.0-20240816214639-573285566f34/go.mod h1:G0W3eI9gG219NHRq3h5uQaRBl4pj4ZpwzRP5ti8y770= +k8s.io/utils v0.0.0-20240821151609-f90d01438635 h1:2wThSvJoW/Ncn9TmQEYXRnevZXi2duqHWf5OX9S3zjI= +k8s.io/utils v0.0.0-20240821151609-f90d01438635/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/controller-runtime v0.19.0 h1:nWVM7aq+Il2ABxwiCizrVDSlmDcshi9llbaFbC0ji/Q= +sigs.k8s.io/controller-runtime v0.19.0/go.mod h1:iRmWllt8IlaLjvTTDLhRBXIEtkCK6hwVBJJsYS9Ajf4= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= diff --git a/pkg/datastore/datastore_rpc.go b/pkg/datastore/datastore_rpc.go index 52c51de..e4af877 100644 --- a/pkg/datastore/datastore_rpc.go +++ b/pkg/datastore/datastore_rpc.go @@ -691,21 +691,27 @@ func (d *Datastore) storeSyncMsg(ctx context.Context, syncup *target.SyncUpdate, store = cachepb.Store_STATE } } - - // TODO:[KR] convert update typedValue if needed - cUpd, err := d.cacheClient.NewUpdate(upd) + expandedUpds, err := d.expandUpdateKeysAsLeaf(ctx, upd) if err != nil { - log.Errorf("datastore %s failed to create update from %v: %v", d.config.Name, upd, err) + log.Errorf("datastore %s failed expanding Update keys as leafs %v: %v", d.config.Name, upd.GetPath(), err) continue } + for _, expUpd := range expandedUpds { + // TODO:[KR] convert update typedValue if needed + cUpd, err := d.cacheClient.NewUpdate(expUpd) + if err != nil { + log.Errorf("datastore %s failed to create update from %v: %v", d.config.Name, upd, err) + continue + } - rctx, cancel := context.WithTimeout(ctx, time.Minute) // TODO:[KR] make this timeout configurable ? - defer cancel() - err = d.cacheClient.Modify(rctx, d.Config().Name, &cache.Opts{ - Store: store, - }, nil, []*cache.Update{cUpd}) - if err != nil { - log.Errorf("datastore %s failed to send modify request to cache: %v", d.config.Name, err) + rctx, cancel := context.WithTimeout(ctx, time.Minute) // TODO:[KR] make this timeout configurable ? + defer cancel() + err = d.cacheClient.Modify(rctx, d.Config().Name, &cache.Opts{ + Store: store, + }, nil, []*cache.Update{cUpd}) + if err != nil { + log.Errorf("datastore %s failed to send modify request to cache: %v", d.config.Name, err) + } } } } @@ -1213,7 +1219,7 @@ func (d *Datastore) runDeviationUpdate(ctx context.Context, dm map[string]sdcpb. } } - intendedUpdates, err := d.readIntendedStoreKeysMeta(ctx) + intendedUpdates, err := d.readStoreKeysMeta(ctx, cachepb.Store_INTENDED) if err != nil { log.Error(err) return diff --git a/pkg/datastore/intent_rpc_set_update.go b/pkg/datastore/intent_rpc_set_update.go index 2cc5414..d032fac 100644 --- a/pkg/datastore/intent_rpc_set_update.go +++ b/pkg/datastore/intent_rpc_set_update.go @@ -18,6 +18,7 @@ import ( "context" "errors" "fmt" + "math" "sort" "strings" @@ -31,6 +32,32 @@ import ( "google.golang.org/protobuf/proto" ) +func (d *Datastore) populateTreeWithRunning(ctx context.Context, tc *tree.TreeContext, r *tree.RootEntry) error { + // read all the keys from the cache intended store but just the keys, no values are populated + configIndex, err := d.readStoreKeysMeta(ctx, cachepb.Store_CONFIG) + if err != nil { + return err + } + ps := make([][]string, 0, len(configIndex)) + + for _, v := range configIndex { + ps = append(ps, v[0].GetPath()) + } + upds, err := tc.ReadRunningMultiple(ctx, ps) + if err != nil { + return err + } + for _, upd := range upds { + newUpd := cache.NewUpdate(upd.GetPath(), upd.Bytes(), math.MaxInt32, "running", 0) + err = r.AddCacheUpdateRecursive(ctx, newUpd, false) + if err != nil { + return err + } + } + + return nil +} + func (d *Datastore) populateTree(ctx context.Context, req *sdcpb.SetIntentRequest, tc *tree.TreeContext) (r *tree.RootEntry, err error) { // create a new Tree root, err := tree.NewTreeRoot(ctx, tc) @@ -39,7 +66,7 @@ func (d *Datastore) populateTree(ctx context.Context, req *sdcpb.SetIntentReques } // read all the keys from the cache intended store but just the keys, no values are populated - storeIndex, err := d.readIntendedStoreKeysMeta(ctx) + storeIndex, err := d.readStoreKeysMeta(ctx, cachepb.Store_INTENDED) if err != nil { return nil, err } @@ -96,12 +123,6 @@ func (d *Datastore) populateTree(ctx context.Context, req *sdcpb.SetIntentReques } } - // // populate schema within the tree - // err = root.Walk(tree.TreeWalkerSchemaRetriever(ctx, d.getValidationClient())) - // if err != nil { - // return nil, err - // } - fmt.Printf("Tree:%s\n", root.String()) return root, nil @@ -156,9 +177,38 @@ func (d *Datastore) SetIntentUpdate(ctx context.Context, req *sdcpb.SetIntentReq log.Debugf("finish insertion phase") root.FinishInsertionPhase() + err = d.populateTreeWithRunning(ctx, tc, root) + if err != nil { + return nil, err + } + + // perform validation + // we use a channel and cumulate all the errors + validationErrors := []error{} + validationErrChan := make(chan error) + go func() { + root.Validate(validationErrChan) + close(validationErrChan) + }() + + // read from the Error channel + for e := range validationErrChan { + validationErrors = append(validationErrors, e) + } + + // check if errors are received + // If so, join them and return the cumulated errors + if len(validationErrors) > 0 { + return nil, fmt.Errorf("cumulated validation errors:\n%v", errors.Join(validationErrors...)) + } + + logger.Debug("intent is validated") + + fmt.Println(root.String()) + // retrieve the data that is meant to be send southbound (towards the device) updates := root.GetHighestPrecedence(true) - deletes := root.GetDeletes() + deletes := root.GetDeletes(true) // set request to be applied into the candidate setDataReq := &sdcpb.SetDataRequest{ @@ -175,44 +225,25 @@ func (d *Datastore) SetIntentUpdate(ctx context.Context, req *sdcpb.SetIntentReq // add all the highes priority updates to the setDataReq for _, u := range updates { - sdcpbUpd, err := d.cacheUpdateToUpdate(ctx, u) + sdcpbUpd, err := d.cacheUpdateToUpdate(ctx, u.Update) if err != nil { return nil, err } setDataReq.Update = append(setDataReq.Update, sdcpbUpd) } - // perform validation - // we use a channel and cumulate all the errors - validationErrors := []error{} - validationErrChan := make(chan error) - go func() { - root.Validate(validationErrChan) - close(validationErrChan) - }() - - // read from the Error channel - for e := range validationErrChan { - validationErrors = append(validationErrors, e) - } - - // check if errors are received - // If so, join them and return the cumulated errors - if len(validationErrors) > 0 { - return nil, fmt.Errorf("cumulated validation errors:\n%v", errors.Join(validationErrors...)) - } - // add all the deletes to the setDataReq for _, u := range deletes { - sdcpbUpd, err := d.cacheUpdateToUpdate(ctx, cache.NewUpdate(u, []byte{}, req.Priority, req.Intent, 0)) + p, err := d.toPath(ctx, u) if err != nil { return nil, err } - setDataReq.Delete = append(setDataReq.Delete, sdcpbUpd.GetPath()) + setDataReq.Delete = append(setDataReq.Delete, p) } log.Debug(prototext.Format(setDataReq)) + // set the response data indicationg the changes to the device setIntentResponse := &sdcpb.SetIntentResponse{ Update: append(setDataReq.Update, setDataReq.Replace...), Delete: setDataReq.GetDelete(), @@ -239,10 +270,9 @@ func (d *Datastore) SetIntentUpdate(ctx context.Context, req *sdcpb.SetIntentReq return nil, err } setIntentResponse.Warnings = dataResp.GetWarnings() - } - logger.Debug("intent is validated") - log.Infof("ds=%s intent=%s: intent applied", req.GetName(), req.GetIntent()) + log.Infof("ds=%s intent=%s: intent applied", req.GetName(), req.GetIntent()) + } ///////////////////////////////////// // update intent in intended store // @@ -253,7 +283,7 @@ func (d *Datastore) SetIntentUpdate(ctx context.Context, req *sdcpb.SetIntentReq deletesOwner := root.GetDeletesForOwner(req.GetIntent()) // logging - strSl := tree.Map(updates, func(u *cache.Update) string { return u.String() }) + strSl := tree.Map(updates.ToCacheUpdateSlice(), func(u *cache.Update) string { return u.String() }) log.Debugf("Updates\n%s", strings.Join(strSl, "\n")) strSl = deletes.StringSlice() @@ -277,7 +307,7 @@ func (d *Datastore) SetIntentUpdate(ctx context.Context, req *sdcpb.SetIntentReq // fast and optimistic writeback to the config store err = d.cacheClient.Modify(ctx, d.Name(), &cache.Opts{ Store: cachepb.Store_CONFIG, - }, deletes.ToStringSlice(), updates) + }, deletes.ToStringSlice(), updates.ToCacheUpdateSlice()) if err != nil { return nil, fmt.Errorf("failed updating the running config store for %s: %w", d.Name(), err) } @@ -311,8 +341,8 @@ func pathIsKeyAsLeaf(p *sdcpb.Path) bool { return ok } -func (d *Datastore) readIntendedStoreKeysMeta(ctx context.Context) (map[string]tree.UpdateSlice, error) { - entryCh, err := d.cacheClient.GetKeys(ctx, d.config.Name, cachepb.Store_INTENDED) +func (d *Datastore) readStoreKeysMeta(ctx context.Context, store cachepb.Store) (map[string]tree.UpdateSlice, error) { + entryCh, err := d.cacheClient.GetKeys(ctx, d.config.Name, store) if err != nil { return nil, err } diff --git a/pkg/datastore/intent_rpc_set_update_test.go b/pkg/datastore/intent_rpc_set_update_test.go index df4cdb0..7f6f0eb 100644 --- a/pkg/datastore/intent_rpc_set_update_test.go +++ b/pkg/datastore/intent_rpc_set_update_test.go @@ -713,12 +713,12 @@ func TestDatastore_populateTree(t *testing.T) { // get the updates that are meant to be send down towards the device updates := root.GetHighestPrecedence(!tt.NotOnlyNewOrUpdated) - if diff := testhelper.DiffCacheUpdates(tt.expectedModify, updates); diff != "" { + if diff := testhelper.DiffCacheUpdates(tt.expectedModify, updates.ToCacheUpdateSlice()); diff != "" { t.Errorf("root.GetHighestPrecedence(true) mismatch (-want +got):\n%s", diff) } // get the deletes that are meant to be send down towards the device - deletes := root.GetDeletes() + deletes := root.GetDeletes(true) if diff := testhelper.DiffDoubleStringPathSlice(tt.expectedDeletes, deletes.ToStringSlice()); diff != "" { t.Errorf("root.GetDeletes() mismatch (-want +got):\n%s", diff) } diff --git a/pkg/tree/entry.go b/pkg/tree/entry.go index af9ae8f..416971f 100644 --- a/pkg/tree/entry.go +++ b/pkg/tree/entry.go @@ -55,13 +55,13 @@ type Entry interface { // GetHighesPrio return the new cache.Update entried from the tree that are the highes priority. // If the onlyNewOrUpdated option is set to true, only the New or Updated entries will be returned // It will append to the given list and provide a new pointer to the slice - GetHighestPrecedence(u UpdateSlice, onlyNewOrUpdated bool) UpdateSlice + GetHighestPrecedence(result LeafVariantSlice, onlyNewOrUpdated bool) LeafVariantSlice // GetByOwner returns the branches Updates by owner GetByOwner(owner string, result []*LeafEntry) []*LeafEntry // markOwnerDelete Sets the delete flag on all the LeafEntries belonging to the given owner. markOwnerDelete(o string) // GetDeletes returns the cache-updates that are not updated, have no lower priority value left and hence should be deleted completely - GetDeletes(PathSlices) PathSlices + GetDeletes(paths PathSlices, aggregatePaths bool) PathSlices // Walk takes the EntryVisitor and applies it to every Entry in the tree Walk(f EntryVisitor) error // shouldDelete indicated if there is no LeafEntry left and the Entry is to be deleted @@ -96,6 +96,8 @@ type Entry interface { SdcpbPathInternal(spath []string) (*sdcpb.Path, error) // GetSchemaKeys checks for the schema of the entry, and returns the defined keys GetSchemaKeys() []string + // Returns all the entries starting from the root down to the actual Entry. + GetRootBasedEntryChain() []Entry } // sharedEntryAttributes contains the attributes shared by Entry and RootEntry @@ -257,7 +259,7 @@ func (s *sharedEntryAttributes) GetSchemaKeys() []string { // getAggregatedDeletes is called on levels that have no schema attached, meaning key schemas. // here we might delete the whole branch of the tree, if all key elements are being deleted // if not, we continue with regular deltes -func (s *sharedEntryAttributes) getAggregatedDeletes(deletes PathSlices) PathSlices { +func (s *sharedEntryAttributes) getAggregatedDeletes(deletes PathSlices, aggregatePaths bool) PathSlices { // we take a look into the level(s) up // trying to get the schema ancestor, level := s.GetFirstAncestorWithSchema() @@ -287,16 +289,16 @@ func (s *sharedEntryAttributes) getAggregatedDeletes(deletes PathSlices) PathSli } else { // otherwise continue with deletion on the childs. for _, c := range s.childs { - deletes = c.GetDeletes(deletes) + deletes = c.GetDeletes(deletes, aggregatePaths) } } return deletes } - return s.getRegularDeletes(deletes) + return s.getRegularDeletes(deletes, aggregatePaths) } // getRegularDeletes performs deletion calculation on elements that have a schema attached. -func (s *sharedEntryAttributes) getRegularDeletes(deletes PathSlices) PathSlices { +func (s *sharedEntryAttributes) getRegularDeletes(deletes PathSlices, aggregate bool) PathSlices { // if entry is a container type, check the keys, to be able to // issue a delte for the whole branch at once via keys switch s.schema.GetSchema().(type) { @@ -319,22 +321,22 @@ func (s *sharedEntryAttributes) getRegularDeletes(deletes PathSlices) PathSlices } for _, e := range s.childs { - deletes = e.GetDeletes(deletes) + deletes = e.GetDeletes(deletes, aggregate) } return deletes } // GetDeletes calculate the deletes that need to be send to the device. -func (s *sharedEntryAttributes) GetDeletes(deletes PathSlices) PathSlices { +func (s *sharedEntryAttributes) GetDeletes(deletes PathSlices, aggregatePaths bool) PathSlices { // if the actual level has no schema assigned we're on a key level // element. Hence we try deletion via aggregation - if s.schema == nil { - return s.getAggregatedDeletes(deletes) + if s.schema == nil && aggregatePaths { + return s.getAggregatedDeletes(deletes, aggregatePaths) } // else perform regular deletion - return s.getRegularDeletes(deletes) + return s.getRegularDeletes(deletes, aggregatePaths) } @@ -342,6 +344,10 @@ func (s *sharedEntryAttributes) GetDeletes(deletes PathSlices) PathSlices { // if the parent has no schema (is a key element in the tree) it will recurs the call to the parents parent. // the level of recursion is indicated via the levelUp attribute func (s *sharedEntryAttributes) GetFirstAncestorWithSchema() (Entry, int) { + // if root node is reached + if s.parent == nil { + return nil, 0 + } // check if the parent has a schema if s.parent.GetSchema() != nil { // if so return it with level 1 @@ -451,11 +457,11 @@ func (s *sharedEntryAttributes) tryLoading(ctx context.Context, path []string) ( // GetHighestPrecedence goes through the whole branch and returns the new and updated cache.Updates. // These are the updated that will be send to the device. -func (s *sharedEntryAttributes) GetHighestPrecedence(result UpdateSlice, onlyNewOrUpdated bool) UpdateSlice { +func (s *sharedEntryAttributes) GetHighestPrecedence(result LeafVariantSlice, onlyNewOrUpdated bool) LeafVariantSlice { // get the highes precedence LeafeVariant and add it to the list lv := s.leafVariants.GetHighestPrecedence(onlyNewOrUpdated) if lv != nil { - result = append(result, lv.Update) + result = append(result, lv) } // continue with childs. Childs are part of choices, process only the "active" (highes precedence) childs @@ -465,6 +471,13 @@ func (s *sharedEntryAttributes) GetHighestPrecedence(result UpdateSlice, onlyNew return result } +func (s *sharedEntryAttributes) GetRootBasedEntryChain() []Entry { + if s.IsRoot() { + return []Entry{} + } + return append(s.parent.GetRootBasedEntryChain(), s) +} + // getHighestPrecedenceValueOfBranch goes through all the child branches to find the highes // precedence value (lowest priority value) for the entire branch and returns it. func (s *sharedEntryAttributes) getHighestPrecedenceValueOfBranch() int32 { @@ -554,6 +567,14 @@ func (s *sharedEntryAttributes) validateLeafListMinMaxAttributes(errchan chan<- } } +// func (s *sharedEntryAttributes) validateUnique(errchan chan<- error) { +// if schema := s.schema.GetLeaflist(); schema != nil { +// if schema. { +// return +// } +// } +// } + // func (s *sharedEntryAttributes) validateLength(errchan chan<- error) { // if schema := s.schema.GetField(); schema != nil { // if schema.GetType().GetLength() == "" { @@ -576,7 +597,7 @@ func (s *sharedEntryAttributes) validatePattern(errchan chan<- error) { if len(schema.Type.Patterns) == 0 { return } - lv := s.leafVariants.GetByOwner(s.treeContext.actualOwner) + lv := s.leafVariants.GetHighestPrecedence(false) tv, err := lv.Update.Value() if err != nil { errchan <- fmt.Errorf("failed reading value from %s LeafVariant %v: %w", s.Path(), lv, err) @@ -771,13 +792,14 @@ func (s *sharedEntryAttributes) SdcpbPathInternal(spath []string) (*sdcpb.Path, // path. if s.schema != nil { switch s.schema.GetSchema().(type) { - case *sdcpb.SchemaElem_Container: + case *sdcpb.SchemaElem_Container, *sdcpb.SchemaElem_Field, *sdcpb.SchemaElem_Leaflist: pe := &sdcpb.PathElem{ Name: s.pathElemName, Key: map[string]string{}, } p.Elem = append(p.Elem, pe) } + // the element does not have a schema attached, hence we need to add a key to // the last element that was pushed to the pathElems } else { @@ -834,8 +856,9 @@ func (s *sharedEntryAttributes) AddCacheUpdateRecursive(ctx context.Context, c * } } else { // if LeafVaraint with same owner does not exist, add the new entry - s.leafVariants = append(s.leafVariants, NewLeafEntry(c, new)) + s.leafVariants = append(s.leafVariants, NewLeafEntry(c, new, s)) } + return nil } @@ -930,14 +953,14 @@ func (r *RootEntry) GetDeletesForOwner(owner string) PathSlices { // GetHighesPrecedence return the new cache.Update entried from the tree that are the highes priority. // If the onlyNewOrUpdated option is set to true, only the New or Updated entries will be returned // It will append to the given list and provide a new pointer to the slice -func (r *RootEntry) GetHighestPrecedence(onlyNewOrUpdated bool) UpdateSlice { - return r.sharedEntryAttributes.GetHighestPrecedence(make(UpdateSlice, 0), onlyNewOrUpdated) +func (r *RootEntry) GetHighestPrecedence(onlyNewOrUpdated bool) LeafVariantSlice { + return r.sharedEntryAttributes.GetHighestPrecedence(make(LeafVariantSlice, 0), onlyNewOrUpdated) } // GetDeletes returns the paths that due to the Tree content are to be deleted from the southbound device. -func (r *RootEntry) GetDeletes() PathSlices { +func (r *RootEntry) GetDeletes(aggregatePaths bool) PathSlices { deletes := PathSlices{} - return r.sharedEntryAttributes.GetDeletes(deletes) + return r.sharedEntryAttributes.GetDeletes(deletes, aggregatePaths) } // getTreeContext returns the handle to the TreeContext diff --git a/pkg/tree/entry_test.go b/pkg/tree/entry_test.go index 1a12212..e3bfb13 100644 --- a/pkg/tree/entry_test.go +++ b/pkg/tree/entry_test.go @@ -10,7 +10,6 @@ import ( "github.com/google/go-cmp/cmp" "github.com/sdcio/data-server/mocks/mockschemaclientbound" "github.com/sdcio/data-server/pkg/cache" - SchemaClient "github.com/sdcio/data-server/pkg/datastore/clients/schema" "github.com/sdcio/data-server/pkg/utils/testhelper" sdcpb "github.com/sdcio/sdc-protos/sdcpb" "go.uber.org/mock/gomock" @@ -124,7 +123,7 @@ func Test_Entry_One(t *testing.T) { highprec := root.GetHighestPrecedence(true) // diff the result with the expected - if diff := testhelper.DiffCacheUpdates([]*cache.Update{u1, u3}, highprec); diff != "" { + if diff := testhelper.DiffCacheUpdates([]*cache.Update{u1, u3}, highprec.ToCacheUpdateSlice()); diff != "" { t.Errorf("root.GetHighesPrio() mismatch (-want +got):\n%s", diff) } }) @@ -181,7 +180,7 @@ func Test_Entry_Two(t *testing.T) { highprec := root.GetHighestPrecedence(true) // diff the result with the expected - if diff := testhelper.DiffCacheUpdates([]*cache.Update{n1}, highprec); diff != "" { + if diff := testhelper.DiffCacheUpdates([]*cache.Update{n1}, highprec.ToCacheUpdateSlice()); diff != "" { t.Errorf("root.GetHighesPrio() mismatch (-want +got):\n%s", diff) } } @@ -229,7 +228,7 @@ func Test_Entry_Three(t *testing.T) { highpri := root.GetHighestPrecedence(false) // diff the result with the expected - if diff := testhelper.DiffCacheUpdates([]*cache.Update{u1, u2, u3, u4}, highpri); diff != "" { + if diff := testhelper.DiffCacheUpdates([]*cache.Update{u1, u2, u3, u4}, highpri.ToCacheUpdateSlice()); diff != "" { t.Errorf("root.GetHighesPrio() mismatch (-want +got):\n%s", diff) } }) @@ -242,7 +241,7 @@ func Test_Entry_Three(t *testing.T) { highpri := root.GetHighestPrecedence(true) // diff the result with the expected - if diff := testhelper.DiffCacheUpdates([]*cache.Update{}, highpri); diff != "" { + if diff := testhelper.DiffCacheUpdates([]*cache.Update{}, highpri.ToCacheUpdateSlice()); diff != "" { t.Errorf("root.GetHighesPrio() mismatch (-want +got):\n%s", diff) } }) @@ -288,7 +287,7 @@ func Test_Entry_Three(t *testing.T) { t.Run("Check the old entries are gone", func(t *testing.T) { highpri := root.GetHighestPrecedence(true) // diff the result with the expected - if diff := testhelper.DiffCacheUpdates([]*cache.Update{n1, n2}, highpri); diff != "" { + if diff := testhelper.DiffCacheUpdates([]*cache.Update{n1, n2}, highpri.ToCacheUpdateSlice()); diff != "" { t.Errorf("root.GetHighesPrio() mismatch (-want +got):\n%s", diff) } }) @@ -344,7 +343,7 @@ func Test_Entry_Four(t *testing.T) { highprec := root.GetHighestPrecedence(false) // diff the result with the expected - if diff := testhelper.DiffCacheUpdates([]*cache.Update{u1o1, u2o1, u3, u4}, highprec); diff != "" { + if diff := testhelper.DiffCacheUpdates([]*cache.Update{u1o1, u2o1, u3, u4}, highprec.ToCacheUpdateSlice()); diff != "" { t.Errorf("root.GetHighesPrio() mismatch (-want +got):\n%s", diff) } }) @@ -389,7 +388,7 @@ func Test_Entry_Four(t *testing.T) { t.Run("Check the old entries are gone from highest", func(t *testing.T) { highpri := root.GetHighestPrecedence(true) // diff the result with the expected - if diff := testhelper.DiffCacheUpdates([]*cache.Update{n1, n2, u1o2, u2o2}, highpri); diff != "" { + if diff := testhelper.DiffCacheUpdates([]*cache.Update{n1, n2, u1o2, u2o2}, highpri.ToCacheUpdateSlice()); diff != "" { t.Errorf("root.GetHighesPrio() mismatch (-want +got):\n%s", diff) } }) @@ -397,7 +396,7 @@ func Test_Entry_Four(t *testing.T) { t.Run("Check the old entries are gone from highest (only New Or Updated)", func(t *testing.T) { highpri := root.GetHighestPrecedence(true) // diff the result with the expected - if diff := testhelper.DiffCacheUpdates([]*cache.Update{u1o2, u2o2, n1, n2}, highpri); diff != "" { + if diff := testhelper.DiffCacheUpdates([]*cache.Update{u1o2, u2o2, n1, n2}, highpri.ToCacheUpdateSlice()); diff != "" { t.Errorf("root.GetHighesPrio() mismatch (-want +got):\n%s", diff) } }) @@ -620,7 +619,7 @@ func Test_Entry_Delete_Aggregation(t *testing.T) { root.FinishInsertionPhase() // retrieve the Deletes - deletesSlices := root.GetDeletes() + deletesSlices := root.GetDeletes(true) // process the result for comparison deletes := make([]string, 0, len(deletesSlices)) @@ -643,7 +642,7 @@ func Test_Entry_Delete_Aggregation(t *testing.T) { } // getSchemaClientBound creates a SchemaClientBound mock that responds to certain GetSchema requests -func getSchemaClientBound(t *testing.T) (SchemaClient.SchemaClientBound, error) { +func getSchemaClientBound(t *testing.T) (*mockschemaclientbound.MockSchemaClientBound, error) { x, schema, err := testhelper.InitSDCIOSchema() if err != nil { diff --git a/pkg/tree/leaf_entry.go b/pkg/tree/leaf_entry.go index d64f78e..3bc13e0 100644 --- a/pkg/tree/leaf_entry.go +++ b/pkg/tree/leaf_entry.go @@ -10,9 +10,14 @@ import ( // These Attributes indicate if the entry is to be deleted / added (new) or updated. type LeafEntry struct { *cache.Update - IsNew bool - Delete bool - IsUpdated bool + parentEntry Entry + IsNew bool + Delete bool + IsUpdated bool +} + +func (l *LeafEntry) GetEntry() Entry { + return l.parentEntry } // MarkUpdate indicate that the entry is an Updated value @@ -30,6 +35,10 @@ func (l *LeafEntry) MarkDelete() { l.Delete = true } +func (l *LeafEntry) GetRootBasedEntryChain() []Entry { + return l.parentEntry.GetRootBasedEntryChain() +} + // String returns a string representation of the LeafEntry func (l *LeafEntry) String() string { tv, err := l.Value() @@ -43,9 +52,10 @@ func (l *LeafEntry) String() string { } // NewLeafEntry constructor for a new LeafEntry -func NewLeafEntry(c *cache.Update, new bool) *LeafEntry { +func NewLeafEntry(c *cache.Update, new bool, parent Entry) *LeafEntry { return &LeafEntry{ - Update: c, - IsNew: new, + parentEntry: parent, + Update: c, + IsNew: new, } } diff --git a/pkg/tree/leaf_variant_slice.go b/pkg/tree/leaf_variant_slice.go new file mode 100644 index 0000000..adf6ecc --- /dev/null +++ b/pkg/tree/leaf_variant_slice.go @@ -0,0 +1,13 @@ +package tree + +import "github.com/sdcio/data-server/pkg/cache" + +type LeafVariantSlice []*LeafEntry + +func (lvs LeafVariantSlice) ToCacheUpdateSlice() []*cache.Update { + result := make([]*cache.Update, 0, len(lvs)) + for _, x := range lvs { + result = append(result, x.Update) + } + return result +} diff --git a/pkg/tree/tree_context.go b/pkg/tree/tree_context.go index 7ce4cd5..9766ce5 100644 --- a/pkg/tree/tree_context.go +++ b/pkg/tree/tree_context.go @@ -98,3 +98,13 @@ func (t *TreeContext) ReadRunning(ctx context.Context, path PathSlice) (*cache.U return updates[0], nil } + +// ReadRunning reads the value from running if the value does not exist, nil is returned +func (t *TreeContext) ReadRunningMultiple(ctx context.Context, paths [][]string) ([]*cache.Update, error) { + updates := t.treeSchemaCacheClient.Read(ctx, &cache.Opts{ + Store: cachepb.Store_CONFIG, + PriorityCount: 1, + }, paths) + + return updates, nil +} diff --git a/pkg/tree/tree_schema_cache_client.go b/pkg/tree/tree_schema_cache_client.go index c722d17..b92d1e7 100644 --- a/pkg/tree/tree_schema_cache_client.go +++ b/pkg/tree/tree_schema_cache_client.go @@ -10,6 +10,10 @@ import ( sdcpb "github.com/sdcio/sdc-protos/sdcpb" ) +const ( + PATHSEP = "/" +) + type TreeSchemaCacheClient interface { // CACHE based Functions // ReadIntended retrieves the highes priority value from the intended store @@ -46,31 +50,88 @@ func (c *TreeSchemaCacheClientImpl) Read(ctx context.Context, opts *cache.Opts, return c.cc.Read(ctx, c.datastore, opts, paths, 1) } +// ToPath local implementation of the ToPath functinality. It takes a string slice that contains schema elements as well as key values. +// Via the help of the schema, the key elemens are being identified and an sdcpb.Path is returned. func (c *TreeSchemaCacheClientImpl) ToPath(ctx context.Context, path []string) (*sdcpb.Path, error) { - return c.scb.ToPath(ctx, path) + var err error + + keylessPathSlice := []string{} + p := &sdcpb.Path{} + // iterate through the path slice + for i := 0; i < len(path); i++ { + // create a PathElem for the actual index + newPathElem := &sdcpb.PathElem{Name: path[i]} + // append the path elem to the path + p.Elem = append(p.Elem, newPathElem) + // prepare key lookup in index + keylessPathSlice = append(keylessPathSlice, path[i]) + // lookup the key in the schema index + schema, exists := c.schemaIndex[strings.Join(keylessPathSlice, PATHSEP)] + // if it does not exist + if !exists { + // retrieve the schema + schema, err = c.retrieveSchema(ctx, p) + if err != nil { + return nil, err + } + } + + // break early if the container itself is defined in the path, not a sub-element + if len(path) <= i+1 { + break + } + + // if it is a container with keys + if schemaKeys := schema.GetSchema().GetContainer().GetKeys(); schemaKeys != nil { + // add key map + newPathElem.Key = make(map[string]string, len(schemaKeys)) + // adding the keys with the value from path[i], which is the key value + for _, k := range schemaKeys { + i++ + newPathElem.Key[k.Name] = path[i] + } + } + + } + + return p, nil } +// retrieveSchema internal function to retrieve a schema, which when retireved will also be +// stored in the TreeSchemaCacheClientImpl's schema index +func (c *TreeSchemaCacheClientImpl) retrieveSchema(ctx context.Context, p *sdcpb.Path) (*sdcpb.GetSchemaResponse, error) { + // if schema wasn't found in index, go and fetch it + schemaRsp, err := c.scb.GetSchema(ctx, p) + if err != nil { + return nil, err + } + + // convert the path into a keyless path, for schema index lookups. + keylessPathSlice := utils.ToStrings(p, false, true) + keylessPath := strings.Join(keylessPathSlice, PATHSEP) + + // store the schema in the lookup index + c.schemaIndex[keylessPath] = schemaRsp + return schemaRsp, nil +} + +// GetSchema retrieves the given schema element from the schema-server. +// relies on TreeSchemaCacheClientImpl.retrieveSchema(...) to source the internal lookup index (cache) of schemas func (c *TreeSchemaCacheClientImpl) GetSchema(ctx context.Context, path []string) (*sdcpb.GetSchemaResponse, error) { // convert the []string path into sdcpb.path for schema retrieval - sdcpbPath, err := c.scb.ToPath(ctx, path) + sdcpbPath, err := c.ToPath(ctx, path) if err != nil { return nil, err } // convert the path into a keyless path, for schema index lookups. keylessPathSlice := utils.ToStrings(sdcpbPath, false, true) - keylessPath := strings.Join(keylessPathSlice, "/") + keylessPath := strings.Join(keylessPathSlice, PATHSEP) // lookup schema in schemaindex, preventing consecutive gets from the schema server if v, exists := c.schemaIndex[keylessPath]; exists { return v, nil } - // if schema wasn't found in index, go and fetch it - schemaRsp, err := c.scb.GetSchema(ctx, sdcpbPath) - if err != nil { - return nil, err - } - - return schemaRsp, nil + return c.retrieveSchema(ctx, sdcpbPath) } diff --git a/pkg/tree/tree_schema_cache_client_test.go b/pkg/tree/tree_schema_cache_client_test.go new file mode 100644 index 0000000..65a9dc2 --- /dev/null +++ b/pkg/tree/tree_schema_cache_client_test.go @@ -0,0 +1,89 @@ +package tree + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/sdcio/data-server/mocks/mockschemaclientbound" + "github.com/sdcio/data-server/pkg/utils/testhelper" + sdcpb "github.com/sdcio/sdc-protos/sdcpb" + "go.uber.org/mock/gomock" +) + +// TestTreeSchemaCacheClientImpl_GetSchema test that the TreeSchemaCacheClientImpl caches SchemaRequests +func TestTreeSchemaCacheClientImpl_GetSchema(t *testing.T) { + + ctx := context.TODO() + + x, schema, err := testhelper.InitSDCIOSchema() + if err != nil { + t.Error(err) + } + + sdcpbSchema := &sdcpb.Schema{ + Name: schema.Name, + Vendor: schema.Vendor, + Version: schema.Version, + } + + mockCtrl := gomock.NewController(t) + mockscb := mockschemaclientbound.NewMockSchemaClientBound(mockCtrl) + + // make the mock respond to GetSchema requests + mockscb.EXPECT().GetSchema(gomock.Any(), gomock.Any()).Times(3).DoAndReturn( + func(ctx context.Context, path *sdcpb.Path) (*sdcpb.GetSchemaResponse, error) { + return x.GetSchema(ctx, &sdcpb.GetSchemaRequest{ + Path: path, + Schema: sdcpbSchema, + }) + }, + ) + + tsc := NewTreeSchemaCacheClient("testds", nil, mockscb) + + // First call should result in 2 calls to GetSchema + path, err := tsc.ToPath(ctx, []string{"network-instance", "default", "admin-state"}) + if err != nil { + t.Error(err) + } + + cmpPath := &sdcpb.Path{Elem: []*sdcpb.PathElem{ + {Name: "network-instance", Key: map[string]string{"name": "default"}}, + {Name: "admin-state"}}, + } + + if diff := cmp.Diff(path.String(), cmpPath.String()); diff != "" { + t.Errorf("TreeSchemaCacheClientImpl.ToPath() mismatch (-want +got):\n%s", diff) + } + + // second call should result in no additional calls to GetSchema + path, err = tsc.ToPath(ctx, []string{"network-instance", "someother", "admin-state"}) + if err != nil { + t.Error(err) + } + + cmpPath = &sdcpb.Path{Elem: []*sdcpb.PathElem{ + {Name: "network-instance", Key: map[string]string{"name": "someother"}}, + {Name: "admin-state"}}, + } + + if diff := cmp.Diff(path.String(), cmpPath.String()); diff != "" { + t.Errorf("TreeSchemaCacheClientImpl.ToPath() mismatch (-want +got):\n%s", diff) + } + + // third call should result in a single additional call to GetSchema + path, err = tsc.ToPath(ctx, []string{"network-instance", "other", "type"}) + if err != nil { + t.Error(err) + } + + cmpPath = &sdcpb.Path{Elem: []*sdcpb.PathElem{ + {Name: "network-instance", Key: map[string]string{"name": "other"}}, + {Name: "type"}}, + } + + if diff := cmp.Diff(path.String(), cmpPath.String()); diff != "" { + t.Errorf("TreeSchemaCacheClientImpl.ToPath() mismatch (-want +got):\n%s", diff) + } +}